From eabe30fc9c3f2d6a3bc2fdcce18a0b9543b6c96e Mon Sep 17 00:00:00 2001 From: Alfred Perlstein Date: Tue, 4 Nov 2008 02:31:03 +0000 Subject: [PATCH] Bring in USB4BSD, Hans Petter Selasky rework of the USB stack that includes significant features and SMP safety. This commit includes a more or less complete rewrite of the *BSD USB stack, including Host Controller and Device Controller drivers and updating all existing USB drivers to use the new USB API: 1) A brief feature list: - A new and mutex enabled USB API. - Many USB drivers are now running Giant free. - Linux USB kernel compatibility layer. - New UGEN backend and libusb library, finally solves the "driver unloading" problem. The new BSD licensed libusb20 library is fully compatible with libusb-0.1.12 from sourceforge. - New "usbconfig" utility, for easy configuration of USB. - Full support for Split transactions, which means you can use your full speed USB audio device on a high speed USB HUB. - Full support for HS ISOC transactions, which makes writing drivers for various HS webcams possible, for example. - Full support for USB on embedded platforms, mostly cache flushing and buffer invalidating stuff. - Safer parsing of USB descriptors. - Autodetect of annoying USB install disks. - Support for USB device side mode, also called USB gadget mode, using the same API like the USB host side. In other words the new USB stack is symmetric with regard to host and device side. - Support for USB transfers like I/O vectors, means more throughput and less interrupts. - ... see the FreeBSD quarterly status reports under "USB project" 2) To enable the driver in the default kernel build: 2.a) Remove all existing USB device options from your kernel config file. 2.b) Add the following USB device options to your kernel configuration file: # USB core support device usb2_core # USB controller support device usb2_controller device usb2_controller_ehci device usb2_controller_ohci device usb2_controller_uhci # USB mass storage support device usb2_storage device usb2_storage_mass # USB ethernet support, requires miibus device usb2_ethernet device usb2_ethernet_aue device usb2_ethernet_axe device usb2_ethernet_cdce device usb2_ethernet_cue device usb2_ethernet_kue device usb2_ethernet_rue device usb2_ethernet_dav # USB wireless LAN support device usb2_wlan device usb2_wlan_rum device usb2_wlan_ral device usb2_wlan_zyd # USB serial device support device usb2_serial device usb2_serial_ark device usb2_serial_bsa device usb2_serial_bser device usb2_serial_chcom device usb2_serial_cycom device usb2_serial_foma device usb2_serial_ftdi device usb2_serial_gensa device usb2_serial_ipaq device usb2_serial_lpt device usb2_serial_mct device usb2_serial_modem device usb2_serial_moscom device usb2_serial_plcom device usb2_serial_visor device usb2_serial_vscom # USB bluetooth support device usb2_bluetooth device usb2_bluetooth_ng # USB input device support device usb2_input device usb2_input_hid device usb2_input_kbd device usb2_input_ms # USB sound and MIDI device support device usb2_sound 2) To enable the driver at runtime: 2.a) Unload all existing USB modules. If USB is compiled into the kernel then you might have to build a new kernel. 2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same base name like the kernel device option. Submitted by: Hans Petter Selasky hselasky at c2i dot net Reviewed by: imp, alfred --- lib/libusb20/Makefile | 24 + lib/libusb20/libusb20.3 | 893 ++ lib/libusb20/libusb20.c | 1245 ++ lib/libusb20/libusb20.h | 313 + lib/libusb20/libusb20_compat01.c | 902 ++ lib/libusb20/libusb20_compat01.h | 310 + lib/libusb20/libusb20_compat10.c | 29 + lib/libusb20/libusb20_compat10.h | 25 + lib/libusb20/libusb20_desc.c | 771 ++ lib/libusb20/libusb20_desc.h | 534 + lib/libusb20/libusb20_int.h | 252 + lib/libusb20/libusb20_ugen20.c | 1077 ++ share/man/man4/usb2_bluetooth.4 | 65 + share/man/man4/usb2_controller.4 | 65 + share/man/man4/usb2_core.4 | 630 + share/man/man4/usb2_ethernet.4 | 65 + share/man/man4/usb2_image.4 | 65 + share/man/man4/usb2_input.4 | 66 + share/man/man4/usb2_misc.4 | 66 + share/man/man4/usb2_ndis.4 | 66 + share/man/man4/usb2_quirk.4 | 64 + share/man/man4/usb2_serial.4 | 66 + share/man/man4/usb2_sound.4 | 65 + share/man/man4/usb2_storage.4 | 65 + share/man/man4/usb2_template.4 | 84 + share/man/man4/usb2_wlan.4 | 65 + sys/conf/files | 139 + sys/dev/sound/pcm/channel.c | 17 +- sys/dev/sound/pcm/channel.h | 2 + sys/dev/sound/pcm/mixer.c | 25 +- sys/dev/sound/pcm/mixer.h | 1 + sys/dev/usb2/bluetooth/TODO.TXT | 18 + sys/dev/usb2/bluetooth/ng_ubt2.c | 1774 +++ sys/dev/usb2/bluetooth/ng_ubt2_var.h | 126 + sys/dev/usb2/bluetooth/ubtbcmfw2.c | 448 + sys/dev/usb2/bluetooth/usb2_bluetooth.c | 31 + sys/dev/usb2/bluetooth/usb2_bluetooth.h | 30 + sys/dev/usb2/controller/at91dci.c | 2547 ++++ sys/dev/usb2/controller/at91dci.h | 242 + sys/dev/usb2/controller/at91dci_atmelarm.c | 361 + sys/dev/usb2/controller/ehci2.c | 3854 ++++++ sys/dev/usb2/controller/ehci2.h | 515 + sys/dev/usb2/controller/ehci2_pci.c | 498 + sys/dev/usb2/controller/musb2_otg.c | 2945 +++++ sys/dev/usb2/controller/musb2_otg.h | 403 + sys/dev/usb2/controller/musb2_otg_atmelarm.c | 256 + sys/dev/usb2/controller/ohci2.c | 2802 ++++ sys/dev/usb2/controller/ohci2.h | 364 + sys/dev/usb2/controller/ohci2_atmelarm.c | 232 + sys/dev/usb2/controller/ohci2_pci.c | 392 + sys/dev/usb2/controller/uhci2.c | 3256 +++++ sys/dev/usb2/controller/uhci2.h | 318 + sys/dev/usb2/controller/uhci2_pci.c | 453 + sys/dev/usb2/controller/usb2_bus.h | 88 + sys/dev/usb2/controller/usb2_controller.c | 477 + sys/dev/usb2/controller/usb2_controller.h | 172 + sys/dev/usb2/controller/usb2_pci.h | 39 + sys/dev/usb2/controller/uss820dci.c | 2572 ++++ sys/dev/usb2/controller/uss820dci.h | 375 + sys/dev/usb2/controller/uss820dci_atmelarm.c | 247 + sys/dev/usb2/controller/uss820dci_pccard.c | 266 + sys/dev/usb2/core/README.TXT | 411 + sys/dev/usb2/core/usb2_busdma.c | 1401 ++ sys/dev/usb2/core/usb2_busdma.h | 169 + sys/dev/usb2/core/usb2_compat_linux.c | 1659 +++ sys/dev/usb2/core/usb2_compat_linux.h | 465 + sys/dev/usb2/core/usb2_config_td.c | 320 + sys/dev/usb2/core/usb2_config_td.h | 71 + sys/dev/usb2/core/usb2_core.c | 40 + sys/dev/usb2/core/usb2_core.h | 448 + sys/dev/usb2/core/usb2_debug.c | 153 + sys/dev/usb2/core/usb2_debug.h | 70 + sys/dev/usb2/core/usb2_dev.c | 2786 ++++ sys/dev/usb2/core/usb2_dev.h | 149 + sys/dev/usb2/core/usb2_device.c | 2110 +++ sys/dev/usb2/core/usb2_device.h | 162 + sys/dev/usb2/core/usb2_dynamic.c | 140 + sys/dev/usb2/core/usb2_dynamic.h | 61 + sys/dev/usb2/core/usb2_error.c | 44 + sys/dev/usb2/core/usb2_generic.c | 2226 ++++ sys/dev/usb2/core/usb2_generic.h | 33 + sys/dev/usb2/core/usb2_handle_request.c | 750 ++ sys/dev/usb2/core/usb2_handle_request.h | 30 + sys/dev/usb2/core/usb2_hid.c | 582 + sys/dev/usb2/core/usb2_hid.h | 89 + sys/dev/usb2/core/usb2_hub.c | 1330 ++ sys/dev/usb2/core/usb2_hub.h | 75 + sys/dev/usb2/core/usb2_if.m | 52 + sys/dev/usb2/core/usb2_lookup.c | 134 + sys/dev/usb2/core/usb2_lookup.h | 119 + sys/dev/usb2/core/usb2_mbuf.c | 77 + sys/dev/usb2/core/usb2_mbuf.h | 100 + sys/dev/usb2/core/usb2_msctest.c | 612 + sys/dev/usb2/core/usb2_msctest.h | 33 + sys/dev/usb2/core/usb2_parse.c | 208 + sys/dev/usb2/core/usb2_parse.h | 36 + sys/dev/usb2/core/usb2_process.c | 480 + sys/dev/usb2/core/usb2_process.h | 89 + sys/dev/usb2/core/usb2_request.c | 1373 ++ sys/dev/usb2/core/usb2_request.h | 61 + sys/dev/usb2/core/usb2_sw_transfer.c | 166 + sys/dev/usb2/core/usb2_sw_transfer.h | 61 + sys/dev/usb2/core/usb2_transfer.c | 2833 ++++ sys/dev/usb2/core/usb2_transfer.h | 123 + sys/dev/usb2/core/usb2_util.c | 354 + sys/dev/usb2/core/usb2_util.h | 57 + sys/dev/usb2/core/usbdevs | 2482 ++++ sys/dev/usb2/ethernet/if_aue2.c | 1567 +++ sys/dev/usb2/ethernet/if_aue2_reg.h | 232 + sys/dev/usb2/ethernet/if_axe2.c | 1522 +++ sys/dev/usb2/ethernet/if_axe2_reg.h | 191 + sys/dev/usb2/ethernet/if_cdce2.c | 1355 ++ sys/dev/usb2/ethernet/if_cdce2_reg.h | 87 + sys/dev/usb2/ethernet/if_cue2.c | 965 ++ sys/dev/usb2/ethernet/if_cue2_reg.h | 138 + sys/dev/usb2/ethernet/if_kue2.c | 1017 ++ sys/dev/usb2/ethernet/if_kue2_fw.h | 685 + sys/dev/usb2/ethernet/if_kue2_reg.h | 142 + sys/dev/usb2/ethernet/if_rue2.c | 1400 ++ sys/dev/usb2/ethernet/if_rue2_reg.h | 194 + sys/dev/usb2/ethernet/if_udav2.c | 1361 ++ sys/dev/usb2/ethernet/if_udav2_reg.h | 166 + sys/dev/usb2/ethernet/usb2_ethernet.c | 101 + sys/dev/usb2/ethernet/usb2_ethernet.h | 67 + sys/dev/usb2/image/usb2_image.c | 31 + sys/dev/usb2/image/usb2_image.h | 30 + sys/dev/usb2/image/uscanner2.c | 642 + sys/dev/usb2/include/Makefile | 14 + sys/dev/usb2/include/ufm2_ioctl.h | 39 + sys/dev/usb2/include/urio2_ioctl.h | 41 + sys/dev/usb2/include/usb2_cdc.h | 205 + sys/dev/usb2/include/usb2_defs.h | 68 + sys/dev/usb2/include/usb2_devid.h | 2489 ++++ sys/dev/usb2/include/usb2_devtable.h | 10748 ++++++++++++++++ sys/dev/usb2/include/usb2_endian.h | 119 + sys/dev/usb2/include/usb2_error.h | 68 + sys/dev/usb2/include/usb2_hid.h | 173 + sys/dev/usb2/include/usb2_ioctl.h | 301 + sys/dev/usb2/include/usb2_mfunc.h | 86 + sys/dev/usb2/include/usb2_revision.h | 67 + sys/dev/usb2/include/usb2_standard.h | 497 + sys/dev/usb2/input/uhid2.c | 822 ++ sys/dev/usb2/input/ukbd2.c | 1503 +++ sys/dev/usb2/input/ums2.c | 911 ++ sys/dev/usb2/input/usb2_input.c | 31 + sys/dev/usb2/input/usb2_input.h | 30 + sys/dev/usb2/input/usb2_rdesc.h | 276 + sys/dev/usb2/misc/udbp2.c | 861 ++ sys/dev/usb2/misc/udbp2.h | 80 + sys/dev/usb2/misc/ufm2.c | 336 + sys/dev/usb2/misc/usb2_misc.c | 31 + sys/dev/usb2/misc/usb2_misc.h | 30 + sys/dev/usb2/ndis/if_ndis_usb2.c | 144 + sys/dev/usb2/ndis/usb2_ndis.c | 31 + sys/dev/usb2/ndis/usb2_ndis.h | 30 + sys/dev/usb2/quirk/usb2_quirk.c | 372 + sys/dev/usb2/quirk/usb2_quirk.h | 83 + sys/dev/usb2/serial/uark2.c | 482 + sys/dev/usb2/serial/ubsa2.c | 755 ++ sys/dev/usb2/serial/ubser2.c | 604 + sys/dev/usb2/serial/uchcom2.c | 1038 ++ sys/dev/usb2/serial/ucycom2.c | 607 + sys/dev/usb2/serial/ufoma2.c | 1198 ++ sys/dev/usb2/serial/uftdi2.c | 868 ++ sys/dev/usb2/serial/uftdi2_reg.h | 340 + sys/dev/usb2/serial/ugensa2.c | 462 + sys/dev/usb2/serial/uipaq2.c | 1408 ++ sys/dev/usb2/serial/ulpt2.c | 798 ++ sys/dev/usb2/serial/umct2.c | 689 + sys/dev/usb2/serial/umodem2.c | 924 ++ sys/dev/usb2/serial/umoscom2.c | 799 ++ sys/dev/usb2/serial/uplcom2.c | 964 ++ sys/dev/usb2/serial/usb2_serial.c | 1112 ++ sys/dev/usb2/serial/usb2_serial.h | 159 + sys/dev/usb2/serial/uvisor2.c | 675 + sys/dev/usb2/serial/uvscom2.c | 827 ++ sys/dev/usb2/sound/uaudio2.c | 3786 ++++++ sys/dev/usb2/sound/uaudio2.h | 55 + sys/dev/usb2/sound/uaudio2_pcm.c | 234 + sys/dev/usb2/sound/uaudio2_reg.h | 406 + sys/dev/usb2/sound/usb2_sound.c | 31 + sys/dev/usb2/sound/usb2_sound.h | 30 + sys/dev/usb2/storage/ata-usb2.c | 1114 ++ sys/dev/usb2/storage/umass2.c | 3670 ++++++ sys/dev/usb2/storage/urio2.c | 491 + sys/dev/usb2/storage/usb2_storage.c | 31 + sys/dev/usb2/storage/usb2_storage.h | 30 + sys/dev/usb2/storage/ustorage2_fs.c | 1906 +++ sys/dev/usb2/template/usb2_template.c | 1306 ++ sys/dev/usb2/template/usb2_template.h | 102 + sys/dev/usb2/template/usb2_template_cdce.c | 325 + sys/dev/usb2/template/usb2_template_msc.c | 199 + sys/dev/usb2/template/usb2_template_mtp.c | 262 + sys/dev/usb2/wlan/if_rum2.c | 2961 +++++ sys/dev/usb2/wlan/if_rum2_fw.h | 213 + sys/dev/usb2/wlan/if_rum2_reg.h | 235 + sys/dev/usb2/wlan/if_rum2_var.h | 172 + sys/dev/usb2/wlan/if_ural2.c | 2788 ++++ sys/dev/usb2/wlan/if_ural2_reg.h | 198 + sys/dev/usb2/wlan/if_ural2_var.h | 161 + sys/dev/usb2/wlan/if_zyd2.c | 3297 +++++ sys/dev/usb2/wlan/if_zyd2_fw.h | 1144 ++ sys/dev/usb2/wlan/if_zyd2_reg.h | 1280 ++ sys/dev/usb2/wlan/usb2_wlan.c | 31 + sys/dev/usb2/wlan/usb2_wlan.h | 57 + sys/modules/Makefile | 1 + sys/modules/usb2/Makefile | 89 + sys/modules/usb2/bluetooth/Makefile | 15 + sys/modules/usb2/bluetooth_fw/Makefile | 38 + sys/modules/usb2/bluetooth_ng/Makefile | 38 + sys/modules/usb2/controller/Makefile | 38 + sys/modules/usb2/controller_at91dci/Makefile | 41 + sys/modules/usb2/controller_ehci/Makefile | 39 + sys/modules/usb2/controller_musb/Makefile | 41 + sys/modules/usb2/controller_ohci/Makefile | 42 + sys/modules/usb2/controller_uhci/Makefile | 39 + .../usb2/controller_uss820dci/Makefile | 41 + sys/modules/usb2/core/Makefile | 60 + sys/modules/usb2/ethernet/Makefile | 38 + sys/modules/usb2/ethernet_aue/Makefile | 38 + sys/modules/usb2/ethernet_axe/Makefile | 38 + sys/modules/usb2/ethernet_cdce/Makefile | 38 + sys/modules/usb2/ethernet_cue/Makefile | 38 + sys/modules/usb2/ethernet_dav/Makefile | 38 + sys/modules/usb2/ethernet_kue/Makefile | 38 + sys/modules/usb2/ethernet_rue/Makefile | 38 + sys/modules/usb2/image/Makefile | 38 + sys/modules/usb2/input/Makefile | 38 + sys/modules/usb2/input_hid/Makefile | 38 + sys/modules/usb2/input_kbd/Makefile | 38 + sys/modules/usb2/input_ms/Makefile | 38 + sys/modules/usb2/misc/Makefile | 38 + sys/modules/usb2/misc_dbp/Makefile | 38 + sys/modules/usb2/misc_fm/Makefile | 38 + sys/modules/usb2/ndis/Makefile | 40 + sys/modules/usb2/quirk/Makefile | 37 + sys/modules/usb2/scanner/Makefile | 38 + sys/modules/usb2/serial/Makefile | 38 + sys/modules/usb2/serial_ark/Makefile | 38 + sys/modules/usb2/serial_bsa/Makefile | 38 + sys/modules/usb2/serial_bser/Makefile | 38 + sys/modules/usb2/serial_chcom/Makefile | 38 + sys/modules/usb2/serial_cycom/Makefile | 38 + sys/modules/usb2/serial_foma/Makefile | 38 + sys/modules/usb2/serial_ftdi/Makefile | 38 + sys/modules/usb2/serial_gensa/Makefile | 38 + sys/modules/usb2/serial_ipaq/Makefile | 38 + sys/modules/usb2/serial_lpt/Makefile | 38 + sys/modules/usb2/serial_mct/Makefile | 38 + sys/modules/usb2/serial_modem/Makefile | 38 + sys/modules/usb2/serial_moscom/Makefile | 38 + sys/modules/usb2/serial_plcom/Makefile | 38 + sys/modules/usb2/serial_visor/Makefile | 38 + sys/modules/usb2/serial_vscom/Makefile | 38 + sys/modules/usb2/sound/Makefile | 42 + sys/modules/usb2/storage/Makefile | 38 + sys/modules/usb2/storage_ata/Makefile | 38 + sys/modules/usb2/storage_fs/Makefile | 38 + sys/modules/usb2/storage_mass/Makefile | 38 + sys/modules/usb2/storage_rio/Makefile | 38 + sys/modules/usb2/template/Makefile | 40 + sys/modules/usb2/wlan/Makefile | 38 + sys/modules/usb2/wlan_ral/Makefile | 38 + sys/modules/usb2/wlan_rum/Makefile | 38 + sys/modules/usb2/wlan_zyd/Makefile | 38 + usr.sbin/usbconfig/Makefile | 9 + usr.sbin/usbconfig/dump.c | 459 + usr.sbin/usbconfig/dump.h | 37 + usr.sbin/usbconfig/usbconfig.8 | 53 + usr.sbin/usbconfig/usbconfig.c | 683 + 270 files changed, 136537 insertions(+), 3 deletions(-) create mode 100644 lib/libusb20/Makefile create mode 100644 lib/libusb20/libusb20.3 create mode 100644 lib/libusb20/libusb20.c create mode 100644 lib/libusb20/libusb20.h create mode 100644 lib/libusb20/libusb20_compat01.c create mode 100644 lib/libusb20/libusb20_compat01.h create mode 100644 lib/libusb20/libusb20_compat10.c create mode 100644 lib/libusb20/libusb20_compat10.h create mode 100644 lib/libusb20/libusb20_desc.c create mode 100644 lib/libusb20/libusb20_desc.h create mode 100644 lib/libusb20/libusb20_int.h create mode 100644 lib/libusb20/libusb20_ugen20.c create mode 100644 share/man/man4/usb2_bluetooth.4 create mode 100644 share/man/man4/usb2_controller.4 create mode 100644 share/man/man4/usb2_core.4 create mode 100644 share/man/man4/usb2_ethernet.4 create mode 100644 share/man/man4/usb2_image.4 create mode 100644 share/man/man4/usb2_input.4 create mode 100644 share/man/man4/usb2_misc.4 create mode 100644 share/man/man4/usb2_ndis.4 create mode 100644 share/man/man4/usb2_quirk.4 create mode 100644 share/man/man4/usb2_serial.4 create mode 100644 share/man/man4/usb2_sound.4 create mode 100644 share/man/man4/usb2_storage.4 create mode 100644 share/man/man4/usb2_template.4 create mode 100644 share/man/man4/usb2_wlan.4 create mode 100644 sys/dev/usb2/bluetooth/TODO.TXT create mode 100644 sys/dev/usb2/bluetooth/ng_ubt2.c create mode 100644 sys/dev/usb2/bluetooth/ng_ubt2_var.h create mode 100644 sys/dev/usb2/bluetooth/ubtbcmfw2.c create mode 100644 sys/dev/usb2/bluetooth/usb2_bluetooth.c create mode 100644 sys/dev/usb2/bluetooth/usb2_bluetooth.h create mode 100644 sys/dev/usb2/controller/at91dci.c create mode 100644 sys/dev/usb2/controller/at91dci.h create mode 100644 sys/dev/usb2/controller/at91dci_atmelarm.c create mode 100644 sys/dev/usb2/controller/ehci2.c create mode 100644 sys/dev/usb2/controller/ehci2.h create mode 100644 sys/dev/usb2/controller/ehci2_pci.c create mode 100644 sys/dev/usb2/controller/musb2_otg.c create mode 100644 sys/dev/usb2/controller/musb2_otg.h create mode 100644 sys/dev/usb2/controller/musb2_otg_atmelarm.c create mode 100644 sys/dev/usb2/controller/ohci2.c create mode 100644 sys/dev/usb2/controller/ohci2.h create mode 100644 sys/dev/usb2/controller/ohci2_atmelarm.c create mode 100644 sys/dev/usb2/controller/ohci2_pci.c create mode 100644 sys/dev/usb2/controller/uhci2.c create mode 100644 sys/dev/usb2/controller/uhci2.h create mode 100644 sys/dev/usb2/controller/uhci2_pci.c create mode 100644 sys/dev/usb2/controller/usb2_bus.h create mode 100644 sys/dev/usb2/controller/usb2_controller.c create mode 100644 sys/dev/usb2/controller/usb2_controller.h create mode 100644 sys/dev/usb2/controller/usb2_pci.h create mode 100644 sys/dev/usb2/controller/uss820dci.c create mode 100644 sys/dev/usb2/controller/uss820dci.h create mode 100644 sys/dev/usb2/controller/uss820dci_atmelarm.c create mode 100644 sys/dev/usb2/controller/uss820dci_pccard.c create mode 100644 sys/dev/usb2/core/README.TXT create mode 100644 sys/dev/usb2/core/usb2_busdma.c create mode 100644 sys/dev/usb2/core/usb2_busdma.h create mode 100644 sys/dev/usb2/core/usb2_compat_linux.c create mode 100644 sys/dev/usb2/core/usb2_compat_linux.h create mode 100644 sys/dev/usb2/core/usb2_config_td.c create mode 100644 sys/dev/usb2/core/usb2_config_td.h create mode 100644 sys/dev/usb2/core/usb2_core.c create mode 100644 sys/dev/usb2/core/usb2_core.h create mode 100644 sys/dev/usb2/core/usb2_debug.c create mode 100644 sys/dev/usb2/core/usb2_debug.h create mode 100644 sys/dev/usb2/core/usb2_dev.c create mode 100644 sys/dev/usb2/core/usb2_dev.h create mode 100644 sys/dev/usb2/core/usb2_device.c create mode 100644 sys/dev/usb2/core/usb2_device.h create mode 100644 sys/dev/usb2/core/usb2_dynamic.c create mode 100644 sys/dev/usb2/core/usb2_dynamic.h create mode 100644 sys/dev/usb2/core/usb2_error.c create mode 100644 sys/dev/usb2/core/usb2_generic.c create mode 100644 sys/dev/usb2/core/usb2_generic.h create mode 100644 sys/dev/usb2/core/usb2_handle_request.c create mode 100644 sys/dev/usb2/core/usb2_handle_request.h create mode 100644 sys/dev/usb2/core/usb2_hid.c create mode 100644 sys/dev/usb2/core/usb2_hid.h create mode 100644 sys/dev/usb2/core/usb2_hub.c create mode 100644 sys/dev/usb2/core/usb2_hub.h create mode 100644 sys/dev/usb2/core/usb2_if.m create mode 100644 sys/dev/usb2/core/usb2_lookup.c create mode 100644 sys/dev/usb2/core/usb2_lookup.h create mode 100644 sys/dev/usb2/core/usb2_mbuf.c create mode 100644 sys/dev/usb2/core/usb2_mbuf.h create mode 100644 sys/dev/usb2/core/usb2_msctest.c create mode 100644 sys/dev/usb2/core/usb2_msctest.h create mode 100644 sys/dev/usb2/core/usb2_parse.c create mode 100644 sys/dev/usb2/core/usb2_parse.h create mode 100644 sys/dev/usb2/core/usb2_process.c create mode 100644 sys/dev/usb2/core/usb2_process.h create mode 100644 sys/dev/usb2/core/usb2_request.c create mode 100644 sys/dev/usb2/core/usb2_request.h create mode 100644 sys/dev/usb2/core/usb2_sw_transfer.c create mode 100644 sys/dev/usb2/core/usb2_sw_transfer.h create mode 100644 sys/dev/usb2/core/usb2_transfer.c create mode 100644 sys/dev/usb2/core/usb2_transfer.h create mode 100644 sys/dev/usb2/core/usb2_util.c create mode 100644 sys/dev/usb2/core/usb2_util.h create mode 100644 sys/dev/usb2/core/usbdevs create mode 100644 sys/dev/usb2/ethernet/if_aue2.c create mode 100644 sys/dev/usb2/ethernet/if_aue2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_axe2.c create mode 100644 sys/dev/usb2/ethernet/if_axe2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_cdce2.c create mode 100644 sys/dev/usb2/ethernet/if_cdce2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_cue2.c create mode 100644 sys/dev/usb2/ethernet/if_cue2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_kue2.c create mode 100644 sys/dev/usb2/ethernet/if_kue2_fw.h create mode 100644 sys/dev/usb2/ethernet/if_kue2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_rue2.c create mode 100644 sys/dev/usb2/ethernet/if_rue2_reg.h create mode 100644 sys/dev/usb2/ethernet/if_udav2.c create mode 100644 sys/dev/usb2/ethernet/if_udav2_reg.h create mode 100644 sys/dev/usb2/ethernet/usb2_ethernet.c create mode 100644 sys/dev/usb2/ethernet/usb2_ethernet.h create mode 100644 sys/dev/usb2/image/usb2_image.c create mode 100644 sys/dev/usb2/image/usb2_image.h create mode 100644 sys/dev/usb2/image/uscanner2.c create mode 100644 sys/dev/usb2/include/Makefile create mode 100644 sys/dev/usb2/include/ufm2_ioctl.h create mode 100644 sys/dev/usb2/include/urio2_ioctl.h create mode 100644 sys/dev/usb2/include/usb2_cdc.h create mode 100644 sys/dev/usb2/include/usb2_defs.h create mode 100644 sys/dev/usb2/include/usb2_devid.h create mode 100644 sys/dev/usb2/include/usb2_devtable.h create mode 100644 sys/dev/usb2/include/usb2_endian.h create mode 100644 sys/dev/usb2/include/usb2_error.h create mode 100644 sys/dev/usb2/include/usb2_hid.h create mode 100644 sys/dev/usb2/include/usb2_ioctl.h create mode 100644 sys/dev/usb2/include/usb2_mfunc.h create mode 100644 sys/dev/usb2/include/usb2_revision.h create mode 100644 sys/dev/usb2/include/usb2_standard.h create mode 100644 sys/dev/usb2/input/uhid2.c create mode 100644 sys/dev/usb2/input/ukbd2.c create mode 100644 sys/dev/usb2/input/ums2.c create mode 100644 sys/dev/usb2/input/usb2_input.c create mode 100644 sys/dev/usb2/input/usb2_input.h create mode 100644 sys/dev/usb2/input/usb2_rdesc.h create mode 100644 sys/dev/usb2/misc/udbp2.c create mode 100644 sys/dev/usb2/misc/udbp2.h create mode 100644 sys/dev/usb2/misc/ufm2.c create mode 100644 sys/dev/usb2/misc/usb2_misc.c create mode 100644 sys/dev/usb2/misc/usb2_misc.h create mode 100644 sys/dev/usb2/ndis/if_ndis_usb2.c create mode 100644 sys/dev/usb2/ndis/usb2_ndis.c create mode 100644 sys/dev/usb2/ndis/usb2_ndis.h create mode 100644 sys/dev/usb2/quirk/usb2_quirk.c create mode 100644 sys/dev/usb2/quirk/usb2_quirk.h create mode 100644 sys/dev/usb2/serial/uark2.c create mode 100644 sys/dev/usb2/serial/ubsa2.c create mode 100644 sys/dev/usb2/serial/ubser2.c create mode 100644 sys/dev/usb2/serial/uchcom2.c create mode 100644 sys/dev/usb2/serial/ucycom2.c create mode 100644 sys/dev/usb2/serial/ufoma2.c create mode 100644 sys/dev/usb2/serial/uftdi2.c create mode 100644 sys/dev/usb2/serial/uftdi2_reg.h create mode 100644 sys/dev/usb2/serial/ugensa2.c create mode 100644 sys/dev/usb2/serial/uipaq2.c create mode 100644 sys/dev/usb2/serial/ulpt2.c create mode 100644 sys/dev/usb2/serial/umct2.c create mode 100644 sys/dev/usb2/serial/umodem2.c create mode 100644 sys/dev/usb2/serial/umoscom2.c create mode 100644 sys/dev/usb2/serial/uplcom2.c create mode 100644 sys/dev/usb2/serial/usb2_serial.c create mode 100644 sys/dev/usb2/serial/usb2_serial.h create mode 100644 sys/dev/usb2/serial/uvisor2.c create mode 100644 sys/dev/usb2/serial/uvscom2.c create mode 100644 sys/dev/usb2/sound/uaudio2.c create mode 100644 sys/dev/usb2/sound/uaudio2.h create mode 100644 sys/dev/usb2/sound/uaudio2_pcm.c create mode 100644 sys/dev/usb2/sound/uaudio2_reg.h create mode 100644 sys/dev/usb2/sound/usb2_sound.c create mode 100644 sys/dev/usb2/sound/usb2_sound.h create mode 100644 sys/dev/usb2/storage/ata-usb2.c create mode 100644 sys/dev/usb2/storage/umass2.c create mode 100644 sys/dev/usb2/storage/urio2.c create mode 100644 sys/dev/usb2/storage/usb2_storage.c create mode 100644 sys/dev/usb2/storage/usb2_storage.h create mode 100644 sys/dev/usb2/storage/ustorage2_fs.c create mode 100644 sys/dev/usb2/template/usb2_template.c create mode 100644 sys/dev/usb2/template/usb2_template.h create mode 100644 sys/dev/usb2/template/usb2_template_cdce.c create mode 100644 sys/dev/usb2/template/usb2_template_msc.c create mode 100644 sys/dev/usb2/template/usb2_template_mtp.c create mode 100644 sys/dev/usb2/wlan/if_rum2.c create mode 100644 sys/dev/usb2/wlan/if_rum2_fw.h create mode 100644 sys/dev/usb2/wlan/if_rum2_reg.h create mode 100644 sys/dev/usb2/wlan/if_rum2_var.h create mode 100644 sys/dev/usb2/wlan/if_ural2.c create mode 100644 sys/dev/usb2/wlan/if_ural2_reg.h create mode 100644 sys/dev/usb2/wlan/if_ural2_var.h create mode 100644 sys/dev/usb2/wlan/if_zyd2.c create mode 100644 sys/dev/usb2/wlan/if_zyd2_fw.h create mode 100644 sys/dev/usb2/wlan/if_zyd2_reg.h create mode 100644 sys/dev/usb2/wlan/usb2_wlan.c create mode 100644 sys/dev/usb2/wlan/usb2_wlan.h create mode 100644 sys/modules/usb2/Makefile create mode 100644 sys/modules/usb2/bluetooth/Makefile create mode 100644 sys/modules/usb2/bluetooth_fw/Makefile create mode 100644 sys/modules/usb2/bluetooth_ng/Makefile create mode 100644 sys/modules/usb2/controller/Makefile create mode 100644 sys/modules/usb2/controller_at91dci/Makefile create mode 100644 sys/modules/usb2/controller_ehci/Makefile create mode 100644 sys/modules/usb2/controller_musb/Makefile create mode 100644 sys/modules/usb2/controller_ohci/Makefile create mode 100644 sys/modules/usb2/controller_uhci/Makefile create mode 100644 sys/modules/usb2/controller_uss820dci/Makefile create mode 100644 sys/modules/usb2/core/Makefile create mode 100644 sys/modules/usb2/ethernet/Makefile create mode 100644 sys/modules/usb2/ethernet_aue/Makefile create mode 100644 sys/modules/usb2/ethernet_axe/Makefile create mode 100644 sys/modules/usb2/ethernet_cdce/Makefile create mode 100644 sys/modules/usb2/ethernet_cue/Makefile create mode 100644 sys/modules/usb2/ethernet_dav/Makefile create mode 100644 sys/modules/usb2/ethernet_kue/Makefile create mode 100644 sys/modules/usb2/ethernet_rue/Makefile create mode 100644 sys/modules/usb2/image/Makefile create mode 100644 sys/modules/usb2/input/Makefile create mode 100644 sys/modules/usb2/input_hid/Makefile create mode 100644 sys/modules/usb2/input_kbd/Makefile create mode 100644 sys/modules/usb2/input_ms/Makefile create mode 100644 sys/modules/usb2/misc/Makefile create mode 100644 sys/modules/usb2/misc_dbp/Makefile create mode 100644 sys/modules/usb2/misc_fm/Makefile create mode 100644 sys/modules/usb2/ndis/Makefile create mode 100644 sys/modules/usb2/quirk/Makefile create mode 100644 sys/modules/usb2/scanner/Makefile create mode 100644 sys/modules/usb2/serial/Makefile create mode 100644 sys/modules/usb2/serial_ark/Makefile create mode 100644 sys/modules/usb2/serial_bsa/Makefile create mode 100644 sys/modules/usb2/serial_bser/Makefile create mode 100644 sys/modules/usb2/serial_chcom/Makefile create mode 100644 sys/modules/usb2/serial_cycom/Makefile create mode 100644 sys/modules/usb2/serial_foma/Makefile create mode 100644 sys/modules/usb2/serial_ftdi/Makefile create mode 100644 sys/modules/usb2/serial_gensa/Makefile create mode 100644 sys/modules/usb2/serial_ipaq/Makefile create mode 100644 sys/modules/usb2/serial_lpt/Makefile create mode 100644 sys/modules/usb2/serial_mct/Makefile create mode 100644 sys/modules/usb2/serial_modem/Makefile create mode 100644 sys/modules/usb2/serial_moscom/Makefile create mode 100644 sys/modules/usb2/serial_plcom/Makefile create mode 100644 sys/modules/usb2/serial_visor/Makefile create mode 100644 sys/modules/usb2/serial_vscom/Makefile create mode 100644 sys/modules/usb2/sound/Makefile create mode 100644 sys/modules/usb2/storage/Makefile create mode 100644 sys/modules/usb2/storage_ata/Makefile create mode 100644 sys/modules/usb2/storage_fs/Makefile create mode 100644 sys/modules/usb2/storage_mass/Makefile create mode 100644 sys/modules/usb2/storage_rio/Makefile create mode 100644 sys/modules/usb2/template/Makefile create mode 100644 sys/modules/usb2/wlan/Makefile create mode 100644 sys/modules/usb2/wlan_ral/Makefile create mode 100644 sys/modules/usb2/wlan_rum/Makefile create mode 100644 sys/modules/usb2/wlan_zyd/Makefile create mode 100644 usr.sbin/usbconfig/Makefile create mode 100644 usr.sbin/usbconfig/dump.c create mode 100644 usr.sbin/usbconfig/dump.h create mode 100644 usr.sbin/usbconfig/usbconfig.8 create mode 100644 usr.sbin/usbconfig/usbconfig.c diff --git a/lib/libusb20/Makefile b/lib/libusb20/Makefile new file mode 100644 index 000000000000..0c0f18f1bfec --- /dev/null +++ b/lib/libusb20/Makefile @@ -0,0 +1,24 @@ +# +# $FreeBSD$ +# +# Makefile for the FreeBSD specific LibUSB 2.0 +# + +LIB= usb20 +SHLIB_MAJOR= 1 +SHLIB_MINOR= 0 +SRCS= libusb20.c +SRCS+= libusb20_desc.c +SRCS+= libusb20_ugen20.c +SRCS+= libusb20_compat01.c +SRCS+= libusb20_compat10.c +INCS+= libusb20.h +INCS+= libusb20_desc.h +INCS+= libusb20_compat01.h +INCS+= libusb20_compat10.h +MAN= libusb20.3 +MKLINT= no +NOGCCERROR= + +.include + diff --git a/lib/libusb20/libusb20.3 b/lib/libusb20/libusb20.3 new file mode 100644 index 000000000000..975bc637ae68 --- /dev/null +++ b/lib/libusb20/libusb20.3 @@ -0,0 +1,893 @@ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky +.\" +.\" 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$ +.\" +.Dd Oct 23, 2008 +.Dt LIBUSB20 3 +.Os +.Sh NAME +.Nm libusb20 +. +.Nd "USB access library" +. +. +.Sh LIBRARY +. +. +USB access library (libusb20 -lusb20) +. +. +. +.Sh SYNOPSIS +. +. +.In libusb20.h +. +. +.Sh DESCRIPTION +. +The +.Nm +library implements functions to be able to easily access and control +USB through the USB file system interface. +. +. +.Sh USB TRANSFER OPERATIONS +. +.Pp +. +.Fn libusb20_tr_close +This function will release all kernel resources associated with an USB +.Fa xfer . +. +This function returns zero upon success. +. +Non-zero return values indicate a LIBUSB20_ERROR value. +. +.Pp +. +.Fn libusb20_tr_open +This function will allocate kernel resources like +.Fa MaxBufSize +and +.Fa MaxFrameCount +associated with an USB +.Fa xfer +and bind the transfer to the specified +.Fa ep_no . +. +This function returns zero upon success. +. +Non-zero return values indicate a LIBUSB20_ERROR value. +. +.Pp +. +.Fn libusb20_tr_get_pointer +This function will return a pointer to the allocated USB transfer according to the +.Fa pdev +and +.Fa tr_index +arguments. +. +This function returns NULL in case of failure. +. +.Pp +. +.Fn libusb20_tr_get_time_complete +This function will return the completion time of an USB transfer in +millisecond units. This function is most useful for isochronous USB +transfers when doing echo cancelling. +. +.Pp +. +.Fn libusb20_tr_get_actual_frames +This function will return the actual number of USB frames after an USB +transfer completed. A value of zero means that no data was transferred. +. +.Pp +. +.Fn libusb20_tr_get_actual_length +This function will return the sum of the actual length for all +transferred USB frames for the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_get_max_frames +This function will return the maximum number of USB frames that were +allocated when an USB transfer was setup for the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_get_max_packet_length +This function will return the maximum packet length in bytes +associated with the given USB transfer. +. +The packet length can be used round up buffer sizes so that short USB +packets are avoided for proxy buffers. +. +. +.Pp +. +.Fn libusb20_tr_get_max_total_length +This function will return the maximum value for the length sum of all +USB frames associated with an USB transfer. +. +.Pp +. +.Fn libusb20_tr_get_status +This function will return the status of an USB transfer. +. +Status values are defined by a set of LIBUSB20_TRANSFER_XXX enums. +. +.Pp +. +.Fn libusb20_tr_pending +This function will return non-zero if the given USB transfer is +pending for completion. +. +Else this function returns zero. +. +.Pp +. +.Fn libusb20_tr_callback_wrapper +This is an internal function used to wrap asynchronous USB callbacks. +. +.Pp +. +.Fn libusb20_tr_clear_stall_sync +This is an internal function used to synchronously clear the stall on +the given USB transfer. +. +Please see the USB specification for more information on stall +clearing. +. +If the given USB transfer is pending when this function is called, the +USB transfer will complete with an error after that this function has +been called. +. +.Pp +. +.Fn libusb20_tr_drain +This function will stop the given USB transfer and will not return +until the USB transfer has been stopped in hardware. +. +.Pp +. +.Fn libusb20_tr_set_buffer +This function is used to set the +.Fa buffer +pointer for the given USB transfer and +.Fa fr_index . +. +Typically the frame index is zero. +. +. +.Pp +. +.Fn libusb20_tr_set_callback +This function is used to set the USB callback for asynchronous USB +transfers. +. +The callback type is defined by libusb20_tr_callback_t. +. +.Pp +. +.Fn libusb20_tr_set_flags +This function is used to set various USB flags for the given USB transfer. +.Bl -tag +.It LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK +Report a short frame as error. +.It LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK +Multiple short frames are not allowed. +.It LIBUSB20_TRANSFER_FORCE_SHORT +All transmitted frames are short terminated. +.It LIBUSB20_TRANSFER_DO_CLEAR_STALL +Will do a clear-stall before starting the transfer. +.El +. +.Pp +. +.Fn libusb20_tr_set_length +This function sets the length of a given USB transfer and frame index. +. +.Pp +. +.Fn libusb20_tr_set_priv_sc0 +This function sets private driver pointer number zero. +. +.Pp +. +.Fn libusb20_tr_set_priv_sc1 +This function sets private driver pointer number one. +. +.Pp +. +.Fn libusb20_tr_set_timeout +This function sets the timeout for the given USB transfer. +. +A timeout value of zero means no timeout. +. +The timeout is given in milliseconds. +. +.Pp +. +.Fn libusb20_tr_set_total_frames +This function sets the total number of frames that should be executed when the USB transfer is submitted. +. +The total number of USB frames must be less than the maximum number of USB frames associated with the given USB transfer. +. +.Pp +. +.Fn libusb20_tr_setup_bulk +This function is a helper function for setting up a single frame USB BULK transfer. +. +.Pp +. +.Fn libusb20_tr_setup_control +This function is a helper function for setting up a single or dual +frame USB CONTROL transfer depending on the control transfer length. +. +.Pp +. +.Fn libusb20_tr_setup_intr +This function is a helper function for setting up a single frame USB INTERRUPT transfer. +. +.Pp +. +.Fn libusb20_tr_setup_isoc +This function is a helper function for setting up a multi frame USB ISOCHRONOUS transfer. +. +.Pp +. +.Fn libusb20_tr_start +This function will get the USB transfer started, if not already +started. +. +This function will not get the transfer queued in hardware. +. +This function is non-blocking. +. +.Pp +. +.Fn libusb20_tr_stop +This function will get the USB transfer stopped, if not already stopped. +. +This function is non-blocking, which means that the actual stop can +happen after the return of this function. +. +.Pp +. +.Fn libusb20_tr_submit +This function will get the USB transfer queued in hardware. +. +. +.Pp +. +.Fn libusb20_tr_get_priv_sc0 +This function returns private driver pointer number zero associated +with an USB transfer. +. +. +.Pp +. +.Fn libusb20_tr_get_priv_sc1 +This function returns private driver pointer number one associated +with an USB transfer. +. +. +.Sh USB DEVICE OPERATIONS +. +.Pp +. +.Fn libusb20_dev_get_backend_name +This function returns a zero terminated string describing the backend used. +. +.Pp +. +.Fn libusb20_dev_get_desc +This function returns a zero terminated string describing the given USB device. +. +.Pp +. +.Fn libusb20_dev_claim_interface +This function will try to claim the given USB interface given by +.Fa iface_index . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_close +This function will close the given USB device. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_detach_kernel_driver +This function will try to detach the kernel driver for the USB interface given by +.Fa iface_index . +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_config_index +This function will try to set the configuration index on an USB +device. +. +The first configuration index is zero. +. +The un-configure index is 255. +. +This function returns zero on success else a LIBUSB20_ERROR value is returned. +. +.Pp +. +.Fn libusb20_dev_get_debug +This function returns the debug level of an USB device. +. +.Pp +. +.Fn libusb20_dev_get_fd +This function returns the file descriptor of the given USB device. +. +A negative value is returned when no file descriptor is present. +. +The file descriptor can be used for polling purposes. +. +.Pp +. +.Fn libusb20_dev_kernel_driver_active +This function returns a non-zero value if a kernel driver is active on +the given USB interface. +. +Else zero is returned. +. +.Pp +. +.Fn libusb20_dev_open +This function opens an USB device so that setting up USB transfers +becomes possible. +. +The number of USB transfers can be zero which means only control +transfers are allowed. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +A return value of LIBUSB20_ERROR_BUSY means that the device is already +opened. +. +.Pp +. +.Fn libusb20_dev_process +This function is called to sync kernel USB transfers with userland USB +transfers. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned typically indicating that the given USB device has been +detached. +. +.Pp +. +.Fn libusb20_dev_release_interface +This function will try to release a claimed USB interface for the specified USB device. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_request_sync +This function will perform a synchronous control request on the given +USB device. +. +Before this call will succeed the USB device must be opened. +. +.Fa setup +is a pointer to a decoded and host endian SETUP packet. +.Fa data +is a pointer to a data transfer buffer associated with the control transaction. This argument can be NULL. +.Fa pactlen +is a pointer to a variable that will hold the actual transfer length after the control transaction is complete. +.Fa timeout +is the transaction timeout given in milliseconds. +A timeout of zero means no timeout. +.Fa flags +is used to specify transaction flags, for example LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_req_string_sync +This function will synchronously request an USB string by language ID +and string index into the given buffer limited by a maximum length. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_req_string_simple_sync +This function will synchronously request an USB string using the +default language ID and convert the string into ASCII before storing +the string into the given buffer limited by a maximum length which +includes the terminating zero. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +. +.Pp +. +.Fn libusb20_dev_reset +This function will try to BUS reset the given USB device and restore +the last set USB configuration. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_power_mode +This function sets the power mode of the USB device. +. +Valid power modes: +.Bl -tag +.It LIBUSB20_POWER_OFF +.It LIBUSB20_POWER_ON +.It LIBUSB20_POWER_SAVE +.It LIBUSB20_POWER_SUSPEND +.It LIBUSB20_POWER_RESUME +.El +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_power_mode +This function returns the currently selected power mode for the given +USB device. +. +.Pp +. +.Fn libusb20_dev_set_alt_index +This function will try to set the given alternate index for the given +USB interface index. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_owner +This function will set the ownership of the given USB device. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_perm +This function will set the permissions of the given USB device. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_iface_owner +This function will set the ownership of the given USB interface. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_set_iface_perm +This function will set the permissions of the given USB interface. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_owner +This function will retrieve the current USB device ownership. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_perm +This function will retrieve the current USB device permissions. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_iface_owner +This function will retrieve the current USB interface ownership for +the given USB interface. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_iface_perm +This function will retrieve the current USB interface permissions for +the given USB interface. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_dev_get_device_desc +This function returns a pointer to the decoded and host endian version +of the device descriptor. +. +The USB device need not be opened when calling this function. +. +.Pp +. +.Fn libusb20_dev_alloc_config +This function will read out and decode the USB config descriptor for +the given USB device and config index. This function returns a pointer +to the decoded configuration which must eventually be passed to +free(). NULL is returned in case of failure. +. +.Pp +. +.Fn libusb20_dev_alloc(void) +This is an internal function to allocate a new USB device. +. +.Pp +. +.Fn libusb20_dev_get_address +This function returns the internal and not necessarily the real +hardware address of the given USB device. +. +.Pp +. +.Fn libusb20_dev_get_bus_number +This function return the internal bus number which the given USB +device belongs to. +. +.Pp +. +.Fn libusb20_dev_get_mode +This function returns the current operation mode of the USB entity. +. +Valid return values are: +.Bl -tag +.It LIBUSB20_MODE_HOST +.It LIBUSB20_MODE_DEVICE +.El +. +.Pp +. +.Fn libusb20_dev_get_speed +This function returns the current speed of the given USB device. +. +.Bl -tag +.It LIBUSB20_SPEED_UNKNOWN +.It LIBUSB20_SPEED_LOW +.It LIBUSB20_SPEED_FULL +.It LIBUSB20_SPEED_HIGH +.It LIBUSB20_SPEED_VARIABLE +.It LIBUSB20_SPEED_SUPER +.El +. +.Pp +. +.Fn libusb20_dev_get_config_index +This function returns the currently select config index for the given +USB device. +. +.Pp +. +.Fn libusb20_dev_free +This function will free the given USB device and all associated USB +transfers. +. +.Pp +. +.Fn libusb20_dev_set_debug +This function will set the debug level for the given USB device. +. +.Pp +. +.Fn libusb20_dev_wait_process +This function will wait until a pending USB transfer has completed on +the given USB device. +. +A timeout value can be specified which is passed on to the +.Xr 2 poll +function. +. +.Sh USB BUS OPERATIONS +. +.Fn libusb20_bus_set_owner +This function will set the ownership for the given USB bus. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_bus_set_perm +This function will set the permissions for the given USB bus. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_bus_get_owner +This function will retrieve the ownership for the given USB bus. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_bus_get_perm +This function will retrieve the permissions for the given USB bus. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +. +.Sh USB BACKEND OPERATIONS +. +.Fn libusb20_be_get_dev_quirk +This function will return the device quirk according to +.Fa index +into the libusb20_quirk structure pointed to by +.Fa pq . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_get_quirk_name +This function will return the quirk name according to +.Fa index +into the libusb20_quirk structure pointed to by +.Fa pq . +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_add_dev_quirk +This function will add the libusb20_quirk structure pointed to by the +.Fa pq +argument into the device quirk list. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk cannot be added LIBUSB20_ERROR_NO_MEM is +returned. +. +.Pp +. +.Fn libusb20_be_remove_dev_quirk +This function will remove the quirk matching the libusb20_quirk structure pointed to by the +.Fa pq +argument from the device quirk list. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is +returned. +. +.Pp +. +.Fn libusb20_be_set_owner +This function will set the ownership for the given backend. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_set_perm +This function will set the permissions for the given backend. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_get_owner +This function will retrieve the ownership of the given backend. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_get_perm +This function will retrieve the permissions of the given backend. +. +. +This function returns zero on success else a LIBUSB20_ERROR value is +returned. +. +.Pp +. +.Fn libusb20_be_alloc +This is an internal function to allocate a USB backend. +. +.Pp +.Fn libusb20_be_alloc_default +.Fn libusb20_be_alloc_freebsd +.Fn libusb20_be_alloc_linux +These functions are used to allocate a specific USB backend or the +operating system default USB backend. Allocating a backend is a way to +scan for currently present USB devices. +. +.Pp +. +.Fn libusb20_be_device_foreach +This function is used to iterate USB devices present in a USB backend. +. +The starting value of +.Fa pdev +is NULL. +. +This function returns the next USB device in the list. +. +If NULL is returned the end of the USB device list has been reached. +. +.Pp +. +.Fn libusb20_be_dequeue_device +This function will dequeue the given USB device pointer from the +backend USB device list. +. +Dequeued USB devices will not be freed when the backend is freed. +. +.Pp +. +.Fn libusb20_be_enqueue_device +This function will enqueue the given USB device pointer in the backend USB device list. +. +Enqueued USB devices will get freed when the backend is freed. +. +.Pp +. +.Fn libusb20_be_free +This function will free the given backend and all USB devices in its device list. +. +. +.Sh USB DESCRIPTOR PARSING +. +.Fn libusb20_me_get_1 +This function will return a byte at the given byte offset of a message +entity. +. +This function is safe against invalid offsets. +. +.Pp +. +.Fn libusb20_me_get_2 +This function will return a little endian 16-bit value at the given byte offset of a message +entity. +. +This function is safe against invalid offsets. +. +.Pp +. +.Fn libusb20_me_encode +This function will encode a so-called *DECODED structure into binary +format. +. +The total encoded length that will fit in the given buffer is +returned. +. +If the buffer pointer is NULL no data will be written to the buffer +location. +. +.Pp +. +.Fn libusb20_me_decode +This function will decode a binary structure into a so-called *DECODED +structure. +. +The total decoded length is returned. +. +The buffer pointer cannot be NULL. +. +. +.Sh LIBUSB VERSION 0.1 COMPATIBILITY +. +.Fn usb_open +.Fn usb_close +.Fn usb_get_string +.Fn usb_get_string_simple +.Fn usb_get_descriptor_by_endpoint +.Fn usb_get_descriptor +.Fn usb_parse_descriptor +.Fn usb_parse_configuration +.Fn usb_destroy_configuration +.Fn usb_fetch_and_parse_descriptors +.Fn usb_bulk_write +.Fn usb_bulk_read +.Fn usb_interrupt_write +.Fn usb_interrupt_read +.Fn usb_control_msg +.Fn usb_set_configuration +.Fn usb_claim_interface +.Fn usb_release_interface +.Fn usb_set_altinterface +.Fn usb_resetep +.Fn usb_clear_halt +.Fn usb_reset +.Fn usb_strerror +.Fn usb_init +.Fn usb_set_debug +.Fn usb_find_busses +.Fn usb_find_devices +.Fn usb_device +.Fn usb_get_busses +These functions are compliant with LibUSB version 0.1.12. +. +.Sh FILES +. +. +/dev/usb +.Sh SEE ALSO +.Xr usb2_core 4 , +.Xr usbconfig 8 +. +. +.Sh HISTORY +. +. +Some parts of the +.Nm +API derives from the libusb project at sourceforge. diff --git a/lib/libusb20/libusb20.c b/lib/libusb20/libusb20.c new file mode 100644 index 000000000000..432cd993b438 --- /dev/null +++ b/lib/libusb20/libusb20.c @@ -0,0 +1,1245 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include +#include + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +static int +dummy_int(void) +{ + return (LIBUSB20_ERROR_NOT_SUPPORTED); +} + +static void +dummy_void(void) +{ + return; +} + +static void +dummy_callback(struct libusb20_transfer *xfer) +{ + ; /* style fix */ + switch (libusb20_tr_get_status(xfer)) { + case LIBUSB20_TRANSFER_START: + libusb20_tr_submit(xfer); + break; + default: + /* complete or error */ + break; + } + return; +} + +#define dummy_get_config_desc_full (void *)dummy_int +#define dummy_get_config_index (void *)dummy_int +#define dummy_set_config_index (void *)dummy_int +#define dummy_claim_interface (void *)dummy_int +#define dummy_release_interface (void *)dummy_int +#define dummy_set_alt_index (void *)dummy_int +#define dummy_reset_device (void *)dummy_int +#define dummy_set_power_mode (void *)dummy_int +#define dummy_get_power_mode (void *)dummy_int +#define dummy_kernel_driver_active (void *)dummy_int +#define dummy_detach_kernel_driver (void *)dummy_int +#define dummy_do_request_sync (void *)dummy_int +#define dummy_tr_open (void *)dummy_int +#define dummy_tr_close (void *)dummy_int +#define dummy_tr_clear_stall_sync (void *)dummy_int +#define dummy_process (void *)dummy_int + +#define dummy_tr_submit (void *)dummy_void +#define dummy_tr_cancel_async (void *)dummy_void + +static const struct libusb20_device_methods libusb20_dummy_methods = { + LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy) +}; + +void +libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer) +{ + ; /* style fix */ + +repeat: + + if (!xfer->is_pending) { + xfer->status = LIBUSB20_TRANSFER_START; + } else { + xfer->is_pending = 0; + } + + (xfer->callback) (xfer); + + if (xfer->is_restart) { + xfer->is_restart = 0; + goto repeat; + } + if (xfer->is_draining && + (!xfer->is_pending)) { + xfer->is_draining = 0; + xfer->status = LIBUSB20_TRANSFER_DRAINED; + (xfer->callback) (xfer); + } + return; +} + +int +libusb20_tr_close(struct libusb20_transfer *xfer) +{ + int error; + + if (!xfer->is_opened) { + return (LIBUSB20_ERROR_OTHER); + } + error = (xfer->pdev->methods->tr_close) (xfer); + + if (xfer->pLength) { + free(xfer->pLength); + } + if (xfer->ppBuffer) { + free(xfer->ppBuffer); + } + /* clear some fields */ + xfer->is_opened = 0; + xfer->maxFrames = 0; + xfer->maxTotalLength = 0; + xfer->maxPacketLen = 0; + return (error); +} + +int +libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, + uint32_t MaxFrameCount, uint8_t ep_no) +{ + uint32_t size; + int error; + + if (xfer->is_opened) { + return (LIBUSB20_ERROR_BUSY); + } + if (MaxFrameCount == 0) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + xfer->maxFrames = MaxFrameCount; + + size = MaxFrameCount * sizeof(xfer->pLength[0]); + xfer->pLength = malloc(size); + if (xfer->pLength == NULL) { + return (LIBUSB20_ERROR_NO_MEM); + } + memset(xfer->pLength, 0, size); + + size = MaxFrameCount * sizeof(xfer->ppBuffer[0]); + xfer->ppBuffer = malloc(size); + if (xfer->ppBuffer == NULL) { + free(xfer->pLength); + return (LIBUSB20_ERROR_NO_MEM); + } + memset(xfer->ppBuffer, 0, size); + + error = (xfer->pdev->methods->tr_open) (xfer, MaxBufSize, + MaxFrameCount, ep_no); + + if (error) { + free(xfer->ppBuffer); + free(xfer->pLength); + } else { + xfer->is_opened = 1; + } + return (error); +} + +struct libusb20_transfer * +libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex) +{ + if (trIndex >= pdev->nTransfer) { + return (NULL); + } + return (pdev->pTransfer + trIndex); +} + +uint32_t +libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer) +{ + return (xfer->aFrames); +} + +uint16_t +libusb20_tr_get_time_complete(struct libusb20_transfer *xfer) +{ + return (xfer->timeComplete); +} + +uint32_t +libusb20_tr_get_actual_length(struct libusb20_transfer *xfer) +{ + uint32_t x; + uint32_t actlen = 0; + + for (x = 0; x != xfer->aFrames; x++) { + actlen += xfer->pLength[x]; + } + return (actlen); +} + +uint32_t +libusb20_tr_get_max_frames(struct libusb20_transfer *xfer) +{ + return (xfer->maxFrames); +} + +uint32_t +libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer) +{ + /* + * Special Case NOTE: If the packet multiplier is non-zero for + * High Speed USB, the value returned is equal to + * "wMaxPacketSize * multiplier" ! + */ + return (xfer->maxPacketLen); +} + +uint32_t +libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer) +{ + return (xfer->maxTotalLength); +} + +uint8_t +libusb20_tr_get_status(struct libusb20_transfer *xfer) +{ + return (xfer->status); +} + +uint8_t +libusb20_tr_pending(struct libusb20_transfer *xfer) +{ + return (xfer->is_pending); +} + +void * +libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer) +{ + return (xfer->priv_sc0); +} + +void * +libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer) +{ + return (xfer->priv_sc1); +} + +void +libusb20_tr_stop(struct libusb20_transfer *xfer) +{ + if (!xfer->is_pending) { + /* transfer not pending */ + return; + } + if (xfer->is_cancel) { + /* already cancelling */ + return; + } + xfer->is_cancel = 1; /* we are cancelling */ + + (xfer->pdev->methods->tr_cancel_async) (xfer); + return; +} + +void +libusb20_tr_drain(struct libusb20_transfer *xfer) +{ + /* make sure that we are cancelling */ + libusb20_tr_stop(xfer); + + if (xfer->is_pending) { + xfer->is_draining = 1; + } + return; +} + +void +libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer) +{ + (xfer->pdev->methods->tr_clear_stall_sync) (xfer); + return; +} + +void +libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex) +{ + xfer->ppBuffer[frIndex] = buffer; + return; +} + +void +libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb) +{ + xfer->callback = cb; + return; +} + +void +libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags) +{ + xfer->flags = flags; + return; +} + +void +libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex) +{ + xfer->pLength[frIndex] = length; + return; +} + +void +libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0) +{ + xfer->priv_sc0 = sc0; + return; +} + +void +libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1) +{ + xfer->priv_sc1 = sc1; + return; +} + +void +libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout) +{ + xfer->timeout = timeout; + return; +} + +void +libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames) +{ + if (nFrames > xfer->maxFrames) { + /* should not happen */ + nFrames = xfer->maxFrames; + } + xfer->nFrames = nFrames; + return; +} + +void +libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) +{ + xfer->ppBuffer[0] = pBuf; + xfer->pLength[0] = length; + xfer->timeout = timeout; + xfer->nFrames = 1; + return; +} + +void +libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout) +{ + uint16_t len; + + xfer->ppBuffer[0] = psetup; + xfer->pLength[0] = 8; /* fixed */ + xfer->timeout = timeout; + + len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8); + + if (len != 0) { + xfer->nFrames = 2; + xfer->ppBuffer[1] = pBuf; + xfer->pLength[1] = len; + } else { + xfer->nFrames = 1; + } + return; +} + +void +libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) +{ + xfer->ppBuffer[0] = pBuf; + xfer->pLength[0] = length; + xfer->timeout = timeout; + xfer->nFrames = 1; + return; +} + +void +libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex) +{ + if (frIndex >= xfer->maxFrames) { + /* should not happen */ + return; + } + xfer->ppBuffer[frIndex] = pBuf; + xfer->pLength[frIndex] = length; + return; +} + +void +libusb20_tr_submit(struct libusb20_transfer *xfer) +{ + if (xfer->is_pending) { + /* should not happen */ + return; + } + xfer->is_pending = 1; /* we are pending */ + xfer->is_cancel = 0; /* not cancelling */ + xfer->is_restart = 0; /* not restarting */ + + (xfer->pdev->methods->tr_submit) (xfer); + return; +} + +void +libusb20_tr_start(struct libusb20_transfer *xfer) +{ + if (xfer->is_pending) { + if (xfer->is_cancel) { + /* cancelling - restart */ + xfer->is_restart = 1; + } + /* transfer not pending */ + return; + } + /* get into the callback */ + libusb20_tr_callback_wrapper(xfer); + return; +} + +/* USB device operations */ + +int +libusb20_dev_claim_interface(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + if (ifaceIndex >= 32) { + error = LIBUSB20_ERROR_INVALID_PARAM; + } else if (pdev->claimed_interfaces & (1 << ifaceIndex)) { + error = LIBUSB20_ERROR_NOT_FOUND; + } else { + error = (pdev->methods->claim_interface) (pdev, ifaceIndex); + } + if (!error) { + pdev->claimed_interfaces |= (1 << ifaceIndex); + } + return (error); +} + +int +libusb20_dev_close(struct libusb20_device *pdev) +{ + struct libusb20_transfer *xfer; + uint16_t x; + int error = 0; + + if (!pdev->is_opened) { + return (LIBUSB20_ERROR_OTHER); + } + for (x = 0; x != pdev->nTransfer; x++) { + xfer = pdev->pTransfer + x; + + libusb20_tr_drain(xfer); + } + + if (pdev->pTransfer != NULL) { + free(pdev->pTransfer); + pdev->pTransfer = NULL; + } + error = (pdev->beMethods->close_device) (pdev); + + pdev->methods = &libusb20_dummy_methods; + + pdev->is_opened = 0; + + return (error); +} + +int +libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + error = (pdev->methods->detach_kernel_driver) (pdev, ifaceIndex); + return (error); +} + +struct LIBUSB20_DEVICE_DESC_DECODED * +libusb20_dev_get_device_desc(struct libusb20_device *pdev) +{ + return (&(pdev->ddesc)); +} + +int +libusb20_dev_get_fd(struct libusb20_device *pdev) +{ + return (pdev->file); +} + +int +libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + error = (pdev->methods->kernel_driver_active) (pdev, ifaceIndex); + return (error); +} + +int +libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) +{ + struct libusb20_transfer *xfer; + uint32_t size; + uint16_t x; + int error; + + if (pdev->is_opened) { + return (LIBUSB20_ERROR_BUSY); + } + if (nTransferMax >= 256) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } else if (nTransferMax != 0) { + size = sizeof(pdev->pTransfer[0]) * nTransferMax; + pdev->pTransfer = malloc(size); + if (pdev->pTransfer == NULL) { + return (LIBUSB20_ERROR_NO_MEM); + } + memset(pdev->pTransfer, 0, size); + } + /* initialise all transfers */ + for (x = 0; x != nTransferMax; x++) { + + xfer = pdev->pTransfer + x; + + xfer->pdev = pdev; + xfer->trIndex = x; + xfer->callback = &dummy_callback; + } + + error = (pdev->beMethods->open_device) (pdev, nTransferMax); + + if (error) { + if (pdev->pTransfer != NULL) { + free(pdev->pTransfer); + pdev->pTransfer = NULL; + } + pdev->file = -1; + pdev->file_ctrl = -1; + pdev->nTransfer = 0; + } else { + pdev->is_opened = 1; + pdev->nTransfer = nTransferMax; + } + return (error); +} + +int +libusb20_dev_release_interface(struct libusb20_device *pdev, uint8_t ifaceIndex) +{ + int error; + + if (ifaceIndex >= 32) { + error = LIBUSB20_ERROR_INVALID_PARAM; + } else if (!(pdev->claimed_interfaces & (1 << ifaceIndex))) { + error = LIBUSB20_ERROR_NOT_FOUND; + } else { + error = (pdev->methods->release_interface) (pdev, ifaceIndex); + } + if (!error) { + pdev->claimed_interfaces &= ~(1 << ifaceIndex); + } + return (error); +} + +int +libusb20_dev_reset(struct libusb20_device *pdev) +{ + int error; + + error = (pdev->methods->reset_device) (pdev); + return (error); +} + +int +libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) +{ + int error; + + error = (pdev->methods->set_power_mode) (pdev, power_mode); + return (error); +} + +uint8_t +libusb20_dev_get_power_mode(struct libusb20_device *pdev) +{ + int error; + uint8_t power_mode; + + error = (pdev->methods->get_power_mode) (pdev, &power_mode); + if (error) + power_mode = LIBUSB20_POWER_ON; /* fake power mode */ + return (power_mode); +} + +int +libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex) +{ + int error; + + error = (pdev->methods->set_alt_index) (pdev, ifaceIndex, altIndex); + return (error); +} + +int +libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex) +{ + int error; + + error = (pdev->methods->set_config_index) (pdev, configIndex); + return (error); +} + +int +libusb20_dev_request_sync(struct libusb20_device *pdev, + struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, + uint16_t *pactlen, uint32_t timeout, uint8_t flags) +{ + int error; + + error = (pdev->methods->do_request_sync) (pdev, + setup, data, pactlen, timeout, flags); + return (error); +} + +int +libusb20_dev_req_string_sync(struct libusb20_device *pdev, + uint8_t index, uint16_t langid, void *ptr, uint16_t len) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int error; + + if (len < 4) { + /* invalid length */ + return (LIBUSB20_ERROR_INVALID_PARAM); + } + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + /* + * We need to read the USB string in two steps else some USB + * devices will complain. + */ + req.bmRequestType = + LIBUSB20_REQUEST_TYPE_STANDARD | + LIBUSB20_RECIPIENT_DEVICE | + LIBUSB20_ENDPOINT_IN; + req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; + req.wValue = (LIBUSB20_DT_STRING << 8) | index; + req.wIndex = langid; + req.wLength = 4; /* bytes */ + + error = libusb20_dev_request_sync(pdev, &req, + ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); + if (error) { + return (error); + } + req.wLength = *(uint8_t *)ptr; /* bytes */ + if (req.wLength > len) { + /* partial string read */ + req.wLength = len; + } + error = libusb20_dev_request_sync(pdev, &req, + ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); + + if (error) { + return (error); + } + if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* success */ +} + +int +libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, + uint8_t index, void *ptr, uint16_t len) +{ + char *buf; + int error; + uint16_t langid; + uint16_t n; + uint16_t i; + uint16_t c; + uint8_t temp[255]; + uint8_t swap; + + /* the following code derives from the FreeBSD USB kernel */ + + if ((len < 1) || (ptr == NULL)) { + /* too short buffer */ + return (LIBUSB20_ERROR_INVALID_PARAM); + } + /* + * Make sure that there is sensible contents in the buffer in case + * of an error: + */ + *(uint8_t *)ptr = 0; + + error = libusb20_dev_req_string_sync(pdev, + 0, 0, temp, sizeof(temp)); + if (error < 0) + return (error); + + langid = temp[2] | (temp[3] << 8); + + error = libusb20_dev_req_string_sync(pdev, index, + langid, temp, sizeof(temp)); + if (error < 0) + return (error); + + if (temp[0] < 2) { + /* string length is too short */ + return (LIBUSB20_ERROR_OTHER); + } + /* reserve one byte for terminating zero */ + len--; + + /* find maximum length */ + n = (temp[0] / 2) - 1; + if (n > len) { + n = len; + } + /* reset swap state */ + swap = 3; + + /* setup output buffer pointer */ + buf = ptr; + + /* convert and filter */ + for (i = 0; (i != n); i++) { + c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8); + + /* convert from Unicode, handle buggy strings */ + if (((c & 0xff00) == 0) && (swap & 1)) { + /* Little Endian, default */ + *buf = c; + swap = 1; + } else if (((c & 0x00ff) == 0) && (swap & 2)) { + /* Big Endian */ + *buf = c >> 8; + swap = 2; + } else { + *buf = '.'; + } + /* + * Filter by default - we don't allow greater and less than + * signs because they might confuse the dmesg printouts! + */ + if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) { + *buf = '.'; + } + buf++; + } + *buf = 0; /* zero terminate string */ + + return (0); +} + +struct libusb20_config * +libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex) +{ + struct libusb20_config *retval = NULL; + uint8_t *ptr; + uint16_t len; + uint8_t do_close; + int error; + + if (!pdev->is_opened) { + error = libusb20_dev_open(pdev, 0); + if (error) { + return (NULL); + } + do_close = 1; + } else { + do_close = 0; + } + error = (pdev->methods->get_config_desc_full) (pdev, + &ptr, &len, configIndex); + + if (error) { + goto done; + } + /* parse new config descriptor */ + retval = libusb20_parse_config_desc(ptr); + + /* free config descriptor */ + free(ptr); + +done: + if (do_close) { + error = libusb20_dev_close(pdev); + } + return (retval); +} + +struct libusb20_device * +libusb20_dev_alloc(void) +{ + struct libusb20_device *pdev; + + pdev = malloc(sizeof(*pdev)); + if (pdev == NULL) { + return (NULL); + } + memset(pdev, 0, sizeof(*pdev)); + + pdev->file = -1; + pdev->file_ctrl = -1; + pdev->methods = &libusb20_dummy_methods; + return (pdev); +} + +uint8_t +libusb20_dev_get_config_index(struct libusb20_device *pdev) +{ + int error; + uint8_t index; + uint8_t do_close; + + if (!pdev->is_opened) { + error = libusb20_dev_open(pdev, 0); + if (error == 0) { + do_close = 1; + } else { + do_close = 0; + } + } else { + do_close = 0; + } + + error = (pdev->methods->get_config_index) (pdev, &index); + if (error) { + index = 0 - 1; /* current config index */ + } + if (do_close) { + if (libusb20_dev_close(pdev)) { + /* ignore */ + } + } + return (index); +} + +uint8_t +libusb20_dev_get_mode(struct libusb20_device *pdev) +{ + return (pdev->usb_mode); +} + +uint8_t +libusb20_dev_get_speed(struct libusb20_device *pdev) +{ + return (pdev->usb_speed); +} + +/* if this function returns an error, the device is gone */ +int +libusb20_dev_process(struct libusb20_device *pdev) +{ + int error; + + error = (pdev->methods->process) (pdev); + return (error); +} + +void +libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout) +{ + struct pollfd pfd[2]; + + if (!pdev->is_opened) { + return; + } + pfd[0].fd = pdev->file; + pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); + pfd[0].revents = 0; + pfd[1].fd = 0; /* standard input */ + pfd[1].events = (POLLIN | POLLRDNORM); + pfd[1].revents = 0; + + if (poll(pfd, 2, timeout)) { + /* ignore any error */ + } + return; +} + +void +libusb20_dev_free(struct libusb20_device *pdev) +{ + if (pdev == NULL) { + /* be NULL safe */ + return; + } + if (pdev->is_opened) { + if (libusb20_dev_close(pdev)) { + /* ignore any errors */ + } + } + free(pdev); + return; +} + +const char * +libusb20_dev_get_backend_name(struct libusb20_device *pdev) +{ + return ((pdev->beMethods->get_backend_name) ()); +} + +const char * +libusb20_dev_get_desc(struct libusb20_device *pdev) +{ + return (pdev->usb_desc); +} + +void +libusb20_dev_set_debug(struct libusb20_device *pdev, int debug) +{ + pdev->debug = debug; + return; +} + +int +libusb20_dev_get_debug(struct libusb20_device *pdev) +{ + return (pdev->debug); +} + +uint8_t +libusb20_dev_get_address(struct libusb20_device *pdev) +{ + return (pdev->device_address); +} + +uint8_t +libusb20_dev_get_bus_number(struct libusb20_device *pdev) +{ + return (pdev->bus_number); +} + +int +libusb20_dev_set_owner(struct libusb20_device *pdev, uid_t user, gid_t group) +{ + return ((pdev->beMethods->dev_set_owner) (pdev, user, group)); +} + +int +libusb20_dev_set_perm(struct libusb20_device *pdev, mode_t mode) +{ + return ((pdev->beMethods->dev_set_perm) (pdev, mode)); +} + +int +libusb20_dev_set_iface_owner(struct libusb20_device *pdev, uint8_t iface_index, uid_t user, gid_t group) +{ + return ((pdev->beMethods->dev_set_iface_owner) (pdev, iface_index, user, group)); +} + +int +libusb20_dev_set_iface_perm(struct libusb20_device *pdev, uint8_t iface_index, mode_t mode) +{ + return ((pdev->beMethods->dev_set_iface_perm) (pdev, iface_index, mode)); +} + +int +libusb20_dev_get_owner(struct libusb20_device *pdev, uid_t *user, gid_t *group) +{ + uid_t a; + gid_t b; + + if (user == NULL) + user = &a; + if (group == NULL) + group = &b; + + return ((pdev->beMethods->dev_get_owner) (pdev, user, group)); +} + +int +libusb20_dev_get_perm(struct libusb20_device *pdev, mode_t *mode) +{ + mode_t a; + + if (mode == NULL) + mode = &a; + return ((pdev->beMethods->dev_get_perm) (pdev, mode)); +} + +int +libusb20_dev_get_iface_owner(struct libusb20_device *pdev, uint8_t iface_index, uid_t *user, gid_t *group) +{ + uid_t a; + gid_t b; + + if (user == NULL) + user = &a; + if (group == NULL) + group = &b; + + return ((pdev->beMethods->dev_get_iface_owner) (pdev, iface_index, user, group)); +} + +int +libusb20_dev_get_iface_perm(struct libusb20_device *pdev, uint8_t iface_index, mode_t *mode) +{ + mode_t a; + + if (mode == NULL) + mode = &a; + return ((pdev->beMethods->dev_get_iface_perm) (pdev, iface_index, mode)); +} + +/* USB bus operations */ + +int +libusb20_bus_set_owner(struct libusb20_backend *pbe, uint8_t bus, uid_t user, gid_t group) +{ + return ((pbe->methods->bus_set_owner) (pbe, bus, user, group)); +} + +int +libusb20_bus_set_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t mode) +{ + return ((pbe->methods->bus_set_perm) (pbe, bus, mode)); +} + +int +libusb20_bus_get_owner(struct libusb20_backend *pbe, uint8_t bus, uid_t *user, gid_t *group) +{ + uid_t a; + gid_t b; + + if (user == NULL) + user = &a; + if (group == NULL) + group = &b; + return ((pbe->methods->bus_get_owner) (pbe, bus, user, group)); +} + +int +libusb20_bus_get_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t *mode) +{ + mode_t a; + + if (mode == NULL) + mode = &a; + return ((pbe->methods->bus_get_perm) (pbe, bus, mode)); +} + +/* USB backend operations */ + +int +libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, + uint16_t index, struct libusb20_quirk *pq) +{ + return ((pbe->methods->root_get_dev_quirk) (pbe, index, pq)); +} + +int +libusb20_be_get_quirk_name(struct libusb20_backend *pbe, + uint16_t index, struct libusb20_quirk *pq) +{ + return ((pbe->methods->root_get_quirk_name) (pbe, index, pq)); +} + +int +libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + return ((pbe->methods->root_add_dev_quirk) (pbe, pq)); +} + +int +libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + return ((pbe->methods->root_remove_dev_quirk) (pbe, pq)); +} + +int +libusb20_be_set_owner(struct libusb20_backend *pbe, uid_t user, gid_t group) +{ + return ((pbe->methods->root_set_owner) (pbe, user, group)); +} + +int +libusb20_be_set_perm(struct libusb20_backend *pbe, mode_t mode) +{ + return ((pbe->methods->root_set_perm) (pbe, mode)); +} + +int +libusb20_be_get_owner(struct libusb20_backend *pbe, uid_t *user, gid_t *group) +{ + uid_t a; + gid_t b; + + if (user == NULL) + user = &a; + if (group == NULL) + group = &b; + return ((pbe->methods->root_get_owner) (pbe, user, group)); +} + +int +libusb20_be_get_perm(struct libusb20_backend *pbe, mode_t *mode) +{ + mode_t a; + + if (mode == NULL) + mode = &a; + return ((pbe->methods->root_get_perm) (pbe, mode)); +} + +struct libusb20_device * +libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev) +{ + if (pbe == NULL) { + pdev = NULL; + } else if (pdev == NULL) { + pdev = TAILQ_FIRST(&(pbe->usb_devs)); + } else { + pdev = TAILQ_NEXT(pdev, dev_entry); + } + return (pdev); +} + +struct libusb20_backend * +libusb20_be_alloc(const struct libusb20_backend_methods *methods) +{ + struct libusb20_backend *pbe; + + pbe = malloc(sizeof(*pbe)); + if (pbe == NULL) { + return (NULL); + } + memset(pbe, 0, sizeof(*pbe)); + + TAILQ_INIT(&(pbe->usb_devs)); + + pbe->methods = methods; /* set backend methods */ + + /* do the initial device scan */ + if (pbe->methods->init_backend) { + (pbe->methods->init_backend) (pbe); + } + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_linux(void) +{ + struct libusb20_backend *pbe; + +#ifdef __linux__ + pbe = libusb20_be_alloc(&libusb20_linux_backend); +#else + pbe = NULL; +#endif + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_ugen20(void) +{ + struct libusb20_backend *pbe; + +#ifdef __FreeBSD__ + pbe = libusb20_be_alloc(&libusb20_ugen20_backend); +#else + pbe = NULL; +#endif + return (pbe); +} + +struct libusb20_backend * +libusb20_be_alloc_default(void) +{ + struct libusb20_backend *pbe; + + pbe = libusb20_be_alloc_linux(); + if (pbe) { + return (pbe); + } + pbe = libusb20_be_alloc_ugen20(); + if (pbe) { + return (pbe); + } + return (NULL); /* no backend found */ +} + +void +libusb20_be_free(struct libusb20_backend *pbe) +{ + struct libusb20_device *pdev; + + if (pbe == NULL) { + /* be NULL safe */ + return; + } + while ((pdev = libusb20_be_device_foreach(pbe, NULL))) { + libusb20_be_dequeue_device(pbe, pdev); + libusb20_dev_free(pdev); + } + if (pbe->methods->exit_backend) { + (pbe->methods->exit_backend) (pbe); + } + return; +} + +void +libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) +{ + pdev->beMethods = pbe->methods; /* copy backend methods */ + TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); + return; +} + +void +libusb20_be_dequeue_device(struct libusb20_backend *pbe, + struct libusb20_device *pdev) +{ + TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); + return; +} diff --git a/lib/libusb20/libusb20.h b/lib/libusb20/libusb20.h new file mode 100644 index 000000000000..7fb8e68bae67 --- /dev/null +++ b/lib/libusb20/libusb20.h @@ -0,0 +1,313 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2007-2008 Daniel Drake. All rights reserved. + * Copyright (c) 2001 Johannes Erdfelt. 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. + */ + +#ifndef _LIBUSB20_H_ +#define _LIBUSB20_H_ + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +}; /* style */ + +#endif + +/** \ingroup misc + * Error codes. Most libusb20 functions return 0 on success or one of + * these codes on failure. + */ +enum libusb20_error { + /** Success (no error) */ + LIBUSB20_SUCCESS = 0, + + /** Input/output error */ + LIBUSB20_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB20_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB20_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB20_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB20_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB20_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB20_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB20_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB20_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB20_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB20_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB20_ERROR_NOT_SUPPORTED = -12, + + /** Other error */ + LIBUSB20_ERROR_OTHER = -99, +}; + +/** \ingroup asyncio + * libusb20_tr_get_status() values */ +enum libusb20_transfer_status { + /** Transfer completed without error. Note that this does not + * indicate that the entire amount of requested data was + * transferred. */ + LIBUSB20_TRANSFER_COMPLETED, + + /** Callback code to start transfer */ + LIBUSB20_TRANSFER_START, + + /** Drain complete callback code */ + LIBUSB20_TRANSFER_DRAINED, + + /** Transfer failed */ + LIBUSB20_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB20_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB20_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected + * (endpoint stalled). For control endpoints: control request + * not supported. */ + LIBUSB20_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB20_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB20_TRANSFER_OVERFLOW, +}; + +/** \ingroup asyncio + * libusb20_tr_set_flags() values */ +enum libusb20_transfer_flags { + /** Report a short frame as error */ + LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK = 0x0001, + + /** Multiple short frames are not allowed */ + LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK = 0x0002, + + /** All transmitted frames are short terminated */ + LIBUSB20_TRANSFER_FORCE_SHORT = 0x0004, + + /** Will do a clear-stall before xfer */ + LIBUSB20_TRANSFER_DO_CLEAR_STALL = 0x0008, +}; + +/** \ingroup misc + * libusb20_dev_get_mode() values + */ +enum libusb20_device_mode { + LIBUSB20_MODE_HOST, /* default */ + LIBUSB20_MODE_DEVICE, +}; + +/** \ingroup misc + * libusb20_dev_get_speed() values + */ +enum { + LIBUSB20_SPEED_UNKNOWN, /* default */ + LIBUSB20_SPEED_LOW, + LIBUSB20_SPEED_FULL, + LIBUSB20_SPEED_HIGH, + LIBUSB20_SPEED_VARIABLE, + LIBUSB20_SPEED_SUPER, +}; + +/** \ingroup misc + * libusb20_dev_set_power() values + */ +enum { + LIBUSB20_POWER_OFF, + LIBUSB20_POWER_ON, + LIBUSB20_POWER_SAVE, + LIBUSB20_POWER_SUSPEND, + LIBUSB20_POWER_RESUME, +}; + +struct libusb20_transfer; +struct libusb20_backend; +struct libusb20_backend_methods; +struct libusb20_device; +struct libusb20_device_methods; +struct libusb20_config; +struct LIBUSB20_CONTROL_SETUP_DECODED; +struct LIBUSB20_DEVICE_DESC_DECODED; + +typedef void (libusb20_tr_callback_t)(struct libusb20_transfer *xfer); + +struct libusb20_quirk { + uint16_t vid; /* vendor ID */ + uint16_t pid; /* product ID */ + uint16_t bcdDeviceLow; /* low revision value, inclusive */ + uint16_t bcdDeviceHigh; /* high revision value, inclusive */ + uint16_t reserved[2]; /* for the future */ + /* quirk name, UQ_XXX, including terminating zero */ + char quirkname[64 - 12]; +}; + +/* USB transfer operations */ + +int libusb20_tr_close(struct libusb20_transfer *xfer); +int libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t pMaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no); +struct libusb20_transfer *libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t tr_index); +uint16_t libusb20_tr_get_time_complete(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_actual_length(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_frames(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer); +uint32_t libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer); +uint8_t libusb20_tr_get_status(struct libusb20_transfer *xfer); +uint8_t libusb20_tr_pending(struct libusb20_transfer *xfer); +void libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer); +void libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer); +void libusb20_tr_drain(struct libusb20_transfer *xfer); +void libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t fr_index); +void libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb); +void libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags); +void libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t fr_index); +void libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0); +void libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1); +void libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout); +void libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames); +void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); +void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pbuf, uint32_t timeout); +void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); +void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint16_t fr_index); +void libusb20_tr_start(struct libusb20_transfer *xfer); +void libusb20_tr_stop(struct libusb20_transfer *xfer); +void libusb20_tr_submit(struct libusb20_transfer *xfer); +void *libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer); +void *libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer); + + +/* USB device operations */ + +const char *libusb20_dev_get_backend_name(struct libusb20_device *pdev); +const char *libusb20_dev_get_desc(struct libusb20_device *pdev); +int libusb20_dev_claim_interface(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_close(struct libusb20_device *pdev); +int libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex); +int libusb20_dev_get_debug(struct libusb20_device *pdev); +int libusb20_dev_get_fd(struct libusb20_device *pdev); +int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_open(struct libusb20_device *pdev, uint16_t transfer_max); +int libusb20_dev_process(struct libusb20_device *pdev); +int libusb20_dev_release_interface(struct libusb20_device *pdev, uint8_t iface_index); +int libusb20_dev_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); +int libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t index, uint16_t langid, void *ptr, uint16_t len); +int libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, uint8_t index, void *ptr, uint16_t len); +int libusb20_dev_reset(struct libusb20_device *pdev); +int libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode); +uint8_t libusb20_dev_get_power_mode(struct libusb20_device *pdev); +int libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); +int libusb20_dev_set_owner(struct libusb20_device *pdev, uid_t user, gid_t group); +int libusb20_dev_set_perm(struct libusb20_device *pdev, mode_t mode); +int libusb20_dev_set_iface_owner(struct libusb20_device *pdev, uint8_t iface_index, uid_t user, gid_t group); +int libusb20_dev_set_iface_perm(struct libusb20_device *pdev, uint8_t iface_index, mode_t mode); +int libusb20_dev_get_owner(struct libusb20_device *pdev, uid_t *user, gid_t *group); +int libusb20_dev_get_perm(struct libusb20_device *pdev, mode_t *mode); +int libusb20_dev_get_iface_owner(struct libusb20_device *pdev, uint8_t iface_index, uid_t *user, gid_t *group); +int libusb20_dev_get_iface_perm(struct libusb20_device *pdev, uint8_t iface_index, mode_t *mode); + +struct LIBUSB20_DEVICE_DESC_DECODED *libusb20_dev_get_device_desc(struct libusb20_device *pdev); +struct libusb20_config *libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t config_index); +struct libusb20_device *libusb20_dev_alloc(void); +uint8_t libusb20_dev_get_address(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_bus_number(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_mode(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_speed(struct libusb20_device *pdev); +uint8_t libusb20_dev_get_config_index(struct libusb20_device *pdev); +void libusb20_dev_free(struct libusb20_device *pdev); +void libusb20_dev_set_debug(struct libusb20_device *pdev, int debug); +void libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout); + +/* USB bus operations */ + +int libusb20_bus_set_owner(struct libusb20_backend *pbe, uint8_t bus, uid_t user, gid_t group); +int libusb20_bus_set_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t mode); +int libusb20_bus_get_owner(struct libusb20_backend *pbe, uint8_t bus, uid_t *user, gid_t *group); +int libusb20_bus_get_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t *mode); + +/* USB global operations */ + +int libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +int libusb20_be_get_quirk_name(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +int libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +int libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +int libusb20_be_set_owner(struct libusb20_backend *be, uid_t user, gid_t group); +int libusb20_be_set_perm(struct libusb20_backend *be, mode_t mode); +int libusb20_be_get_owner(struct libusb20_backend *be, uid_t *user, gid_t *group); +int libusb20_be_get_perm(struct libusb20_backend *be, mode_t *mode); + +/* USB backend operations */ + +struct libusb20_backend *libusb20_be_alloc(const struct libusb20_backend_methods *methods); +struct libusb20_backend *libusb20_be_alloc_default(void); +struct libusb20_backend *libusb20_be_alloc_freebsd(void); +struct libusb20_backend *libusb20_be_alloc_linux(void); +struct libusb20_device *libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); +void libusb20_be_free(struct libusb20_backend *pbe); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_H_ */ diff --git a/lib/libusb20/libusb20_compat01.c b/lib/libusb20/libusb20_compat01.c new file mode 100644 index 000000000000..e9d9ebe12a16 --- /dev/null +++ b/lib/libusb20/libusb20_compat01.c @@ -0,0 +1,902 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 file contains the emulation layer for LibUSB v0.1 from sourceforge. + */ + +#include + +#include +#include + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb20_compat01.h" + +/* + * The two following macros were taken from the original LibUSB v0.1 + * for sake of compatibility: + */ +#define LIST_ADD(begin, ent) \ + do { \ + if (begin) { \ + ent->next = begin; \ + ent->next->prev = ent; \ + } else { \ + ent->next = NULL; \ + } \ + ent->prev = NULL; \ + begin = ent; \ + } while(0) + +#define LIST_DEL(begin, ent) \ + do { \ + if (ent->prev) { \ + ent->prev->next = ent->next; \ + } else { \ + begin = ent->next; \ + } \ + if (ent->next) { \ + ent->next->prev = ent->prev; \ + } \ + ent->prev = NULL; \ + ent->next = NULL; \ + } while (0) + +struct usb_bus *usb_busses = NULL; + +static struct usb_bus usb_global_bus = { + .dirname = {"/dev/usb"}, + .root_dev = NULL, + .devices = NULL, +}; + +static struct libusb20_backend *usb_backend = NULL; + +struct usb_parse_state { + + struct { + struct libusb20_endpoint *currep; + struct libusb20_interface *currifc; + struct libusb20_config *currcfg; + struct libusb20_me_struct *currextra; + } a; + + struct { + struct usb_config_descriptor *currcfg; + struct usb_interface_descriptor *currifc; + struct usb_endpoint_descriptor *currep; + struct usb_interface *currifcw; + uint8_t *currextra; + } b; + + uint8_t preparse; +}; + +static uint8_t +usb_get_first_claimed_interface(usb_dev_handle * dev) +{ + struct libusb20_device *pdev = (void *)dev; + uint32_t x; + uint8_t y; + + x = pdev->claimed_interfaces; + + for (y = 0; y != 32; y++) { + if (x & (1 << y)) + break; + } + + if (y == 32) + y = 0xFF; /* dummy */ + + return (y); +} + +static struct libusb20_transfer * +usb_get_transfer_by_ep_no(usb_dev_handle * dev, uint8_t ep_no) +{ + struct libusb20_device *pdev = (void *)dev; + struct libusb20_transfer *xfer; + int err; + uint32_t bufsize; + uint8_t x; + uint8_t speed; + + x = (ep_no & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 2; + + if (ep_no & LIBUSB20_ENDPOINT_DIR_MASK) { + /* this is a IN endpoint */ + x |= 1; + } + speed = libusb20_dev_get_speed(pdev); + + /* select a sensible buffer size */ + if (speed == LIBUSB20_SPEED_LOW) { + bufsize = 256; + } else if (speed == LIBUSB20_SPEED_FULL) { + bufsize = 4096; + } else { + bufsize = 16384; + } + + xfer = libusb20_tr_get_pointer(pdev, x); + + if (xfer == NULL) + return (xfer); + + err = libusb20_tr_open(xfer, bufsize, 1, ep_no); + if (err == LIBUSB20_ERROR_BUSY) { + /* already opened */ + return (xfer); + } else if (err) { + return (NULL); + } + /* success */ + return (xfer); +} + +usb_dev_handle * +usb_open(struct usb_device *dev) +{ + int err; + + err = libusb20_dev_open(dev->dev, 16 * 2); + if (err == LIBUSB20_ERROR_BUSY) { + /* + * Workaround buggy USB applications which open the USB + * device multiple times: + */ + return (dev->dev); + } + if (err) + return (NULL); + + return (dev->dev); +} + +int +usb_close(usb_dev_handle * dev) +{ + int err; + + err = libusb20_dev_close((void *)dev); + + if (err) + return (-1); + + return (0); +} + +int +usb_get_string(usb_dev_handle * dev, int index, + int langid, char *buf, size_t buflen) +{ + int err; + + err = libusb20_dev_req_string_sync((void *)dev, + index, langid, buf, buflen); + + if (err) + return (-1); + + return (0); +} + +int +usb_get_string_simple(usb_dev_handle * dev, int index, + char *buf, size_t buflen) +{ + int err; + + err = libusb20_dev_req_string_simple_sync((void *)dev, + index, buf, buflen); + + if (err) + return (-1); + + return (strlen(buf)); +} + +int +usb_get_descriptor_by_endpoint(usb_dev_handle * udev, int ep, uint8_t type, + uint8_t index, void *buf, int size) +{ + memset(buf, 0, size); + + return (usb_control_msg(udev, ep | USB_ENDPOINT_IN, + USB_REQ_GET_DESCRIPTOR, (type << 8) + index, 0, + buf, size, 1000)); +} + +int +usb_get_descriptor(usb_dev_handle * udev, uint8_t type, uint8_t index, + void *buf, int size) +{ + memset(buf, 0, size); + + return (usb_control_msg(udev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (type << 8) + index, 0, buf, size, 1000)); +} + +int +usb_parse_descriptor(uint8_t *source, char *description, void *dest) +{ + uint8_t *sp = source; + uint8_t *dp = dest; + uint16_t w; + uint32_t d; + char *cp; + + for (cp = description; *cp; cp++) { + switch (*cp) { + case 'b': /* 8-bit byte */ + *dp++ = *sp++; + break; + /* + * 16-bit word, convert from little endian to CPU + */ + case 'w': + w = (sp[1] << 8) | sp[0]; + sp += 2; + /* Align to word boundary */ + dp += ((dp - (uint8_t *)0) & 1); + *((uint16_t *)dp) = w; + dp += 2; + break; + /* + * 32-bit dword, convert from little endian to CPU + */ + case 'd': + d = (sp[3] << 24) | (sp[2] << 16) | + (sp[1] << 8) | sp[0]; + sp += 4; + /* Align to word boundary */ + dp += ((dp - (uint8_t *)0) & 1); + /* Align to double word boundary */ + dp += ((dp - (uint8_t *)0) & 2); + *((uint32_t *)dp) = d; + dp += 4; + break; + } + } + return (sp - source); +} + +static void +usb_parse_extra(struct usb_parse_state *ps, uint8_t **pptr, int *plen) +{ + void *ptr; + uint16_t len; + + ptr = ps->a.currextra->ptr; + len = ps->a.currextra->len; + + if (ps->preparse == 0) { + memcpy(ps->b.currextra, ptr, len); + *pptr = ps->b.currextra; + *plen = len; + } + ps->b.currextra += len; + return; +} + +static void +usb_parse_endpoint(struct usb_parse_state *ps) +{ + struct usb_endpoint_descriptor *bep; + struct libusb20_endpoint *aep; + + aep = ps->a.currep; + bep = ps->b.currep++; + + if (ps->preparse == 0) { + /* copy descriptor fields */ + bep->bLength = aep->desc.bLength; + bep->bDescriptorType = aep->desc.bDescriptorType; + bep->bEndpointAddress = aep->desc.bEndpointAddress; + bep->bmAttributes = aep->desc.bmAttributes; + bep->wMaxPacketSize = aep->desc.wMaxPacketSize; + bep->bInterval = aep->desc.bInterval; + bep->bRefresh = aep->desc.bRefresh; + bep->bSynchAddress = aep->desc.bSynchAddress; + } + ps->a.currextra = &aep->extra; + usb_parse_extra(ps, &bep->extra, &bep->extralen); + return; +} + +static void +usb_parse_iface_sub(struct usb_parse_state *ps) +{ + struct libusb20_interface *aifc; + struct usb_interface_descriptor *bifc; + uint8_t x; + + aifc = ps->a.currifc; + bifc = ps->b.currifc++; + + if (ps->preparse == 0) { + /* copy descriptor fields */ + bifc->bLength = aifc->desc.bLength; + bifc->bDescriptorType = aifc->desc.bDescriptorType; + bifc->bInterfaceNumber = aifc->desc.bInterfaceNumber; + bifc->bAlternateSetting = aifc->desc.bAlternateSetting; + bifc->bNumEndpoints = aifc->num_endpoints; + bifc->bInterfaceClass = aifc->desc.bInterfaceClass; + bifc->bInterfaceSubClass = aifc->desc.bInterfaceSubClass; + bifc->bInterfaceProtocol = aifc->desc.bInterfaceProtocol; + bifc->iInterface = aifc->desc.iInterface; + bifc->endpoint = ps->b.currep; + } + for (x = 0; x != aifc->num_endpoints; x++) { + ps->a.currep = aifc->endpoints + x; + usb_parse_endpoint(ps); + } + + ps->a.currextra = &aifc->extra; + usb_parse_extra(ps, &bifc->extra, &bifc->extralen); + return; +} + +static void +usb_parse_iface(struct usb_parse_state *ps) +{ + struct libusb20_interface *aifc; + struct usb_interface *bifc; + uint8_t x; + + aifc = ps->a.currifc; + bifc = ps->b.currifcw++; + + if (ps->preparse == 0) { + /* initialise interface wrapper */ + bifc->altsetting = ps->b.currifc; + bifc->num_altsetting = aifc->num_altsetting + 1; + } + usb_parse_iface_sub(ps); + + for (x = 0; x != aifc->num_altsetting; x++) { + ps->a.currifc = aifc->altsetting + x; + usb_parse_iface_sub(ps); + } + return; +} + +static void +usb_parse_config(struct usb_parse_state *ps) +{ + struct libusb20_config *acfg; + struct usb_config_descriptor *bcfg; + uint8_t x; + + acfg = ps->a.currcfg; + bcfg = ps->b.currcfg; + + if (ps->preparse == 0) { + /* initialise config wrapper */ + bcfg->bLength = acfg->desc.bLength; + bcfg->bDescriptorType = acfg->desc.bDescriptorType; + bcfg->wTotalLength = acfg->desc.wTotalLength; + bcfg->bNumInterfaces = acfg->num_interface; + bcfg->bConfigurationValue = acfg->desc.bConfigurationValue; + bcfg->iConfiguration = acfg->desc.iConfiguration; + bcfg->bmAttributes = acfg->desc.bmAttributes; + bcfg->MaxPower = acfg->desc.bMaxPower; + bcfg->interface = ps->b.currifcw; + } + for (x = 0; x != acfg->num_interface; x++) { + ps->a.currifc = acfg->interface + x; + usb_parse_iface(ps); + } + + ps->a.currextra = &acfg->extra; + usb_parse_extra(ps, &bcfg->extra, &bcfg->extralen); + return; +} + +int +usb_parse_configuration(struct usb_config_descriptor *config, + uint8_t *buffer) +{ + struct usb_parse_state ps; + uint8_t *ptr; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + + if ((buffer == NULL) || (config == NULL)) { + return (-1); + } + memset(&ps, 0, sizeof(ps)); + + ps.a.currcfg = libusb20_parse_config_desc(buffer); + ps.b.currcfg = config; + if (ps.a.currcfg == NULL) { + /* could not parse config or out of memory */ + return (-1); + } + /* do the pre-parse */ + ps.preparse = 1; + usb_parse_config(&ps); + + a = ((uint8_t *)(ps.b.currifcw) - ((uint8_t *)0)); + b = ((uint8_t *)(ps.b.currifc) - ((uint8_t *)0)); + c = ((uint8_t *)(ps.b.currep) - ((uint8_t *)0)); + d = ((uint8_t *)(ps.b.currextra) - ((uint8_t *)0)); + + /* allocate memory for our configuration */ + ptr = malloc(a + b + c + d); + + /* "currifcw" must be first, hence this pointer is freed */ + ps.b.currifcw = (void *)(ptr); + ps.b.currifc = (void *)(ptr + a); + ps.b.currep = (void *)(ptr + a + b); + ps.b.currextra = (void *)(ptr + a + b + c); + + /* generate a libusb v0.1 compatible structure */ + ps.preparse = 0; + usb_parse_config(&ps); + + /* free config structure */ + free(ps.a.currcfg); + + return (0); /* success */ +} + +void +usb_destroy_configuration(struct usb_device *dev) +{ + uint8_t c; + + if (dev->config == NULL) { + return; + } + for (c = 0; c != dev->descriptor.bNumConfigurations; c++) { + struct usb_config_descriptor *cf = &dev->config[c]; + + if (cf->interface != NULL) { + free(cf->interface); + cf->interface = NULL; + } + } + + free(dev->config); + dev->config = NULL; + return; +} + +void +usb_fetch_and_parse_descriptors(usb_dev_handle * udev) +{ + struct usb_device *dev; + struct libusb20_device *pdev; + uint8_t *ptr; + int error; + uint32_t size; + uint16_t len; + uint8_t x; + + if (udev == NULL) { + /* be NULL safe */ + return; + } + dev = usb_device(udev); + pdev = (void *)udev; + + if (dev->descriptor.bNumConfigurations == 0) { + /* invalid device */ + return; + } + size = dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor); + + dev->config = malloc(size); + if (dev->config == NULL) { + /* out of memory */ + return; + } + memset(dev->config, 0, size); + + for (x = 0; x != dev->descriptor.bNumConfigurations; x++) { + + error = (pdev->methods->get_config_desc_full) ( + pdev, &ptr, &len, x); + + if (error) { + usb_destroy_configuration(dev); + return; + } + usb_parse_configuration(dev->config + x, ptr); + + /* free config buffer */ + free(ptr); + } + return; +} + +static int +usb_std_io(usb_dev_handle * dev, int ep, char *bytes, int size, + int timeout, int is_intr) +{ + struct libusb20_transfer *xfer; + uint32_t temp; + uint32_t maxsize; + uint32_t actlen; + char *oldbytes; + + xfer = usb_get_transfer_by_ep_no(dev, ep); + if (xfer == NULL) + return (-1); + + if (libusb20_tr_pending(xfer)) { + /* there is already a transfer ongoing */ + return (-1); + } + maxsize = libusb20_tr_get_max_total_length(xfer); + oldbytes = bytes; + + /* + * We allow transferring zero bytes which is the same + * equivalent to a zero length USB packet. + */ + do { + + temp = size; + if (temp > maxsize) { + /* find maximum possible length */ + temp = maxsize; + } + if (is_intr) + libusb20_tr_setup_intr(xfer, bytes, temp, timeout); + else + libusb20_tr_setup_bulk(xfer, bytes, temp, timeout); + + libusb20_tr_start(xfer); + + while (1) { + + if (libusb20_dev_process((void *)dev) != 0) { + /* device detached */ + return (-1); + } + if (libusb20_tr_pending(xfer) == 0) { + /* transfer complete */ + break; + } + /* wait for USB event from kernel */ + libusb20_dev_wait_process((void *)dev, -1); + } + + if (libusb20_tr_get_status(xfer)) { + /* transfer error */ + return (-1); + } + actlen = libusb20_tr_get_actual_length(xfer); + + bytes += actlen; + size -= actlen; + + if (actlen != temp) { + /* short transfer */ + break; + } + } while (size > 0); + + return (bytes - oldbytes); +} + +int +usb_bulk_write(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep, bytes, size, timeout, 0)); +} + +int +usb_bulk_read(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep, bytes, size, timeout, 0)); +} + +int +usb_interrupt_write(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep, bytes, size, timeout, 1)); +} + +int +usb_interrupt_read(usb_dev_handle * dev, int ep, char *bytes, + int size, int timeout) +{ + return (usb_std_io(dev, ep, bytes, size, timeout, 1)); +} + +int +usb_control_msg(usb_dev_handle * dev, int requesttype, int request, + int value, int index, char *bytes, int size, int timeout) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int err; + uint16_t actlen; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + req.bmRequestType = requesttype; + req.bRequest = request; + req.wValue = value; + req.wIndex = index; + req.wLength = size; + + err = libusb20_dev_request_sync((void *)dev, &req, bytes, + &actlen, timeout, 0); + + if (err) + return (-1); + + return (actlen); +} + +int +usb_set_configuration(usb_dev_handle * dev, int configuration) +{ + int err; + + err = libusb20_dev_set_config_index((void *)dev, configuration); + + if (err) + return (-1); + + return (0); +} + +int +usb_claim_interface(usb_dev_handle * dev, int interface) +{ + int err; + + err = libusb20_dev_claim_interface((void *)dev, interface); + + if (err) + return (-1); + + return (0); +} + +int +usb_release_interface(usb_dev_handle * dev, int interface) +{ + int err; + + err = libusb20_dev_release_interface((void *)dev, interface); + + if (err) + return (-1); + + return (0); +} + +int +usb_set_altinterface(usb_dev_handle * dev, int alternate) +{ + int err; + uint8_t iface; + + iface = usb_get_first_claimed_interface(dev); + + err = libusb20_dev_set_alt_index((void *)dev, iface, alternate); + + if (err) + return (-1); + + return (0); +} + +int +usb_resetep(usb_dev_handle * dev, unsigned int ep) +{ + /* emulate an endpoint reset through clear-STALL */ + return (usb_clear_halt(dev, ep)); +} + +int +usb_clear_halt(usb_dev_handle * dev, unsigned int ep) +{ + struct libusb20_transfer *xfer; + + xfer = usb_get_transfer_by_ep_no(dev, ep); + if (xfer == NULL) + return (-1); + + libusb20_tr_clear_stall_sync(xfer); + + return (0); +} + +int +usb_reset(usb_dev_handle * dev) +{ + int err; + + err = libusb20_dev_reset((void *)dev); + + if (err) + return (-1); + + return (0); +} + +char * +usb_strerror(void) +{ + /* TODO */ + return ("Unknown error"); +} + +void +usb_init(void) +{ + /* nothing to do */ + return; +} + +void +usb_set_debug(int level) +{ + /* use kernel UGEN debugging if you need to see what is going on */ + return; +} + +int +usb_find_busses(void) +{ + usb_busses = &usb_global_bus; + return (0); +} + +int +usb_find_devices(void) +{ + struct libusb20_device *pdev; + struct usb_device *udev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + struct libusb20_backend *pold; + int err; + + /* cleanup after last device search */ + + pold = usb_backend; + + pdev = NULL; + while ((pdev = libusb20_be_device_foreach(pold, pdev))) { + if (!pdev->is_opened) { + /* + * if the device has not been opened we free the + * device data + */ + udev = pdev->priv01Data; + libusb20_be_dequeue_device(pold, pdev); + libusb20_dev_free(pdev); + if (udev != NULL) { + LIST_DEL(usb_global_bus.devices, udev); + free(udev); + } + pdev = NULL; /* restart search */ + } + } + + /* do a new backend device search */ + + usb_backend = libusb20_be_alloc_default(); + if (usb_backend == NULL) { + usb_backend = pold; /* restore */ + return (-1); + } + /* iterate all devices */ + + pdev = NULL; + while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) { + udev = malloc(sizeof(*udev)); + if (udev == NULL) + break; + + memset(udev, 0, sizeof(*udev)); + + udev->bus = &usb_global_bus; + + snprintf(udev->filename, sizeof(udev->filename), + "/dev/ugen%u.%u", + libusb20_dev_get_bus_number(pdev), + libusb20_dev_get_address(pdev)); + + ddesc = libusb20_dev_get_device_desc(pdev); + + udev->descriptor.bLength = sizeof(udev->descriptor); + udev->descriptor.bDescriptorType = ddesc->bDescriptorType; + udev->descriptor.bcdUSB = ddesc->bcdUSB; + udev->descriptor.bDeviceClass = ddesc->bDeviceClass; + udev->descriptor.bDeviceSubClass = ddesc->bDeviceSubClass; + udev->descriptor.bDeviceProtocol = ddesc->bDeviceProtocol; + udev->descriptor.bMaxPacketSize0 = ddesc->bMaxPacketSize0; + udev->descriptor.idVendor = ddesc->idVendor; + udev->descriptor.idProduct = ddesc->idProduct; + udev->descriptor.bcdDevice = ddesc->bcdDevice; + udev->descriptor.iManufacturer = ddesc->iManufacturer; + udev->descriptor.iProduct = ddesc->iProduct; + udev->descriptor.iSerialNumber = ddesc->iSerialNumber; + udev->descriptor.bNumConfigurations = + ddesc->bNumConfigurations; + if (udev->descriptor.bNumConfigurations > USB_MAXCONFIG) { + /* truncate number of configurations */ + udev->descriptor.bNumConfigurations = USB_MAXCONFIG; + } + /* link together the two structures */ + udev->dev = pdev; + pdev->priv01Data = udev; + + err = libusb20_dev_open(pdev, 0); + if (err == 0) { + /* XXX get all config descriptors by default */ + usb_fetch_and_parse_descriptors((void *)pdev); + libusb20_dev_close(pdev); + } + LIST_ADD(usb_global_bus.devices, udev); + } + + /* move old devices over to the new USB backend */ + + while ((pdev = libusb20_be_device_foreach(pold, pdev))) { + libusb20_be_dequeue_device(pold, pdev); + libusb20_be_enqueue_device(usb_backend, pdev); + } + + /* free old backend, if any */ + + libusb20_be_free(pold); + + return (0); /* success */ +} + +struct usb_device * +usb_device(usb_dev_handle * dev) +{ + struct libusb20_device *pdev; + + pdev = (void *)dev; + + return (pdev->priv01Data); +} + +struct usb_bus * +usb_get_busses(void) +{ + return (usb_busses); +} diff --git a/lib/libusb20/libusb20_compat01.h b/lib/libusb20/libusb20_compat01.h new file mode 100644 index 000000000000..333793d5e996 --- /dev/null +++ b/lib/libusb20/libusb20_compat01.h @@ -0,0 +1,310 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _LIBUSB20_COMPAT_01_H_ +#define _LIBUSB20_COMPAT_01_H_ + +#include +#include +#include +#include + +/* USB interface class codes */ + +#define USB_CLASS_PER_INTERFACE 0 +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_PTP 6 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* USB descriptor types */ + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_HUB 0x29 + +/* USB descriptor type sizes */ + +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* USB descriptor header */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* USB string descriptor */ +struct usb_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[1]; +}; + +/* USB HID descriptor */ +struct usb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + /* uint8_t bReportDescriptorType; */ + /* uint16_t wDescriptorLength; */ + /* ... */ +}; + +/* USB endpoint descriptor */ +#define USB_MAXENDPOINTS 32 +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; +#define USB_ENDPOINT_ADDRESS_MASK 0x0f +#define USB_ENDPOINT_DIR_MASK 0x80 + uint8_t bmAttributes; +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 +#define USB_ENDPOINT_TYPE_BULK 2 +#define USB_ENDPOINT_TYPE_INTERRUPT 3 + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +/* USB interface descriptor */ +#define USB_MAXINTERFACES 32 +struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + + struct usb_endpoint_descriptor *endpoint; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_MAXALTSETTING 128 /* Hard limit */ +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int num_altsetting; +}; + +/* USB configuration descriptor */ +#define USB_MAXCONFIG 8 +struct usb_config_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t MaxPower; + + struct usb_interface *interface; + + uint8_t *extra; /* Extra descriptors */ + int extralen; +}; + +/* USB device descriptor */ +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +}; + +/* USB setup packet */ +struct usb_ctrl_setup { + uint8_t bRequestType; +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + uint8_t bRequest; +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +/* Error codes */ +#define USB_ERROR_BEGIN 500000 + +/* Byte swapping */ +#define USB_LE16_TO_CPU(x) le16toh(x) + +/* Data types */ +struct usb_device; +struct usb_bus; + +/* + * To maintain compatibility with applications already built with libusb, + * we must only add entries to the end of this structure. NEVER delete or + * move members and only change types if you really know what you're doing. + */ +struct usb_device { + struct usb_device *next; + struct usb_device *prev; + + char filename[PATH_MAX + 1]; + + struct usb_bus *bus; + + struct usb_device_descriptor descriptor; + struct usb_config_descriptor *config; + + void *dev; + + uint8_t devnum; + + uint8_t num_children; + struct usb_device **children; +}; + +struct usb_bus { + struct usb_bus *next; + struct usb_bus *prev; + + char dirname[PATH_MAX + 1]; + + struct usb_device *devices; + uint32_t location; + + struct usb_device *root_dev; +}; + +struct usb_dev_handle; +typedef struct usb_dev_handle usb_dev_handle; + +/* Variables */ +extern struct usb_bus *usb_busses; + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* style */ + +#endif + +/* Function prototypes from "libusb20_compat01.c" */ + +usb_dev_handle *usb_open(struct usb_device *dev); +int usb_close(usb_dev_handle * dev); +int usb_get_string(usb_dev_handle * dev, int index, int langid, char *buf, size_t buflen); +int usb_get_string_simple(usb_dev_handle * dev, int index, char *buf, size_t buflen); +int usb_get_descriptor_by_endpoint(usb_dev_handle * udev, int ep, uint8_t type, uint8_t index, void *buf, int size); +int usb_get_descriptor(usb_dev_handle * udev, uint8_t type, uint8_t index, void *buf, int size); +int usb_parse_descriptor(uint8_t *source, char *description, void *dest); +int usb_parse_configuration(struct usb_config_descriptor *config, uint8_t *buffer); +void usb_destroy_configuration(struct usb_device *dev); +void usb_fetch_and_parse_descriptors(usb_dev_handle * udev); +int usb_bulk_write(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_bulk_read(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_interrupt_write(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_interrupt_read(usb_dev_handle * dev, int ep, char *bytes, int size, int timeout); +int usb_control_msg(usb_dev_handle * dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); +int usb_set_configuration(usb_dev_handle * dev, int configuration); +int usb_claim_interface(usb_dev_handle * dev, int interface); +int usb_release_interface(usb_dev_handle * dev, int interface); +int usb_set_altinterface(usb_dev_handle * dev, int alternate); +int usb_resetep(usb_dev_handle * dev, unsigned int ep); +int usb_clear_halt(usb_dev_handle * dev, unsigned int ep); +int usb_reset(usb_dev_handle * dev); +char *usb_strerror(void); +void usb_init(void); +void usb_set_debug(int level); +int usb_find_busses(void); +int usb_find_devices(void); +struct usb_device *usb_device(usb_dev_handle * dev); +struct usb_bus *usb_get_busses(void); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_COMPAT01_H_ */ diff --git a/lib/libusb20/libusb20_compat10.c b/lib/libusb20/libusb20_compat10.c new file mode 100644 index 000000000000..36244850fbda --- /dev/null +++ b/lib/libusb20/libusb20_compat10.c @@ -0,0 +1,29 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 file contains the emulation layer for LibUSB v1.0 from sourceforge. + */ diff --git a/lib/libusb20/libusb20_compat10.h b/lib/libusb20/libusb20_compat10.h new file mode 100644 index 000000000000..d98895fa25e8 --- /dev/null +++ b/lib/libusb20/libusb20_compat10.h @@ -0,0 +1,25 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ diff --git a/lib/libusb20/libusb20_desc.c b/lib/libusb20/libusb20_desc.c new file mode 100644 index 000000000000..fbbd96fc980f --- /dev/null +++ b/lib/libusb20/libusb20_desc.c @@ -0,0 +1,771 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +static const uint32_t libusb20_me_encode_empty[2]; /* dummy */ + +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC); +LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP); + +/*------------------------------------------------------------------------* + * libusb20_parse_config_desc + * + * Return values: + * NULL: Out of memory. + * Else: A valid config structure pointer which must be passed to "free()" + *------------------------------------------------------------------------*/ +struct libusb20_config * +libusb20_parse_config_desc(const void *config_desc) +{ + struct libusb20_config *lub_config; + struct libusb20_interface *lub_interface; + struct libusb20_interface *lub_alt_interface; + struct libusb20_interface *last_if; + struct libusb20_endpoint *lub_endpoint; + struct libusb20_endpoint *last_ep; + + struct libusb20_me_struct pcdesc; + const uint8_t *ptr; + uint32_t size; + uint16_t niface_no_alt; + uint16_t niface; + uint16_t nendpoint; + uint8_t iface_no; + + ptr = config_desc; + if (ptr[1] != LIBUSB20_DT_CONFIG) { + return (NULL); /* not config descriptor */ + } + /* + * The first "bInterfaceNumber" should never have the value 0xff. + * Then it is corrupt. + */ + niface_no_alt = 0; + nendpoint = 0; + niface = 0; + iface_no = 0 - 1; + ptr = NULL; + + /* get "wTotalLength" and setup "pcdesc" */ + pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0); + pcdesc.len = + ((uint8_t *)config_desc)[2] | + (((uint8_t *)config_desc)[3] << 8); + pcdesc.type = LIBUSB20_ME_IS_RAW; + + /* descriptor pre-scan */ + while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { + if (ptr[1] == LIBUSB20_DT_ENDPOINT) { + nendpoint++; + } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { + niface++; + /* check "bInterfaceNumber" */ + if (ptr[2] != iface_no) { + iface_no = ptr[2]; + niface_no_alt++; + } + } + } + + /* sanity checking */ + if (niface >= 256) { + return (NULL); /* corrupt */ + } + if (nendpoint >= 256) { + return (NULL); /* corrupt */ + } + size = sizeof(*lub_config) + + (niface * sizeof(*lub_interface)) + + (nendpoint * sizeof(*lub_endpoint)) + + pcdesc.len; + + lub_config = malloc(size); + if (lub_config == NULL) { + return (NULL); /* out of memory */ + } + lub_interface = (void *)(lub_config + 1); + lub_alt_interface = (void *)(lub_interface + niface_no_alt); + lub_endpoint = (void *)(lub_interface + niface); + + /* + * Make a copy of the config descriptor, so that the caller can free + * the inital config descriptor pointer! + */ + ptr = (void *)(lub_endpoint + nendpoint); + memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len); + pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0); + config_desc = LIBUSB20_ADD_BYTES(ptr, 0); + + /* init config structure */ + + ptr = config_desc; + + LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc); + + if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) { + /* ignore */ + } + lub_config->num_interface = 0; + lub_config->interface = lub_interface; + lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + lub_config->extra.len = -ptr[0]; + lub_config->extra.type = LIBUSB20_ME_IS_RAW; + + /* reset states */ + niface = 0; + iface_no = 0 - 1; + ptr = NULL; + lub_interface--; + lub_endpoint--; + last_if = NULL; + last_ep = NULL; + + /* descriptor pre-scan */ + while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { + if (ptr[1] == LIBUSB20_DT_ENDPOINT) { + if (last_if) { + lub_endpoint++; + last_ep = lub_endpoint; + last_if->num_endpoints++; + + LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc); + + if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) { + /* ignore */ + } + last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + last_ep->extra.len = 0; + last_ep->extra.type = LIBUSB20_ME_IS_RAW; + } else { + lub_config->extra.len += ptr[0]; + } + + } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { + if (ptr[2] != iface_no) { + /* new interface */ + iface_no = ptr[2]; + lub_interface++; + lub_config->num_interface++; + last_if = lub_interface; + niface++; + } else { + /* one more alternate setting */ + lub_interface->num_altsetting++; + last_if = lub_alt_interface; + lub_alt_interface++; + } + + LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc); + + if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) { + /* ignore */ + } + /* + * Sometimes USB devices have corrupt interface + * descriptors and we need to overwrite the provided + * interface number! + */ + last_if->desc.bInterfaceNumber = niface - 1; + last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + last_if->extra.len = 0; + last_if->extra.type = LIBUSB20_ME_IS_RAW; + last_if->endpoints = lub_endpoint + 1; + last_if->altsetting = lub_alt_interface; + last_if->num_altsetting = 0; + last_if->num_endpoints = 0; + last_ep = NULL; + } else { + /* unknown descriptor */ + if (last_if) { + if (last_ep) { + last_ep->extra.len += ptr[0]; + } else { + last_if->extra.len += ptr[0]; + } + } else { + lub_config->extra.len += ptr[0]; + } + } + } + return (lub_config); +} + +/*------------------------------------------------------------------------* + * libusb20_desc_foreach + * + * Safe traversal of USB descriptors. + * + * Return values: + * NULL: End of descriptors + * Else: Pointer to next descriptor + *------------------------------------------------------------------------*/ +const uint8_t * +libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, + const uint8_t *psubdesc) +{ + void *end; + + if (pdesc == NULL) { + return (NULL); + } + end = LIBUSB20_ADD_BYTES(pdesc->ptr, pdesc->len); + + if (psubdesc == NULL) { + psubdesc = LIBUSB20_ADD_BYTES(pdesc->ptr, 0); + } else { + psubdesc = LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]); + } + return (((((void *)psubdesc) >= ((void *)(pdesc->ptr))) && + (((void *)psubdesc) < end) && + (LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]) >= ((void *)(pdesc->ptr))) && + (LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]) <= end) && + (psubdesc[0] >= 3)) ? psubdesc : NULL); +} + +/*------------------------------------------------------------------------* + * libusb20_me_get_1 - safety wrapper to read out one byte + *------------------------------------------------------------------------*/ +uint8_t +libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset) +{ + if (offset < ie->len) { + return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset))); + } + return (0); +} + +/*------------------------------------------------------------------------* + * libusb20_me_get_2 - safety wrapper to read out one word + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset) +{ + return (libusb20_me_get_1(ie, offset) | + (libusb20_me_get_1(ie, offset + 1) << 8)); +} + +/*------------------------------------------------------------------------* + * libusb20_me_encode - encode a message structure + * + * Description of parameters: + * "len" - maximum length of output buffer + * "ptr" - pointer to output buffer. If NULL, no data will be written + * "pd" - source structure + * + * Return values: + * 0..65535 - Number of bytes used, limited by the "len" input parameter. + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_encode(void *ptr, uint16_t len, const void *pd) +{ + const uint8_t *pf; /* pointer to format data */ + uint8_t *buf; /* pointer to output buffer */ + + uint32_t pd_offset; /* decoded structure offset */ + uint16_t len_old; /* old length */ + uint16_t pd_count; /* decoded element count */ + uint8_t me; /* message element */ + + /* initialise */ + + len_old = len; + buf = ptr; + pd_offset = sizeof(void *); + pf = (*((struct libusb20_me_format **)pd))->format; + + /* scan */ + + while (1) { + + /* get information element */ + + me = (pf[0]) & LIBUSB20_ME_MASK; + pd_count = pf[1] | (pf[2] << 8); + pf += 3; + + /* encode the message element */ + + switch (me) { + case LIBUSB20_ME_INT8: + while (pd_count--) { + uint8_t temp; + + if (len < 1) /* overflow */ + goto done; + if (buf) { + temp = *((const uint8_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[0] = temp; + buf += 1; + } + pd_offset += 1; + len -= 1; + } + break; + + case LIBUSB20_ME_INT16: + pd_offset = -((-pd_offset) & ~1); /* align */ + while (pd_count--) { + uint16_t temp; + + if (len < 2) /* overflow */ + goto done; + + if (buf) { + temp = *((const uint16_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 2; + } + pd_offset += 2; + len -= 2; + } + break; + + case LIBUSB20_ME_INT32: + pd_offset = -((-pd_offset) & ~3); /* align */ + while (pd_count--) { + uint32_t temp; + + if (len < 4) /* overflow */ + goto done; + if (buf) { + temp = *((const uint32_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[3] = (temp >> 24) & 0xFF; + buf[2] = (temp >> 16) & 0xFF; + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 4; + } + pd_offset += 4; + len -= 4; + } + break; + + case LIBUSB20_ME_INT64: + pd_offset = -((-pd_offset) & ~7); /* align */ + while (pd_count--) { + uint64_t temp; + + if (len < 8) /* overflow */ + goto done; + if (buf) { + + temp = *((const uint64_t *) + LIBUSB20_ADD_BYTES(pd, pd_offset)); + buf[7] = (temp >> 56) & 0xFF; + buf[6] = (temp >> 48) & 0xFF; + buf[5] = (temp >> 40) & 0xFF; + buf[4] = (temp >> 32) & 0xFF; + buf[3] = (temp >> 24) & 0xFF; + buf[2] = (temp >> 16) & 0xFF; + buf[1] = (temp >> 8) & 0xFF; + buf[0] = temp & 0xFF; + buf += 8; + } + pd_offset += 8; + len -= 8; + } + break; + + case LIBUSB20_ME_STRUCT: + pd_offset = -((-pd_offset) & + ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ + while (pd_count--) { + void *src_ptr; + uint16_t src_len; + struct libusb20_me_struct *ps; + + ps = LIBUSB20_ADD_BYTES(pd, pd_offset); + + switch (ps->type) { + case LIBUSB20_ME_IS_RAW: + src_len = ps->len; + src_ptr = ps->ptr; + break; + + case LIBUSB20_ME_IS_ENCODED: + if (ps->len == 0) { + /* + * Length is encoded + * in the data itself + * and should be + * correct: + */ + ps->len = 0 - 1; + } + src_len = libusb20_me_get_1(pd, 0); + src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1); + if (src_len == 0xFF) { + /* length is escaped */ + src_len = libusb20_me_get_2(pd, 1); + src_ptr = + LIBUSB20_ADD_BYTES(ps->ptr, 3); + } + break; + + case LIBUSB20_ME_IS_DECODED: + /* reserve 3 length bytes */ + src_len = libusb20_me_encode(NULL, + 0 - 1 - 3, ps->ptr); + src_ptr = NULL; + break; + + default: /* empty structure */ + src_len = 0; + src_ptr = NULL; + break; + } + + if (src_len > 0xFE) { + if (src_len > (uint16_t)(0 - 1 - 3)) + /* overflow */ + goto done; + + if (len < (src_len + 3)) + /* overflow */ + goto done; + + if (buf) { + buf[0] = 0xFF; + buf[1] = (src_len & 0xFF); + buf[2] = (src_len >> 8) & 0xFF; + buf += 3; + } + len -= (src_len + 3); + } else { + if (len < (src_len + 1)) + /* overflow */ + goto done; + + if (buf) { + buf[0] = (src_len & 0xFF); + buf += 1; + } + len -= (src_len + 1); + } + + /* check for buffer and non-zero length */ + + if (buf && src_len) { + if (ps->type == LIBUSB20_ME_IS_DECODED) { + /* + * Repeat encode + * procedure - we have + * room for the + * complete structure: + */ + uint16_t dummy; + + dummy = libusb20_me_encode(buf, + 0 - 1 - 3, ps->ptr); + } else { + bcopy(src_ptr, buf, src_len); + } + buf += src_len; + } + pd_offset += sizeof(struct libusb20_me_struct); + } + break; + + default: + goto done; + } + } +done: + return (len_old - len); +} + +/*------------------------------------------------------------------------* + * libusb20_me_decode - decode a message into a decoded structure + * + * Description of parameters: + * "ptr" - message pointer + * "len" - message length + * "pd" - pointer to decoded structure + * + * Returns: + * "0..65535" - number of bytes decoded, limited by "len" + *------------------------------------------------------------------------*/ +uint16_t +libusb20_me_decode(const void *ptr, uint16_t len, void *pd) +{ + const uint8_t *pf; /* pointer to format data */ + const uint8_t *buf; /* pointer to input buffer */ + + uint32_t pd_offset; /* decoded structure offset */ + uint16_t len_old; /* old length */ + uint16_t pd_count; /* decoded element count */ + uint8_t me; /* message element */ + + /* initialise */ + + len_old = len; + buf = ptr; + pd_offset = sizeof(void *); + pf = (*((struct libusb20_me_format **)pd))->format; + + /* scan */ + + while (1) { + + /* get information element */ + + me = (pf[0]) & LIBUSB20_ME_MASK; + pd_count = pf[1] | (pf[2] << 8); + pf += 3; + + /* decode the message element by type */ + + switch (me) { + case LIBUSB20_ME_INT8: + while (pd_count--) { + uint8_t temp; + + if (len < 1) { + len = 0; + temp = 0; + } else { + len -= 1; + temp = buf[0]; + buf++; + } + *((uint8_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 1; + } + break; + + case LIBUSB20_ME_INT16: + pd_offset = -((-pd_offset) & ~1); /* align */ + while (pd_count--) { + uint16_t temp; + + if (len < 2) { + len = 0; + temp = 0; + } else { + len -= 2; + temp = buf[1] << 8; + temp |= buf[0]; + buf += 2; + } + *((uint16_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 2; + } + break; + + case LIBUSB20_ME_INT32: + pd_offset = -((-pd_offset) & ~3); /* align */ + while (pd_count--) { + uint32_t temp; + + if (len < 4) { + len = 0; + temp = 0; + } else { + len -= 4; + temp = buf[3] << 24; + temp |= buf[2] << 16; + temp |= buf[1] << 8; + temp |= buf[0]; + buf += 4; + } + + *((uint32_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 4; + } + break; + + case LIBUSB20_ME_INT64: + pd_offset = -((-pd_offset) & ~7); /* align */ + while (pd_count--) { + uint64_t temp; + + if (len < 8) { + len = 0; + temp = 0; + } else { + len -= 8; + temp = ((uint64_t)buf[7]) << 56; + temp |= ((uint64_t)buf[6]) << 48; + temp |= ((uint64_t)buf[5]) << 40; + temp |= ((uint64_t)buf[4]) << 32; + temp |= buf[3] << 24; + temp |= buf[2] << 16; + temp |= buf[1] << 8; + temp |= buf[0]; + buf += 8; + } + + *((uint64_t *)LIBUSB20_ADD_BYTES(pd, + pd_offset)) = temp; + pd_offset += 8; + } + break; + + case LIBUSB20_ME_STRUCT: + pd_offset = -((-pd_offset) & + ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ + while (pd_count--) { + uint16_t temp; + uint16_t dummy; + struct libusb20_me_struct *ps; + + ps = LIBUSB20_ADD_BYTES(pd, pd_offset); + + if (ps->type == LIBUSB20_ME_IS_ENCODED) { + /* + * Pre-store a de-constified + * pointer to the raw + * structure: + */ + ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); + + /* + * Get the correct number of + * length bytes: + */ + if (len != 0) { + if (buf[0] == 0xFF) { + ps->len = 3; + } else { + ps->len = 1; + } + } else { + ps->len = 0; + } + } + /* get the structure length */ + + if (len != 0) { + if (buf[0] == 0xFF) { + if (len < 3) { + len = 0; + temp = 0; + } else { + len -= 3; + temp = buf[1] | + (buf[2] << 8); + buf += 3; + } + } else { + len -= 1; + temp = buf[0]; + buf += 1; + } + } else { + len = 0; + temp = 0; + } + /* check for invalid length */ + + if (temp > len) { + len = 0; + temp = 0; + } + /* check wanted structure type */ + + switch (ps->type) { + case LIBUSB20_ME_IS_ENCODED: + /* check for zero length */ + if (temp == 0) { + /* + * The pointer must + * be valid: + */ + ps->ptr = LIBUSB20_ADD_BYTES( + libusb20_me_encode_empty, 0); + ps->len = 1; + } else { + ps->len += temp; + } + break; + + case LIBUSB20_ME_IS_RAW: + /* update length and pointer */ + ps->len = temp; + ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); + break; + + case LIBUSB20_ME_IS_EMPTY: + case LIBUSB20_ME_IS_DECODED: + /* check for non-zero length */ + if (temp != 0) { + /* update type */ + ps->type = LIBUSB20_ME_IS_DECODED; + ps->len = 0; + /* + * Recursivly decode + * the next structure + */ + dummy = libusb20_me_decode(buf, + temp, ps->ptr); + } else { + /* update type */ + ps->type = LIBUSB20_ME_IS_EMPTY; + ps->len = 0; + } + break; + + default: + /* + * nothing to do - should + * not happen + */ + ps->ptr = NULL; + ps->len = 0; + break; + } + buf += temp; + len -= temp; + pd_offset += sizeof(struct libusb20_me_struct); + } + break; + + default: + goto done; + } + } +done: + return (len_old - len); +} diff --git a/lib/libusb20/libusb20_desc.h b/lib/libusb20/libusb20_desc.h new file mode 100644 index 000000000000..76bcca8fd991 --- /dev/null +++ b/lib/libusb20/libusb20_desc.h @@ -0,0 +1,534 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2007-2008 Daniel Drake. All rights reserved. + * Copyright (c) 2001 Johannes Erdfelt. 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. + */ + +/* + * NOTE: This file contains the definition of some standard USB + * structures. All structures which name ends by *DECODED use host byte + * order. + */ + +/* + * NOTE: This file uses a lot of macros. If you want to see what the + * macros become when they are expanded then run the following + * commands from your shell: + * + * cpp libusb20_desc.h > temp.h + * indent temp.h + * less temp.h + */ + +#ifndef _LIBUSB20_DESC_H_ +#define _LIBUSB20_DESC_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +}; /* style */ + +#endif +/* basic macros */ + +#define LIBUSB20__NOT(...) __VA_ARGS__ +#define LIBUSB20_NOT(arg) LIBUSB20__NOT(LIBUSB20_YES arg(() LIBUSB20_NO)) +#define LIBUSB20_YES(...) __VA_ARGS__ +#define LIBUSB20_NO(...) +#define LIBUSB20_END(...) __VA_ARGS__ +#define LIBUSB20_MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define LIBUSB20_MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define LIBUSB20_ADD_BYTES(ptr,off) \ + ((void *)(((const uint8_t *)(ptr)) + (off))) + +/* basic message elements */ +enum { + LIBUSB20_ME_INT8, + LIBUSB20_ME_INT16, + LIBUSB20_ME_INT32, + LIBUSB20_ME_INT64, + LIBUSB20_ME_STRUCT, + LIBUSB20_ME_MAX, /* used to indicate end */ +}; + +/* basic message element modifiers */ +enum { + LIBUSB20_ME_IS_UNSIGNED = 0x00, + LIBUSB20_ME_IS_SIGNED = 0x80, + LIBUSB20_ME_MASK = 0x7F, +}; + +enum { + LIBUSB20_ME_IS_RAW, /* structure excludes length field + * (hardcoded value) */ + LIBUSB20_ME_IS_ENCODED, /* structure includes length field */ + LIBUSB20_ME_IS_EMPTY, /* no structure */ + LIBUSB20_ME_IS_DECODED, /* structure is recursive */ +}; + +/* basic helper structures and macros */ + +#define LIBUSB20_ME_STRUCT_ALIGN sizeof(void *) + +struct libusb20_me_struct { + void *ptr; /* data pointer */ + uint16_t len; /* defaults to zero */ + uint16_t type; /* defaults to LIBUSB20_ME_IS_EMPTY */ +} __aligned(LIBUSB20_ME_STRUCT_ALIGN); + +struct libusb20_me_format { + const uint8_t *format; /* always set */ + const char *desc; /* optionally set */ + const char *fields; /* optionally set */ +}; + +#define LIBUSB20_ME_STRUCT(n, field, arg, ismeta) \ + ismeta ( LIBUSB20_ME_STRUCT, 1, 0, ) \ + LIBUSB20_NOT(ismeta) ( struct libusb20_me_struct field; ) + +#define LIBUSB20_ME_STRUCT_ARRAY(n, field, arg, ismeta) \ + ismeta ( LIBUSB20_ME_STRUCT , (arg) & 0xFF, \ + ((arg) / 0x100) & 0xFF, ) \ + LIBUSB20_NOT(ismeta) ( struct libusb20_me_struct field [arg]; ) + +#define LIBUSB20_ME_INTEGER(n, field, ismeta, un, u, bits, a, size) \ + ismeta ( LIBUSB20_ME_INT##bits | \ + LIBUSB20_ME_IS_##un##SIGNED , \ + (size) & 0xFF, ((size) / 0x100) & 0xFF, ) \ + LIBUSB20_NOT(ismeta) ( u##int##bits##_t \ + __aligned((bits) / 8) field a; ) + +#define LIBUSB20_ME_UINT8_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 8, , 1) + +#define LIBUSB20_ME_UINT8_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 8, [arg], arg) + +#define LIBUSB20_ME_SINT8_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 8, , 1) + +#define LIBUSB20_ME_SINT8_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 8, [arg], arg) + +#define LIBUSB20_ME_UINT16_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 16, , 1) + +#define LIBUSB20_ME_UINT16_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 16, [arg], arg) + +#define LIBUSB20_ME_SINT16_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 16, , 1) + +#define LIBUSB20_ME_SINT16_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 16, [arg], arg) + +#define LIBUSB20_ME_UINT32_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 32, , 1) + +#define LIBUSB20_ME_UINT32_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 32, [arg], arg) + +#define LIBUSB20_ME_SINT32_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 32, , 1) + +#define LIBUSB20_ME_SINT32_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 32, [arg], arg) + +#define LIBUSB20_ME_UINT64_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 64, , 1) + +#define LIBUSB20_ME_UINT64_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta, UN, u, 64, [arg], arg) + +#define LIBUSB20_ME_SINT64_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 64, , 1) + +#define LIBUSB20_ME_SINT64_ARRAY_T(n, field, arg, ismeta) \ + LIBUSB20_ME_INTEGER(n, field, ismeta,,, 64, [arg], arg) + +#define LIBUSB20_MAKE_DECODED_FIELD(n, type, field, arg) \ + LIBUSB20_ME_##type (n, field, arg, LIBUSB20_NO) + +#define LIBUSB20_MAKE_STRUCT(name) \ + extern const struct libusb20_me_format \ + name##_FORMAT[1]; \ + struct name##_DECODED { \ + const struct libusb20_me_format *name##_FORMAT; \ + name (LIBUSB20_MAKE_DECODED_FIELD,) \ + } + +#define LIBUSB20_MAKE_STRUCT_FORMAT(name) \ + const struct libusb20_me_format \ + name##_FORMAT[1] = {{ \ + .format = LIBUSB20_MAKE_FORMAT(name), \ + .desc = #name, \ + .fields = NULL, \ + }} + +#define LIBUSB20_MAKE_FORMAT_SUB(n, type, field, arg) \ + LIBUSB20_ME_##type (n, field, arg, LIBUSB20_YES) + +#define LIBUSB20_MAKE_FORMAT(what) (const uint8_t []) \ + { what (LIBUSB20_MAKE_FORMAT_SUB, ) LIBUSB20_ME_MAX, 0, 0 } + +#define LIBUSB20_INIT(what, ptr) do { \ + memset(ptr, 0, sizeof(*(ptr))); \ + (ptr)->what##_FORMAT = what##_FORMAT; \ +} while (0) + +#define LIBUSB20_DEVICE_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT16_T, bcdUSB, ) \ + m(n, UINT8_T, bDeviceClass, ) \ + m(n, UINT8_T, bDeviceSubClass, ) \ + m(n, UINT8_T, bDeviceProtocol, ) \ + m(n, UINT8_T, bMaxPacketSize0, ) \ + m(n, UINT16_T, idVendor, ) \ + m(n, UINT16_T, idProduct, ) \ + m(n, UINT16_T, bcdDevice, ) \ + m(n, UINT8_T, iManufacturer, ) \ + m(n, UINT8_T, iProduct, ) \ + m(n, UINT8_T, iSerialNumber, ) \ + m(n, UINT8_T, bNumConfigurations, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_DEVICE_DESC); + +#define LIBUSB20_ENDPOINT_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bEndpointAddress, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT16_T, wMaxPacketSize, ) \ + m(n, UINT8_T, bInterval, ) \ + m(n, UINT8_T, bRefresh, ) \ + m(n, UINT8_T, bSynchAddress, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_ENDPOINT_DESC); + +#define LIBUSB20_INTERFACE_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT8_T, bInterfaceNumber, ) \ + m(n, UINT8_T, bAlternateSetting, ) \ + m(n, UINT8_T, bNumEndpoints, ) \ + m(n, UINT8_T, bInterfaceClass, ) \ + m(n, UINT8_T, bInterfaceSubClass, ) \ + m(n, UINT8_T, bInterfaceProtocol, ) \ + m(n, UINT8_T, iInterface, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_INTERFACE_DESC); + +#define LIBUSB20_CONFIG_DESC(m,n) \ + m(n, UINT8_T, bLength, ) \ + m(n, UINT8_T, bDescriptorType, ) \ + m(n, UINT16_T, wTotalLength, ) \ + m(n, UINT8_T, bNumInterfaces, ) \ + m(n, UINT8_T, bConfigurationValue, ) \ + m(n, UINT8_T, iConfiguration, ) \ + m(n, UINT8_T, bmAttributes, ) \ + m(n, UINT8_T, bMaxPower, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_CONFIG_DESC); + +#define LIBUSB20_CONTROL_SETUP(m,n) \ + m(n, UINT8_T, bmRequestType, ) \ + m(n, UINT8_T, bRequest, ) \ + m(n, UINT16_T, wValue, ) \ + m(n, UINT16_T, wIndex, ) \ + m(n, UINT16_T, wLength, ) \ + +LIBUSB20_MAKE_STRUCT(LIBUSB20_CONTROL_SETUP); + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb20_class_code { + /** In the context of a \ref LIBUSB20_DEVICE_DESC "device + * descriptor", this bDeviceClass value indicates that each + * interface specifies its own class information and all + * interfaces operate independently. + */ + LIBUSB20_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB20_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB20_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB20_CLASS_HID = 3, + + /** Printer dclass */ + LIBUSB20_CLASS_PRINTER = 7, + + /** Picture transfer protocol class */ + LIBUSB20_CLASS_PTP = 6, + + /** Mass storage class */ + LIBUSB20_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB20_CLASS_HUB = 9, + + /** Data class */ + LIBUSB20_CLASS_DATA = 10, + + /** Class is vendor-specific */ + LIBUSB20_CLASS_VENDOR_SPEC = 0xff, +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb20_descriptor_type { + /** Device descriptor. See LIBUSB20_DEVICE_DESC. */ + LIBUSB20_DT_DEVICE = 0x01, + + /** Configuration descriptor. See LIBUSB20_CONFIG_DESC. */ + LIBUSB20_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB20_DT_STRING = 0x03, + + /** Interface descriptor. See LIBUSB20_INTERFACE_DESC. */ + LIBUSB20_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See LIBUSB20_ENDPOINT_DESC. */ + LIBUSB20_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB20_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB20_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB20_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB20_DT_HUB = 0x29, +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB20_DT_DEVICE_SIZE 18 +#define LIBUSB20_DT_CONFIG_SIZE 9 +#define LIBUSB20_DT_INTERFACE_SIZE 9 +#define LIBUSB20_DT_ENDPOINT_SIZE 7 +#define LIBUSB20_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB20_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB20_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB20_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref LIBUSB20_ENDPOINT_DESC::bEndpointAddress "endpoint address" scheme. + */ +enum libusb20_endpoint_direction { + /** In: device-to-host */ + LIBUSB20_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB20_ENDPOINT_OUT = 0x00, +}; + +#define LIBUSB20_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "endpoint attributes" field. + */ +enum libusb20_transfer_type { + /** Control endpoint */ + LIBUSB20_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB20_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB20_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB20_TRANSFER_TYPE_INTERRUPT = 3, +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb20_standard_request { + /** Request status of the specific recipient */ + LIBUSB20_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB20_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB20_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB20_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB20_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB20_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB20_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB20_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified + * interface */ + LIBUSB20_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB20_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB20_REQUEST_SYNCH_FRAME = 0x0C, +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb20_control_setup::bmRequestType "bmRequestType" field in + * control transfers. */ +enum libusb20_request_type { + /** Standard */ + LIBUSB20_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB20_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB20_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB20_REQUEST_TYPE_RESERVED = (0x03 << 5), +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb20_control_setup::bmRequestType "bmRequestType" field in + * control transfers. Values 4 through 31 are reserved. */ +enum libusb20_request_recipient { + /** Device */ + LIBUSB20_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB20_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB20_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB20_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB20_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 + * of the \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "bmAttributes" + * field in LIBUSB20_ENDPOINT_DESC. + */ +enum libusb20_iso_sync_type { + /** No synchronization */ + LIBUSB20_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB20_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB20_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB20_ISO_SYNC_TYPE_SYNC = 3, +}; + +#define LIBUSB20_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref LIBUSB20_ENDPOINT_DESC::bmAttributes "bmAttributes" field in + * LIBUSB20_ENDPOINT_DESC. + */ +enum libusb20_iso_usage_type { + /** Data endpoint */ + LIBUSB20_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB20_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB20_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +struct libusb20_endpoint { + struct LIBUSB20_ENDPOINT_DESC_DECODED desc; + struct libusb20_me_struct extra; +} __aligned(sizeof(void *)); + +struct libusb20_interface { + struct LIBUSB20_INTERFACE_DESC_DECODED desc; + struct libusb20_me_struct extra; + struct libusb20_interface *altsetting; + struct libusb20_endpoint *endpoints; + uint8_t num_altsetting; + uint8_t num_endpoints; +} __aligned(sizeof(void *)); + +struct libusb20_config { + struct LIBUSB20_CONFIG_DESC_DECODED desc; + struct libusb20_me_struct extra; + struct libusb20_interface *interface; + uint8_t num_interface; +} __aligned(sizeof(void *)); + +uint8_t libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset); +uint16_t libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset); +uint16_t libusb20_me_encode(void *ptr, uint16_t len, const void *pd); +uint16_t libusb20_me_decode(const void *ptr, uint16_t len, void *pd); +const uint8_t *libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, const uint8_t *psubdesc); +struct libusb20_config *libusb20_parse_config_desc(const void *config_desc); + +#if 0 +{ /* style */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* _LIBUSB20_DESC_H_ */ diff --git a/lib/libusb20/libusb20_int.h b/lib/libusb20/libusb20_int.h new file mode 100644 index 000000000000..6c849b9396b5 --- /dev/null +++ b/lib/libusb20/libusb20_int.h @@ -0,0 +1,252 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 file describes internal structures. + */ + +#ifndef _LIBUSB20_INT_H_ +#define _LIBUSB20_INT_H_ + +struct libusb20_device; +struct libusb20_backend; +struct libusb20_transfer; +struct libusb20_quirk; + +union libusb20_session_data { + unsigned long session_data; + struct timespec tv; + uint32_t plugtime; +}; + +/* USB backend specific */ +typedef const char *(libusb20_get_backend_name_t)(void); +typedef int (libusb20_root_get_dev_quirk_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +typedef int (libusb20_root_get_quirk_name_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); +typedef int (libusb20_root_add_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +typedef int (libusb20_root_remove_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); +typedef int (libusb20_bus_get_owner_t)(struct libusb20_backend *pbe, uint8_t bus, uid_t *user, gid_t *group); +typedef int (libusb20_bus_get_perm_t)(struct libusb20_backend *pbe, uint8_t bus, mode_t *mode); +typedef int (libusb20_bus_set_owner_t)(struct libusb20_backend *pbe, uint8_t bus, uid_t user, gid_t group); +typedef int (libusb20_bus_set_perm_t)(struct libusb20_backend *pbe, uint8_t bus, mode_t mode); +typedef int (libusb20_close_device_t)(struct libusb20_device *pdev); +typedef int (libusb20_dev_get_iface_owner_t)(struct libusb20_device *pdev, uint8_t iface_index, uid_t *user, gid_t *group); +typedef int (libusb20_dev_get_iface_perm_t)(struct libusb20_device *pdev, uint8_t iface_index, mode_t *mode); +typedef int (libusb20_dev_get_owner_t)(struct libusb20_device *pdev, uid_t *user, gid_t *group); +typedef int (libusb20_dev_get_perm_t)(struct libusb20_device *pdev, mode_t *mode); +typedef int (libusb20_dev_set_iface_owner_t)(struct libusb20_device *pdev, uint8_t iface_index, uid_t user, gid_t group); +typedef int (libusb20_dev_set_iface_perm_t)(struct libusb20_device *pdev, uint8_t iface_index, mode_t mode); +typedef int (libusb20_dev_set_owner_t)(struct libusb20_device *pdev, uid_t user, gid_t group); +typedef int (libusb20_dev_set_perm_t)(struct libusb20_device *pdev, mode_t mode); +typedef int (libusb20_init_backend_t)(struct libusb20_backend *pbe); +typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max); +typedef int (libusb20_root_get_owner_t)(struct libusb20_backend *pbe, uid_t *user, gid_t *group); +typedef int (libusb20_root_get_perm_t)(struct libusb20_backend *pbe, mode_t *mode); +typedef int (libusb20_root_set_owner_t)(struct libusb20_backend *pbe, uid_t user, gid_t group); +typedef int (libusb20_root_set_perm_t)(struct libusb20_backend *pbe, mode_t mode); +typedef void (libusb20_exit_backend_t)(struct libusb20_backend *pbe); + +#define LIBUSB20_DEFINE(n,field) \ + libusb20_##field##_t *field; + +#define LIBUSB20_DECLARE(n,field) \ + /* .field = */ n##_##field, + +#define LIBUSB20_BACKEND(m,n) \ + /* description of this backend */ \ + m(n, get_backend_name) \ + /* optional backend methods */ \ + m(n, init_backend) \ + m(n, exit_backend) \ + m(n, bus_set_owner) \ + m(n, bus_get_owner) \ + m(n, bus_set_perm) \ + m(n, bus_get_perm) \ + m(n, dev_get_iface_owner) \ + m(n, dev_get_iface_perm) \ + m(n, dev_get_owner) \ + m(n, dev_get_perm) \ + m(n, dev_set_iface_owner) \ + m(n, dev_set_iface_perm) \ + m(n, dev_set_owner) \ + m(n, dev_set_perm) \ + m(n, root_get_dev_quirk) \ + m(n, root_get_quirk_name) \ + m(n, root_add_dev_quirk) \ + m(n, root_remove_dev_quirk) \ + m(n, root_set_owner) \ + m(n, root_get_owner) \ + m(n, root_set_perm) \ + m(n, root_get_perm) \ + /* mandatory device methods */ \ + m(n, open_device) \ + m(n, close_device) \ + +struct libusb20_backend_methods { + LIBUSB20_BACKEND(LIBUSB20_DEFINE,) +}; + +/* USB dummy methods */ +typedef int (libusb20_dummy_int_t)(void); +typedef void (libusb20_dummy_void_t)(void); + +/* USB device specific */ +typedef int (libusb20_claim_interface_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_detach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_do_request_sync_t)(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); +typedef int (libusb20_get_config_desc_full_t)(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t index); +typedef int (libusb20_get_config_index_t)(struct libusb20_device *pdev, uint8_t *pindex); +typedef int (libusb20_kernel_driver_active_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_process_t)(struct libusb20_device *pdev); +typedef int (libusb20_release_interface_t)(struct libusb20_device *pdev, uint8_t iface_index); +typedef int (libusb20_reset_device_t)(struct libusb20_device *pdev); +typedef int (libusb20_set_power_mode_t)(struct libusb20_device *pdev, uint8_t power_mode); +typedef int (libusb20_get_power_mode_t)(struct libusb20_device *pdev, uint8_t *power_mode); +typedef int (libusb20_set_alt_index_t)(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); +typedef int (libusb20_set_config_index_t)(struct libusb20_device *pdev, uint8_t index); + +/* USB transfer specific */ +typedef int (libusb20_tr_open_t)(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no); +typedef int (libusb20_tr_close_t)(struct libusb20_transfer *xfer); +typedef int (libusb20_tr_clear_stall_sync_t)(struct libusb20_transfer *xfer); +typedef void (libusb20_tr_submit_t)(struct libusb20_transfer *xfer); +typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer); + +#define LIBUSB20_DEVICE(m,n) \ + m(n, claim_interface) \ + m(n, detach_kernel_driver) \ + m(n, do_request_sync) \ + m(n, get_config_desc_full) \ + m(n, get_config_index) \ + m(n, kernel_driver_active) \ + m(n, process) \ + m(n, release_interface) \ + m(n, reset_device) \ + m(n, set_power_mode) \ + m(n, get_power_mode) \ + m(n, set_alt_index) \ + m(n, set_config_index) \ + m(n, tr_cancel_async) \ + m(n, tr_clear_stall_sync) \ + m(n, tr_close) \ + m(n, tr_open) \ + m(n, tr_submit) \ + +struct libusb20_device_methods { + LIBUSB20_DEVICE(LIBUSB20_DEFINE,) +}; + +struct libusb20_backend { + TAILQ_HEAD(, libusb20_device) usb_devs; + const struct libusb20_backend_methods *methods; +}; + +struct libusb20_transfer { + struct libusb20_device *pdev; /* the USB device we belong to */ + libusb20_tr_callback_t *callback; + void *priv_sc0; /* private client data */ + void *priv_sc1; /* private client data */ + /* + * Pointer to a list of buffer pointers: + */ + void **ppBuffer; + /* + * Pointer to frame lengths, which are updated to actual length + * after the USB transfer completes: + */ + uint32_t *pLength; + uint32_t maxTotalLength; + uint32_t maxFrames; /* total number of frames */ + uint32_t nFrames; /* total number of frames */ + uint32_t aFrames; /* actual number of frames */ + uint32_t timeout; + /* isochronous completion time in milliseconds */ + uint16_t timeComplete; + uint16_t trIndex; + uint16_t maxPacketLen; + uint8_t flags; /* see LIBUSB20_TRANSFER_XXX */ + uint8_t status; /* see LIBUSB20_TRANSFER_XXX */ + uint8_t is_opened; + uint8_t is_pending; + uint8_t is_cancel; + uint8_t is_draining; + uint8_t is_restart; +}; + +struct libusb20_device { + + /* device descriptor */ + struct LIBUSB20_DEVICE_DESC_DECODED ddesc; + + /* device timestamp */ + union libusb20_session_data session_data; + + /* our device entry */ + TAILQ_ENTRY(libusb20_device) dev_entry; + + /* device methods */ + const struct libusb20_device_methods *methods; + + /* backend methods */ + const struct libusb20_backend_methods *beMethods; + + /* list of USB transfers */ + struct libusb20_transfer *pTransfer; + + /* private backend data */ + void *privBeData; + + /* libUSB v0.1 compat data */ + void *priv01Data; + + /* claimed interfaces */ + uint32_t claimed_interfaces; + + /* device file handle */ + int file; + + /* device file handle (control transfers only) */ + int file_ctrl; + + /* debugging level */ + int debug; + + /* number of USB transfers */ + uint16_t nTransfer; + + uint8_t bus_number; + uint8_t device_address; + uint8_t usb_mode; + uint8_t usb_speed; + uint8_t is_opened; + + char usb_desc[96]; +}; + +extern const struct libusb20_backend_methods libusb20_ugen20_backend; +extern const struct libusb20_backend_methods libusb20_linux_backend; + +#endif /* _LIBUSB20_INT_H_ */ diff --git a/lib/libusb20/libusb20_ugen20.c b/lib/libusb20/libusb20_ugen20.c new file mode 100644 index 000000000000..ffbd861de5c2 --- /dev/null +++ b/lib/libusb20/libusb20_ugen20.c @@ -0,0 +1,1077 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" + +#include +#include +#include +#include +#include + +static libusb20_init_backend_t ugen20_init_backend; +static libusb20_open_device_t ugen20_open_device; +static libusb20_close_device_t ugen20_close_device; +static libusb20_get_backend_name_t ugen20_get_backend_name; +static libusb20_exit_backend_t ugen20_exit_backend; +static libusb20_bus_set_owner_t ugen20_bus_set_owner; +static libusb20_bus_get_owner_t ugen20_bus_get_owner; +static libusb20_bus_set_perm_t ugen20_bus_set_perm; +static libusb20_bus_get_perm_t ugen20_bus_get_perm; +static libusb20_dev_get_iface_owner_t ugen20_dev_get_iface_owner; +static libusb20_dev_get_iface_perm_t ugen20_dev_get_iface_perm; +static libusb20_dev_get_owner_t ugen20_dev_get_owner; +static libusb20_dev_get_perm_t ugen20_dev_get_perm; +static libusb20_dev_set_iface_owner_t ugen20_dev_set_iface_owner; +static libusb20_dev_set_iface_perm_t ugen20_dev_set_iface_perm; +static libusb20_dev_set_owner_t ugen20_dev_set_owner; +static libusb20_dev_set_perm_t ugen20_dev_set_perm; +static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk; +static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name; +static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk; +static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk; +static libusb20_root_set_owner_t ugen20_root_set_owner; +static libusb20_root_get_owner_t ugen20_root_get_owner; +static libusb20_root_set_perm_t ugen20_root_set_perm; +static libusb20_root_get_perm_t ugen20_root_get_perm; + +const struct libusb20_backend_methods libusb20_ugen20_backend = { + LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20) +}; + +/* USB device specific */ +static libusb20_get_config_desc_full_t ugen20_get_config_desc_full; +static libusb20_get_config_index_t ugen20_get_config_index; +static libusb20_set_config_index_t ugen20_set_config_index; +static libusb20_claim_interface_t ugen20_claim_interface; +static libusb20_release_interface_t ugen20_release_interface; +static libusb20_set_alt_index_t ugen20_set_alt_index; +static libusb20_reset_device_t ugen20_reset_device; +static libusb20_set_power_mode_t ugen20_set_power_mode; +static libusb20_get_power_mode_t ugen20_get_power_mode; +static libusb20_kernel_driver_active_t ugen20_kernel_driver_active; +static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver; +static libusb20_do_request_sync_t ugen20_do_request_sync; +static libusb20_process_t ugen20_process; + +/* USB transfer specific */ +static libusb20_tr_open_t ugen20_tr_open; +static libusb20_tr_close_t ugen20_tr_close; +static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync; +static libusb20_tr_submit_t ugen20_tr_submit; +static libusb20_tr_cancel_async_t ugen20_tr_cancel_async; + +static const struct libusb20_device_methods libusb20_ugen20_device_methods = { + LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20) +}; + +static const char * +ugen20_get_backend_name(void) +{ + return ("FreeBSD UGEN 2.0"); +} + +static uint32_t +ugen20_path_convert_one(const char **pp) +{ + const char *ptr; + uint32_t temp = 0; + + ptr = *pp; + + while ((*ptr >= '0') && (*ptr <= '9')) { + temp *= 10; + temp += (*ptr - '0'); + if (temp >= 1000000) { + /* catch overflow early */ + return (0 - 1); + } + ptr++; + } + + if (*ptr == '.') { + /* skip dot */ + ptr++; + } + *pp = ptr; + + return (temp); +} + +static int +ugen20_enumerate(struct libusb20_device *pdev, const char *id) +{ + const char *tmp = id; + struct usb2_device_descriptor ddesc; + struct usb2_device_info devinfo; + uint32_t plugtime; + char buf[64]; + int f; + int error; + + pdev->bus_number = ugen20_path_convert_one(&tmp); + pdev->device_address = ugen20_path_convert_one(&tmp); + + snprintf(buf, sizeof(buf), "/dev/ugen%u.%u", + pdev->bus_number, pdev->device_address); + + f = open(buf, O_RDWR); + if (f < 0) { + return (LIBUSB20_ERROR_OTHER); + } + if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + /* store when the device was plugged */ + pdev->session_data.plugtime = plugtime; + + if (ioctl(f, USB_GET_DEVICE_DESC, &ddesc)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc)); + + libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc)); + + if (pdev->ddesc.bNumConfigurations == 0) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } else if (pdev->ddesc.bNumConfigurations >= 8) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + if (ioctl(f, USB_GET_DEVICEINFO, &devinfo)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + switch (devinfo.udi_mode) { + case USB_MODE_DEVICE: + pdev->usb_mode = LIBUSB20_MODE_DEVICE; + break; + default: + pdev->usb_mode = LIBUSB20_MODE_HOST; + break; + } + + switch (devinfo.udi_speed) { + case USB_SPEED_LOW: + pdev->usb_speed = LIBUSB20_SPEED_LOW; + break; + case USB_SPEED_FULL: + pdev->usb_speed = LIBUSB20_SPEED_FULL; + break; + case USB_SPEED_HIGH: + pdev->usb_speed = LIBUSB20_SPEED_HIGH; + break; + case USB_SPEED_VARIABLE: + pdev->usb_speed = LIBUSB20_SPEED_VARIABLE; + break; + case USB_SPEED_SUPER: + pdev->usb_speed = LIBUSB20_SPEED_SUPER; + break; + default: + pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN; + break; + } + + /* generate a nice description for printout */ + + snprintf(pdev->usb_desc, sizeof(pdev->usb_desc), + "ugen%u.%u: <%s %s> at usbus%u", pdev->bus_number, + pdev->device_address, devinfo.udi_product, + devinfo.udi_vendor, pdev->bus_number); + + error = 0; +done: + close(f); + return (error); +} + +struct ugen20_urd_state { + struct usb2_read_dir urd; + uint32_t nparsed; + int f; + uint8_t *ptr; + const char *src; + const char *dst; + uint8_t buf[256]; + uint8_t dummy_zero[1]; +}; + +static int +ugen20_readdir(struct ugen20_urd_state *st) +{ + ; /* style fix */ +repeat: + if (st->ptr == NULL) { + st->urd.urd_startentry += st->nparsed; + st->urd.urd_data = st->buf; + st->urd.urd_maxlen = sizeof(st->buf); + st->nparsed = 0; + + if (ioctl(st->f, USB_READ_DIR, &st->urd)) { + return (EINVAL); + } + st->ptr = st->buf; + } + if (st->ptr[0] == 0) { + if (st->nparsed) { + st->ptr = NULL; + goto repeat; + } else { + return (ENXIO); + } + } + st->src = (void *)(st->ptr + 1); + st->dst = st->src + strlen(st->src) + 1; + st->ptr = st->ptr + st->ptr[0]; + st->nparsed++; + + if ((st->ptr < st->buf) || + (st->ptr > st->dummy_zero)) { + /* invalid entry */ + return (EINVAL); + } + return (0); +} + +static int +ugen20_init_backend(struct libusb20_backend *pbe) +{ + struct ugen20_urd_state state; + struct libusb20_device *pdev; + + memset(&state, 0, sizeof(state)); + + state.f = open("/dev/usb", O_RDONLY); + if (state.f < 0) + return (LIBUSB20_ERROR_OTHER); + + while (ugen20_readdir(&state) == 0) { + + if ((state.src[0] != 'u') || + (state.src[1] != 'g') || + (state.src[2] != 'e') || + (state.src[3] != 'n')) { + continue; + } + pdev = libusb20_dev_alloc(); + if (pdev == NULL) { + continue; + } + if (ugen20_enumerate(pdev, state.src + 4)) { + libusb20_dev_free(pdev); + continue; + } + /* put the device on the backend list */ + libusb20_be_enqueue_device(pbe, pdev); + } + close(state.f); + return (0); /* success */ +} + +static int +ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) +{ + struct usb2_fs_endpoint *pfse = NULL; + struct usb2_fs_init fs_init = { /* zero */ }; + uint32_t size; + uint32_t plugtime; + char buf[64]; + int f; + int g; + int error; + + snprintf(buf, sizeof(buf), "/dev/ugen%u.%u", + pdev->bus_number, pdev->device_address); + + /* + * We need two file handles, one for the control endpoint and one + * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised + * kernel locking. + */ + g = open(buf, O_RDWR); + if (g < 0) { + return (LIBUSB20_ERROR_NO_DEVICE); + } + f = open(buf, O_RDWR); + if (f < 0) { + close(g); + return (LIBUSB20_ERROR_NO_DEVICE); + } + if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + /* check that the correct device is still plugged */ + if (pdev->session_data.plugtime != plugtime) { + error = LIBUSB20_ERROR_NO_DEVICE; + goto done; + } + if (nMaxTransfer != 0) { + + size = nMaxTransfer * sizeof(*pfse); + + pfse = malloc(size); + if (!pfse) { + error = LIBUSB20_ERROR_NO_MEM; + goto done; + } + memset(pfse, 0, size); + + fs_init.pEndpoints = pfse; + fs_init.ep_index_max = nMaxTransfer; + + if (ioctl(f, USB_FS_INIT, &fs_init)) { + error = LIBUSB20_ERROR_OTHER; + goto done; + } + } + /* set methods */ + pdev->methods = &libusb20_ugen20_device_methods; + pdev->privBeData = pfse; + pdev->file = f; + pdev->file_ctrl = g; + error = 0; +done: + if (error) { + if (pfse) { + free(pfse); + } + close(f); + close(g); + } + return (error); +} + +static int +ugen20_close_device(struct libusb20_device *pdev) +{ + struct usb2_fs_uninit fs_uninit = { /* zero */ }; + int error = 0; + + if (pdev->privBeData) { + if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) { + error = LIBUSB20_ERROR_OTHER; + } + free(pdev->privBeData); + } + pdev->nTransfer = 0; + pdev->privBeData = NULL; + close(pdev->file); + close(pdev->file_ctrl); + pdev->file = -1; + pdev->file_ctrl = -1; + return (error); +} + +static void +ugen20_exit_backend(struct libusb20_backend *pbe) +{ + return; /* nothing to do */ +} + +static int +ugen20_get_config_desc_full(struct libusb20_device *pdev, + uint8_t **ppbuf, uint16_t *plen, uint8_t index) +{ + struct usb2_gen_descriptor gen_desc = { /* zero */ }; + struct usb2_config_descriptor cdesc; + uint8_t *ptr; + uint16_t len; + int error; + + gen_desc.ugd_data = &cdesc; + gen_desc.ugd_maxlen = sizeof(cdesc); + gen_desc.ugd_config_index = index; + + error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc); + if (error) { + return (LIBUSB20_ERROR_OTHER); + } + len = UGETW(cdesc.wTotalLength); + if (len < sizeof(cdesc)) { + /* corrupt descriptor */ + return (LIBUSB20_ERROR_OTHER); + } + ptr = malloc(len); + if (!ptr) { + return (LIBUSB20_ERROR_NO_MEM); + } + gen_desc.ugd_data = ptr; + gen_desc.ugd_maxlen = len; + + error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc); + if (error) { + free(ptr); + return (LIBUSB20_ERROR_OTHER); + } + /* make sure that the device doesn't fool us */ + memcpy(ptr, &cdesc, sizeof(cdesc)); + + *ppbuf = ptr; + *plen = len; + + return (0); /* success */ +} + +static int +ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex) +{ + int temp; + + if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + *pindex = temp; + + return (0); +} + +static int +ugen20_set_config_index(struct libusb20_device *pdev, uint8_t index) +{ + int temp = index; + + if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_claim_interface(struct libusb20_device *pdev, uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, USB_CLAIM_INTERFACE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_release_interface(struct libusb20_device *pdev, uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, USB_RELEASE_INTERFACE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_set_alt_index(struct libusb20_device *pdev, + uint8_t iface_index, uint8_t alt_index) +{ + struct usb2_alt_interface alt_iface = { /* zero */ }; + + alt_iface.uai_interface_index = iface_index; + alt_iface.uai_alt_index = alt_index; + + if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_reset_device(struct libusb20_device *pdev) +{ + int temp = 0; + + if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) +{ + int temp; + + switch (power_mode) { + case LIBUSB20_POWER_OFF: + temp = USB_POWER_MODE_OFF; + break; + case LIBUSB20_POWER_ON: + temp = USB_POWER_MODE_ON; + break; + case LIBUSB20_POWER_SAVE: + temp = USB_POWER_MODE_SAVE; + break; + case LIBUSB20_POWER_SUSPEND: + temp = USB_POWER_MODE_SUSPEND; + break; + case LIBUSB20_POWER_RESUME: + temp = USB_POWER_MODE_RESUME; + break; + default: + return (LIBUSB20_ERROR_INVALID_PARAM); + } + if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); +} + +static int +ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode) +{ + int temp; + + if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + switch (temp) { + case USB_POWER_MODE_OFF: + temp = LIBUSB20_POWER_OFF; + break; + case USB_POWER_MODE_ON: + temp = LIBUSB20_POWER_ON; + break; + case USB_POWER_MODE_SAVE: + temp = LIBUSB20_POWER_SAVE; + break; + case USB_POWER_MODE_SUSPEND: + temp = LIBUSB20_POWER_SUSPEND; + break; + case USB_POWER_MODE_RESUME: + temp = LIBUSB20_POWER_RESUME; + break; + default: + temp = LIBUSB20_POWER_ON; + break; + } + *power_mode = temp; + return (0); /* success */ +} + +static int +ugen20_kernel_driver_active(struct libusb20_device *pdev, + uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_ACTIVE, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* kernel driver is active */ +} + +static int +ugen20_detach_kernel_driver(struct libusb20_device *pdev, + uint8_t iface_index) +{ + int temp = iface_index; + + if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) { + return (LIBUSB20_ERROR_OTHER); + } + return (0); /* kernel driver is active */ +} + +static int +ugen20_do_request_sync(struct libusb20_device *pdev, + struct LIBUSB20_CONTROL_SETUP_DECODED *setup, + void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) +{ + struct usb2_ctl_request req = { /* zero */ }; + + req.ucr_data = data; + if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { + req.ucr_flags |= USB_SHORT_XFER_OK; + } + if (libusb20_me_encode(&req.ucr_request, + sizeof(req.ucr_request), setup)) { + /* ignore */ + } + if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) { + return (LIBUSB20_ERROR_OTHER); + } + if (pactlen) { + /* get actual length */ + *pactlen = req.ucr_actlen; + } + return (0); /* kernel driver is active */ +} + +static int +ugen20_process(struct libusb20_device *pdev) +{ + struct usb2_fs_complete temp; + struct usb2_fs_endpoint *fsep; + struct libusb20_transfer *xfer; + + while (1) { + + if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) { + if (errno == EBUSY) { + break; + } else { + /* device detached */ + return (LIBUSB20_ERROR_OTHER); + } + } + fsep = pdev->privBeData; + xfer = pdev->pTransfer; + fsep += temp.ep_index; + xfer += temp.ep_index; + + /* update transfer status */ + + if (fsep->status == 0) { + xfer->aFrames = fsep->aFrames; + xfer->timeComplete = fsep->isoc_time_complete; + xfer->status = LIBUSB20_TRANSFER_COMPLETED; + } else if (fsep->status == USB_ERR_CANCELLED) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_CANCELLED; + } else if (fsep->status == USB_ERR_STALLED) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_STALL; + } else if (fsep->status == USB_ERR_TIMEOUT) { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_TIMED_OUT; + } else { + xfer->aFrames = 0; + xfer->timeComplete = 0; + xfer->status = LIBUSB20_TRANSFER_ERROR; + } + libusb20_tr_callback_wrapper(xfer); + } + return (0); /* done */ +} + +static int +ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, + uint32_t MaxFrameCount, uint8_t ep_no) +{ + struct usb2_fs_open temp = { /* zero */ }; + struct usb2_fs_endpoint *fsep; + + fsep = xfer->pdev->privBeData; + fsep += xfer->trIndex; + + temp.max_bufsize = MaxBufSize; + temp.max_frames = MaxFrameCount; + temp.ep_index = xfer->trIndex; + temp.ep_no = ep_no; + + if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + /* maximums might have changed - update */ + xfer->maxFrames = temp.max_frames; + + /* "max_bufsize" should be multiple of "max_packet_length" */ + xfer->maxTotalLength = temp.max_bufsize; + xfer->maxPacketLen = temp.max_packet_length; + + /* setup buffer and length lists */ + fsep->ppBuffer = xfer->ppBuffer;/* zero copy */ + fsep->pLength = xfer->pLength; /* zero copy */ + + return (0); /* success */ +} + +static int +ugen20_tr_close(struct libusb20_transfer *xfer) +{ + struct usb2_fs_close temp = { /* zero */ }; + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); /* success */ +} + +static int +ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer) +{ + struct usb2_fs_clear_stall_sync temp = { /* zero */ }; + + /* if the transfer is active, an error will be returned */ + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) { + return (LIBUSB20_ERROR_INVALID_PARAM); + } + return (0); /* success */ +} + +static void +ugen20_tr_submit(struct libusb20_transfer *xfer) +{ + struct usb2_fs_start temp = { /* zero */ }; + struct usb2_fs_endpoint *fsep; + + fsep = xfer->pdev->privBeData; + fsep += xfer->trIndex; + + fsep->nFrames = xfer->nFrames; + fsep->flags = 0; + if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { + fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK; + } + if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) { + fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK; + } + if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) { + fsep->flags |= USB_FS_FLAG_FORCE_SHORT; + } + if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) { + fsep->flags |= USB_FS_FLAG_CLEAR_STALL; + } + fsep->timeout = xfer->timeout; + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) { + /* ignore any errors - should never happen */ + } + return; /* success */ +} + +static void +ugen20_tr_cancel_async(struct libusb20_transfer *xfer) +{ + struct usb2_fs_stop temp = { /* zero */ }; + + temp.ep_index = xfer->trIndex; + + if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) { + /* ignore any errors - should never happen */ + } + return; +} + +static int +ugen20_be_ioctl(uint32_t cmd, void *data) +{ + int f; + int err; + + f = open("/dev/usb", O_RDONLY); + if (f < 0) + return (LIBUSB20_ERROR_OTHER); + err = ioctl(f, cmd, data); + if (err == -1) { + if (errno == EPERM) { + err = LIBUSB20_ERROR_ACCESS; + } else { + err = LIBUSB20_ERROR_OTHER; + } + } + close(f); + return (err); +} + +static int +ugen20_be_do_perm(uint32_t get_cmd, uint32_t set_cmd, uint8_t bus, + uint8_t dev, uint8_t iface, uid_t *uid, + gid_t *gid, mode_t *mode) +{ + struct usb2_dev_perm perm = { /* zero */ }; + int err; + + perm.bus_index = bus; + perm.dev_index = dev; + perm.iface_index = iface; + + err = ugen20_be_ioctl(get_cmd, &perm); + if (err) + return (err); + + if (set_cmd == 0) { + if (uid) + *uid = perm.user_id; + if (gid) + *gid = perm.group_id; + if (mode) + *mode = perm.mode; + return (0); + } + if (uid) + perm.user_id = *uid; + if (gid) + perm.group_id = *gid; + if (mode) + perm.mode = *mode; + + return (ugen20_be_ioctl(set_cmd, &perm)); +} + +static int +ugen20_bus_set_owner(struct libusb20_backend *pbe, + uint8_t bus, uid_t user, gid_t group) +{ + return (ugen20_be_do_perm(USB_GET_BUS_PERM, USB_SET_BUS_PERM, + bus, 0, 0, &user, &group, NULL)); +} + +static int +ugen20_bus_get_owner(struct libusb20_backend *pbe, uint8_t bus, + uid_t *user, gid_t *group) +{ + return (ugen20_be_do_perm(USB_GET_BUS_PERM, 0, + bus, 0, 0, user, group, NULL)); +} + +static int +ugen20_bus_set_perm(struct libusb20_backend *pbe, + uint8_t bus, mode_t mode) +{ + return (ugen20_be_do_perm(USB_GET_BUS_PERM, USB_SET_BUS_PERM, + bus, 0, 0, NULL, NULL, &mode)); +} + +static int +ugen20_bus_get_perm(struct libusb20_backend *pbe, + uint8_t bus, mode_t *mode) +{ + return (ugen20_be_do_perm(USB_GET_BUS_PERM, 0, + bus, 0, 0, NULL, NULL, mode)); +} + +static int +ugen20_dev_get_iface_owner(struct libusb20_device *pdev, + uint8_t iface_index, uid_t *user, gid_t *group) +{ + return (ugen20_be_do_perm(USB_GET_IFACE_PERM, 0, + pdev->bus_number, pdev->device_address, iface_index, + user, group, NULL)); +} + +static int +ugen20_dev_get_iface_perm(struct libusb20_device *pdev, + uint8_t iface_index, mode_t *mode) +{ + return (ugen20_be_do_perm(USB_GET_IFACE_PERM, 0, + pdev->bus_number, pdev->device_address, iface_index, + NULL, NULL, mode)); +} + +static int +ugen20_dev_get_owner(struct libusb20_device *pdev, + uid_t *user, gid_t *group) +{ + return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, 0, + pdev->bus_number, pdev->device_address, 0, + user, group, NULL)); +} + +static int +ugen20_dev_get_perm(struct libusb20_device *pdev, mode_t *mode) +{ + return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, 0, + pdev->bus_number, pdev->device_address, 0, + NULL, NULL, mode)); +} + +static int +ugen20_dev_set_iface_owner(struct libusb20_device *pdev, + uint8_t iface_index, uid_t user, gid_t group) +{ + return (ugen20_be_do_perm(USB_GET_IFACE_PERM, USB_SET_IFACE_PERM, + pdev->bus_number, pdev->device_address, iface_index, + &user, &group, NULL)); +} + +static int +ugen20_dev_set_iface_perm(struct libusb20_device *pdev, + uint8_t iface_index, mode_t mode) +{ + return (ugen20_be_do_perm(USB_GET_IFACE_PERM, USB_SET_IFACE_PERM, + pdev->bus_number, pdev->device_address, iface_index, + NULL, NULL, &mode)); +} + +static int +ugen20_root_get_dev_quirk(struct libusb20_backend *pbe, + uint16_t index, struct libusb20_quirk *pq) +{ + struct usb2_gen_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.index = index; + + err = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q); + + if (err) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } else { + pq->vid = q.vid; + pq->pid = q.pid; + pq->bcdDeviceLow = q.bcdDeviceLow; + pq->bcdDeviceHigh = q.bcdDeviceHigh; + strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); + } + return (err); +} + +static int +ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t index, + struct libusb20_quirk *pq) +{ + struct usb2_gen_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.index = index; + + err = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q); + + if (err) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } else { + strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); + } + return (err); +} + +static int +ugen20_root_add_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + struct usb2_gen_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.vid = pq->vid; + q.pid = pq->pid; + q.bcdDeviceLow = pq->bcdDeviceLow; + q.bcdDeviceHigh = pq->bcdDeviceHigh; + strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); + + err = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q); + if (err) { + if (errno == ENOMEM) { + return (LIBUSB20_ERROR_NO_MEM); + } + } + return (err); +} + +static int +ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, + struct libusb20_quirk *pq) +{ + struct usb2_gen_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.vid = pq->vid; + q.pid = pq->pid; + q.bcdDeviceLow = pq->bcdDeviceLow; + q.bcdDeviceHigh = pq->bcdDeviceHigh; + strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); + + err = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q); + if (err) { + if (errno == EINVAL) { + return (LIBUSB20_ERROR_NOT_FOUND); + } + } + return (err); +} + +static int +ugen20_dev_set_owner(struct libusb20_device *pdev, + uid_t user, gid_t group) +{ + return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, USB_SET_DEVICE_PERM, + pdev->bus_number, pdev->device_address, 0, + &user, &group, NULL)); +} + +static int +ugen20_dev_set_perm(struct libusb20_device *pdev, mode_t mode) +{ + return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, USB_SET_DEVICE_PERM, + pdev->bus_number, pdev->device_address, 0, + NULL, NULL, &mode)); +} + +static int +ugen20_root_set_owner(struct libusb20_backend *pbe, + uid_t user, gid_t group) +{ + return (ugen20_be_do_perm(USB_GET_ROOT_PERM, USB_SET_ROOT_PERM, 0, 0, 0, + &user, &group, NULL)); +} + +static int +ugen20_root_get_owner(struct libusb20_backend *pbe, uid_t *user, gid_t *group) +{ + return (ugen20_be_do_perm(USB_GET_ROOT_PERM, 0, 0, 0, 0, + user, group, NULL)); +} + +static int +ugen20_root_set_perm(struct libusb20_backend *pbe, mode_t mode) +{ + return (ugen20_be_do_perm(USB_GET_ROOT_PERM, USB_SET_ROOT_PERM, 0, 0, 0, + NULL, NULL, &mode)); +} + +static int +ugen20_root_get_perm(struct libusb20_backend *pbe, mode_t *mode) +{ + return (ugen20_be_do_perm(USB_GET_ROOT_PERM, 0, 0, 0, 0, + NULL, NULL, mode)); +} diff --git a/share/man/man4/usb2_bluetooth.4 b/share/man/man4/usb2_bluetooth.4 new file mode 100644 index 000000000000..8b2c0fc5167a --- /dev/null +++ b/share/man/man4/usb2_bluetooth.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_BLUETOOTH 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_bluetooth +. +.Nd "USB bluetooth container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_bluetooth" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_bluetooth_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB bluetooth drivers. +. +When you plug an USB bluetooth device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_controller.4 b/share/man/man4/usb2_controller.4 new file mode 100644 index 000000000000..998d0dccaf78 --- /dev/null +++ b/share/man/man4/usb2_controller.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_CONTROLLER 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_controller +. +.Nd "USB controller container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_controller" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_controller_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB Host and Device side +controller drivers. +. +When you plug an USB controller the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_core.4 b/share/man/man4/usb2_core.4 new file mode 100644 index 000000000000..318b4fc4125a --- /dev/null +++ b/share/man/man4/usb2_core.4 @@ -0,0 +1,630 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 20, 2008 +.Dt USB2_CORE 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_core +. +.Nd "USB core functions" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_core" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_core_load="YES" +.Ed +. +.Pp +Here is a list of commonly used functions: +.Pp +. +.Ft "usb2_error_t" +.Fo "usb2_transfer_setup" +.Fa "udev" +.Fa "ifaces" +.Fa "pxfer" +.Fa "setup_start" +.Fa "n_setup" +.Fa "priv_sc" +.Fa "priv_mtx" +.Fc +. +.Ft "void" +.Fo "usb2_transfer_unsetup" +.Fa "pxfer" +.Fa "n_setup" +.Fc +. +.Ft "void" +.Fo "usb2_transfer_start" +.Fa "xfer" +.Fc +. +.Ft "void" +.Fo "usb2_transfer_stop" +.Fa "xfer" +.Fc +. +.Ft "void" +.Fo "usb2_transfer_drain" +.Fa "xfer" +.Fc +. +. +.Sh DESCRIPTION +The +.Nm +module implements the core functionality of the USB standard and many +helper functions to make USB device driver programming easier and more +safe. +. +The +.Nm +module supports both USB Host and USB Device side mode! +. +.Sh USB TRANSFER MANAGEMENT FUNCTIONS +The USB standard defines four types of USB transfers. +. +Control transfers, Bulk transfers, Interrupt transfers and Isochronous +transfers. +. +All the transfer types are managed using the following five functions: +. +.Pp +. +.Fn usb2_transfer_setup +This function will allocate memory for and initialise an array of USB +transfers and all required DMA memory. +. +This function can sleep or block waiting for resources to become +available. +.Fa udev +is a pointer to "struct usb2_device". +.Fa ifaces +is an array of interface index numbers to use. See "if_index". +.Fa pxfer +is a pointer to an array of USB transfer pointers that are initialized +to NULL, and then pointed to allocated USB transfers. +.Fa setup_start +is a pointer to an array of USB config structures. +.Fa n_setup +is a number telling the USB system how many USB transfers should be +setup. +.Fa priv_sc +is the private softc pointer, which will be used to initialize +"xfer->priv_sc". +.Fa priv_mtx +is the private mutex protecting the transfer structure and the +softc. This pointer is used to initialize "xfer->priv_mtx". +This function returns +zero upon success. A non-zero return value indicates failure. +. +.Pp +. +.Fn usb2_transfer_unsetup +This function will release the given USB transfers and all allocated +resources associated with these USB transfers. +.Fa pxfer +is a pointer to an array of USB transfer pointers, that may be NULL, +that should be freed by the USB system. +.Fa n_setup +is a number telling the USB system how many USB transfers should be +unsetup. +. +This function can sleep waiting for USB transfers to complete. +. +This function is NULL safe with regard to the USB transfer structure +pointer. +. +It is not allowed to call this function from the USB transfer +callback. +. +.Pp +. +.Fn usb2_transfer_start +This function will start the USB transfer pointed to by +.Fa xfer, +if not already started. +. +This function is always non-blocking and must be called with the +so-called private USB mutex locked. +. +This function is NULL safe with regard to the USB transfer structure +pointer. +. +.Pp +. +.Fn usb2_transfer_stop +This function will stop the USB transfer pointed to by +.Fa xfer, +if not already stopped. +. +This function is always non-blocking and must be called with the +so-called private USB mutex locked. +. +This function can return before the USB callback has been called. +. +This function is NULL safe with regard to the USB transfer structure +pointer. +. +If the transfer was in progress, the callback will called with +"USB_ST_ERROR" and "xfer->error = USB_ERR_CANCELLED". +. +.Pp +. +.Fn usb2_transfer_drain +This function will stop an USB transfer, if not already stopped and +wait for any additional USB hardware operations to complete. +. +Buffers that are loaded into DMA using "usb2_set_frame_data()" can +safely be freed after that this function has returned. +. +This function can block the caller and will not return before the USB +callback has been called. +. +This function is NULL safe with regard to the USB transfer structure +pointer. +. +.Sh USB TRANSFER CALLBACK +. +The USB callback has three states. +. +USB_ST_SETUP, USB_ST_TRANSFERRED and USB_ST_ERROR. USB_ST_SETUP is the +initial state. +. +After the callback has been called with this state it will always be +called back at a later stage in one of the other two states. +. +In the USB_ST_ERROR state the "error" field of the USB transfer +structure is set to the error cause. +. +The USB callback should not restart the USB transfer in case the error +cause is USB_ERR_CANCELLED. +. +The USB callback is protected from recursion. +. +That means one can start and stop whatever transfer from the callback +of another transfer one desires. +. +Also the transfer that is currently called back. +. +Recursion is handled like this that when the callback that wants to +recurse returns it is called one more time. +. +. +.Pp +. +.Fn usb2_start_hardware +This function should only be called from within the USB callback and +is used to start the USB hardware. +. +Typical parameters that should be set in the USB transfer structure +before this function is called are "frlengths[]", "nframes" and +"frbuffers[]". +. +An USB transfer can have multiple frames consisting of one or more USB +packets making up an I/O vector for all USB transfer types. +. +After the USB transfer is complete "frlengths[]" is updated to the +actual USB transfer length for the given frame. +.Bd -literal -offset indent +void +usb2_default_callback(struct usb2_xfer *xfer) +{ + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + /* + * Setup xfer->frlengths[], xfer->nframes + * and write data to xfer->frbuffers[], if any + */ + usb2_start_hardware(xfer); + break; + + case USB_ST_TRANSFERRED: + /* + * Read data from xfer->frbuffers[], if any. + * "xfer->frlengths[]" should now have been + * updated to the actual length. + */ + break; + + default: /* Error */ + /* + * Print error message and clear stall + * for example. + */ + break; + } + /* + * Here it is safe to do something without the private + * USB mutex locked. + */ + return; +} +.Ed +. +.Sh USB CONTROL TRANSFERS +An USB control transfer has three parts. +. +First the SETUP packet, then DATA packet(s) and then a STATUS +packet. +. +The SETUP packet is always pointed to by "xfer->frbuffers[0]" and the +length is stored in "xfer->frlengths[0]" also if there should not be +sent any SETUP packet! If an USB control transfer has no DATA stage, +then "xfer->nframes" should be set to 1. +. +Else the default value is "xfer->nframes" equal to 2. +. +.Bd -literal -offset indent + +Example1: SETUP + STATUS + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usb2_start_hardware(xfer); + +Example2: SETUP + DATA + STATUS + xfer->nframes = 2; + xfer->frlenghts[0] = 8; + xfer->frlenghts[1] = 1; + usb2_start_hardware(xfer); + +Example3: SETUP + DATA + STATUS - split +1st callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usb2_start_hardware(xfer); + +2nd callback: + /* IMPORTANT: frbuffers[0] must still point at the setup packet! */ + xfer->nframes = 2; + xfer->frlenghts[0] = 0; + xfer->frlenghts[1] = 1; + usb2_start_hardware(xfer); + +Example4: SETUP + STATUS - split +1st callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + xfer->flags.manual_status = 1; + usb2_start_hardware(xfer); + +2nd callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 0; + xfer->flags.manual_status = 0; + usb2_start_hardware(xfer); + +.Ed +.Sh USB TRANSFER CONFIG +To simply the search for endpoints the +.Nm +module defines a USB config structure where it is possible to specify +the characteristics of the wanted endpoint. +.Bd -literal -offset indent + +struct usb2_config { + bufsize, + callback + direction, + endpoint, + frames, + index flags, + interval, + timeout, + type, +}; + +.Ed +. +.Pp +.Fa type +field selects the USB pipe type. +. +Valid values are: UE_INTERRUPT, UE_CONTROL, UE_BULK, +UE_ISOCHRONOUS. +. +The special value UE_BULK_INTR will select BULK and INTERRUPT pipes. +. +This field is mandatory. +. +.Pp +.Fa endpoint +field selects the USB endpoint number. +. +A value of 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching +endpoint. +. +This field is mandatory. +. +.Pp +.Fa direction +field selects the USB endpoint direction. +. +A value of "UE_DIR_ANY" will select the first matching endpoint. +. +Else valid values are: "UE_DIR_IN" and "UE_DIR_OUT". +. +"UE_DIR_IN" and "UE_DIR_OUT" can be binary OR'ed by "UE_DIR_SID" which +means that the direction will be swapped in case of +USB_MODE_DEVICE. +. +Note that "UE_DIR_IN" refers to the data transfer direction of the +"IN" tokens and "UE_DIR_OUT" refers to the data transfer direction of +the "OUT" tokens. +. +This field is mandatory. +. +.Pp +.Fa interval +field selects the interrupt interval. +. +The value of this field is given in milliseconds and is independent of +device speed. +. +Depending on the endpoint type, this field has different meaning: +.Bl -tag +.It UE_INTERRUPT +"0" use the default interrupt interval based on endpoint descriptor. +"Else" use the given value for polling rate. +.It UE_ISOCHRONOUS +"0" use default. "Else" the value is ignored. +.It UE_BULK +.It UE_CONTROL +"0" no transfer pre-delay. "Else" a delay as given by this field in +milliseconds is inserted before the hardware is started when +"usb2_start_hardware()" is called. +.Pp +NOTE: The transfer timeout, if any, is started after that the +pre-delay has elapsed! +.El +. +.Pp +.Fa timeout +field, if non-zero, will set the transfer timeout in milliseconds. If +the "timeout" field is zero and the transfer type is ISOCHRONOUS a +timeout of 250ms will be used. +. +.Pp +.Fa frames +field sets the maximum number of frames. If zero is specified it will +yield the following results: +.Bl -tag +.It UE_BULK +xfer->nframes = 1; +.It UE_INTERRUPT +xfer->nframes = 1; +.It UE_CONTROL +xfer->nframes = 2; +.It UE_ISOCHRONOUS +Not allowed. Will cause an error. +.El +. +.Pp +.Fa ep_index +field allows you to give a number, in case more endpoints match the +description, that selects which matching "ep_index" should be used. +. +.Pp +.Fa if_index +field allows you to select which of the interface numbers in the +"ifaces" array parameter passed to "usb2_transfer_setup" that should +be used when setting up the given USB transfer. +. +.Pp +.Fa flags +field has type "struct usb2_xfer_flags" and allows one to set initial +flags an USB transfer. Valid flags are: +.Bl -tag +.It force_short_xfer +This flag forces the last transmitted USB packet to be short. A short +packet has a length of less than "xfer->max_packet_size", which +derives from "wMaxPacketSize". This flag can be changed during +operation. +.It short_xfer_ok +This flag allows the received transfer length, "xfer->actlen" to be +less than "xfer->sumlen" upon completion of a transfer. This flag can +be changed during operation. +.It pipe_bof +This flag causes a failing USB transfer to remain first in the PIPE +queue except in the case of "xfer->error" equal to +"USB_ERR_CANCELLED". No other USB transfers in the affected PIPE queue +will be started until either: +.Bl -tag +.It 1 +The failing USB transfer is stopped using "usb2_transfer_stop()". +.It 2 +The failing USB transfer performs a successful transfer. +.El +The purpose of this flag is to avoid races when multiple transfers are +queued for execution on an USB endpoint, and the first executing +transfer fails leading to the need for clearing of stall for +example. +. +In this case this flag is used to prevent the following USB transfers +from being executed at the same time the clear-stall command is +executed on the USB control endpoint. +. +This flag can be changed during operation. +.Pp +"BOF" is short for "Block On Failure" +.Pp +NOTE: This flag should be set on all BULK and INTERRUPT USB transfers +which use an endpoint that can be shared between userland and kernel. +. +. +.It proxy_buffer +Setting this flag will cause that the total buffer size will be +rounded up to the nearest atomic hardware transfer size. +. +The maximum data length of any USB transfer is always stored in the +"xfer->max_data_length". +. +For control transfers the USB kernel will allocate additional space +for the 8-bytes of SETUP header. +. +These 8-bytes are not counted by the "xfer->max_data_length" +variable. +. +This flag can not be changed during operation. +. +. +.It ext_buffer +Setting this flag will cause that no data buffer will be +allocated. +. +Instead the USB client must supply a data buffer. +. +This flag can not be changed during operation. +. +. +.It manual_status +Setting this flag prevents an USB STATUS stage to be appended to the +end of the USB control transfer. +. +If no control data is transferred this flag must be cleared. +. +Else an error will be returned to the USB callback. +. +This flag is mostly useful for the USB device side. +. +This flag can be changed during operation. +. +. +.It no_pipe_ok +Setting this flag causes the USB_ERR_NO_PIPE error to be ignored. This +flag can not be changed during operation. +. +. +.It stall_pipe +.Bl -tag +.It Device Side Mode +Setting this flag will cause STALL pids to be sent to the endpoint +belonging to this transfer before the transfer is started. +. +The transfer is started at the moment the host issues a clear-stall +command on the STALL'ed endpoint. +. +This flag can be changed during operation. +.It Host Side Mode +Setting this flag will cause a clear-stall control request to be +executed on the endpoint before the USB transfer is started. +.El +.Pp +If this flag is changed outside the USB callback function you have to +use the "usb2_transfer_set_stall()" and "usb2_transfer_clear_stall()" +functions ! +. +.El +.Pp +.Fa bufsize +field sets the total buffer size in bytes. +. +If this field is zero, "wMaxPacketSize" will be used, multiplied by +the "frames" field if the transfer type is ISOCHRONOUS. +. +This is useful for setting up interrupt pipes. +. +This field is mandatory. +.Pp +NOTE: For control transfers "bufsize" includes the length of the +request structure. +. +.Pp +.Fa callback +pointer sets the USB callback. This field is mandatory. +. +. +.Sh USB LINUX COMPAT LAYER +The +.Nm +module supports the Linux USB API. +. +. +. +. +.Sh USB SECURITY MODEL +. +. +The +.Nm +module implements fine grained read and write access based on username +and group. +. +Access is granted at four levels: +. +.Bl -tag +.It Level 4 - USB interface +USB interfaces can be given individual access rights. +.It Level 3 - USB device +USB devices can be given individual access rights. +.It Level 2 - USB BUS +USB busses can be given individual access rights. +.It Level 1 - USB +USB as a whole can be given individual access rights. +.El +.Pp +The +.Nm +module will search for access rights starting at level 4 continuing +downwards to USB at level 1. +. +For critical applications you should be aware that the outgoing serial +BUS traffic will be broadcasted to all USB devices. +. +For absolute security USB devices that require different access rights +should not be placed on the same USB BUS or controller. +. +If connected to the same USB bus, it is possible that a USB device can +sniff and intercept the communication of another USB device. +. +Using USB HUBs will not solve this problem. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usbconfig 8 +.Sh STANDARDS +The +.Nm +module complies with the USB 2.0 standard. +.Sh HISTORY +The +.Nm +module has been inspired by the NetBSD USB stack initially written by +Lennart Augustsson. The +.Nm +module was written by +.An Hans Petter Selasky Aq hselasky@freebsd.org . diff --git a/share/man/man4/usb2_ethernet.4 b/share/man/man4/usb2_ethernet.4 new file mode 100644 index 000000000000..f951677c70f9 --- /dev/null +++ b/share/man/man4/usb2_ethernet.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_ETHERNET 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_ethernet +. +.Nd "USB ethernet container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_ethernet" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_ethernet_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB ethernet drivers. +. +When you plug an USB ethernet device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_image.4 b/share/man/man4/usb2_image.4 new file mode 100644 index 000000000000..30c9ef60dd7b --- /dev/null +++ b/share/man/man4/usb2_image.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_IMAGE 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_image +. +.Nd "USB image container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_image" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_image_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB image scanner drivers. +. +When you plug an USB image scanner device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_input.4 b/share/man/man4/usb2_input.4 new file mode 100644 index 000000000000..cf486039cb75 --- /dev/null +++ b/share/man/man4/usb2_input.4 @@ -0,0 +1,66 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_INPUT 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_input +. +.Nd "USB input container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_input" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_input_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB input drivers. +. +When you plug an USB input device, like USB mouse, USB keyboard and USB +HID device, the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_misc.4 b/share/man/man4/usb2_misc.4 new file mode 100644 index 000000000000..e658f3158276 --- /dev/null +++ b/share/man/man4/usb2_misc.4 @@ -0,0 +1,66 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_MISC 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_misc +. +.Nd "USB miscellaneous container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_misc" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_misc_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for various USB drivers that does not fit +into into any other USB container module. +. +When you plug an USB miscellaneous device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_ndis.4 b/share/man/man4/usb2_ndis.4 new file mode 100644 index 000000000000..4173c1bdfd65 --- /dev/null +++ b/share/man/man4/usb2_ndis.4 @@ -0,0 +1,66 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_NDIS 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_ndis +. +.Nd "USB NDIS container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_ndis" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_ndis_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for various USB drivers that does not fit +into into any other USB container module. +. +When you plug an USB NDIS device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_quirk.4 b/share/man/man4/usb2_quirk.4 new file mode 100644 index 000000000000..55c5859991d5 --- /dev/null +++ b/share/man/man4/usb2_quirk.4 @@ -0,0 +1,64 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_QUIRK 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_quirk +. +.Nd "USB quirk container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_quirk" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_quirk_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB quirks. +. +USB quirks are workarounds for specific USB device problems. The +.Nm +module can be dynamically loaded and unloaded at any time. +. +. +. +.Sh SEE ALSO +.Xr usb2_core 4 +.Xr usb2_controller 4 diff --git a/share/man/man4/usb2_serial.4 b/share/man/man4/usb2_serial.4 new file mode 100644 index 000000000000..d3512cfbf2c3 --- /dev/null +++ b/share/man/man4/usb2_serial.4 @@ -0,0 +1,66 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_SERIAL 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_serial +. +.Nd "USB serial container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_serial" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_serial_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB serial and USB parallel port +drivers. +. +When you plug an USB serial or USB parallel port device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_sound.4 b/share/man/man4/usb2_sound.4 new file mode 100644 index 000000000000..1e70ceac145c --- /dev/null +++ b/share/man/man4/usb2_sound.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_SOUND 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_sound +. +.Nd "USB sound container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_sound" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_sound_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB sound and USB MIDI drivers. +. +When you plug an USB sound or USB MIDI device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_storage.4 b/share/man/man4/usb2_storage.4 new file mode 100644 index 000000000000..9f85207e16ab --- /dev/null +++ b/share/man/man4/usb2_storage.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_STORAGE 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_storage +. +.Nd "USB storage container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_storage" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_storage_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB storage drivers. +. +When you plug an USB storage device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/share/man/man4/usb2_template.4 b/share/man/man4/usb2_template.4 new file mode 100644 index 000000000000..06dc611d0298 --- /dev/null +++ b/share/man/man4/usb2_template.4 @@ -0,0 +1,84 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_TEMPLATE 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_template +. +.Nd "USB templates" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_template" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_template_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module implements various USB templates that are needed when +programming an USB device side driver. +. +A USB template consists of an USB device descriptor, one or more USB +configuration descriptors, one or more USB interface descriptors, one +or more USB endpoint descriptors, USB strings and additional USB +descriptors. +. +The USB template module currently has templates for USB Mass Storage, +USB CDC Ethernet and Message Transfer Protocol. +. +USB templates are currently selected using the "hw.usb2.template" +sysctl. +. +The "hw.usb2.template" value can be changed at any time, but will not +have any effect until the USB device has been re-enumerated. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 +.Sh STANDARDS +The +.Nm +module complies with the USB 2.0 standard. +.Sh HISTORY +The +.Nm +module was written by +.An Hans Petter Selasky Aq hselasky@freebsd.org . diff --git a/share/man/man4/usb2_wlan.4 b/share/man/man4/usb2_wlan.4 new file mode 100644 index 000000000000..73ff75f24fc6 --- /dev/null +++ b/share/man/man4/usb2_wlan.4 @@ -0,0 +1,65 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd September 21, 2008 +.Dt USB2_WLAN 4 +.Os +. +.Sh NAME +. +. +.Nm usb2_wlan +. +.Nd "USB WLAN container module" +. +. +.Sh SYNOPSIS +To compile this module into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb2_wlan" +.Ed +.Pp +To load the module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +usb2_wlan_load="YES" +.Ed +. +.Sh DESCRIPTION +The +.Nm +module is a container module for all USB WLAN drivers. +. +When you plug an USB WLAN device the +.Nm +module will try to load the required driver for your device +automatically. +. +. +. +.Sh SEE ALSO +.Xr usb2_controller 4 +.Xr usb2_core 4 diff --git a/sys/conf/files b/sys/conf/files index af7f0b0a369e..9b67a7342c25 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1366,6 +1366,145 @@ dev/usb/uscanner.c optional uscanner dev/usb/uslcom.c optional uslcom dev/usb/uvisor.c optional uvisor dev/usb/uvscom.c optional uvscom +# +# USB2 controller drivers +# +dev/usb2/controller/at91dci.c optional usb2_core usb2_controller usb2_controller_at91dci +dev/usb2/controller/at91dci_atmelarm.c optional usb2_core usb2_controller usb2_controller_at91dci at91rm9200 +dev/usb2/controller/musb2_otg.c optional usb2_core usb2_controller usb2_controller_musb +dev/usb2/controller/musb2_otg_atmelarm.c optional usb2_core usb2_controller usb2_controller_musb at91rm9200 +dev/usb2/controller/ehci2.c optional usb2_core usb2_controller usb2_controller_ehci +dev/usb2/controller/ehci2_pci.c optional usb2_core usb2_controller usb2_controller_ehci pci +dev/usb2/controller/ohci2.c optional usb2_core usb2_controller usb2_controller_ohci +dev/usb2/controller/ohci2_atmelarm.c optional usb2_core usb2_controller usb2_controller_ohci at91rm9200 +dev/usb2/controller/ohci2_pci.c optional usb2_core usb2_controller usb2_controller_ohci pci +dev/usb2/controller/uhci2.c optional usb2_core usb2_controller usb2_controller_uhci +dev/usb2/controller/uhci2_pci.c optional usb2_core usb2_controller usb2_controller_uhci pci +dev/usb2/controller/uss820dci.c optional usb2_core usb2_controller usb2_controller_uss820dci +dev/usb2/controller/uss820dci_atmelarm.c optional usb2_core usb2_controller usb2_controller_uss820dci at91rm9200 +dev/usb2/controller/usb2_controller.c optional usb2_core usb2_controller +# +# USB2 storage drivers +# +dev/usb2/storage/ata-usb2.c optional usb2_core usb2_storage usb2_storage_ata +dev/usb2/storage/umass2.c optional usb2_core usb2_storage usb2_storage_mass +dev/usb2/storage/urio2.c optional usb2_core usb2_storage usb2_storage_rio +dev/usb2/storage/usb2_storage.c optional usb2_core usb2_storage +dev/usb2/storage/ustorage2_fs.c optional usb2_core usb2_storage usb2_storage_fs +# +# USB2 NDIS driver +# +dev/usb2/ndis/if_ndis_usb2.c optional usb2_core usb2_ndis +dev/usb2/ndis/usb2_ndis.c optional usb2_core usb2_ndis +# +# USB2 core +# +dev/usb2/core/usb2_busdma.c optional usb2_core +dev/usb2/core/usb2_compat_linux.c optional usb2_core +dev/usb2/core/usb2_config_td.c optional usb2_core +dev/usb2/core/usb2_core.c optional usb2_core +dev/usb2/core/usb2_debug.c optional usb2_core +dev/usb2/core/usb2_dev.c optional usb2_core +dev/usb2/core/usb2_device.c optional usb2_core +dev/usb2/core/usb2_dynamic.c optional usb2_core +dev/usb2/core/usb2_error.c optional usb2_core +dev/usb2/core/usb2_generic.c optional usb2_core +dev/usb2/core/usb2_handle_request.c optional usb2_core +dev/usb2/core/usb2_hid.c optional usb2_core +dev/usb2/core/usb2_hub.c optional usb2_core +dev/usb2/core/usb2_if.m optional usb2_core +dev/usb2/core/usb2_lookup.c optional usb2_core +dev/usb2/core/usb2_mbuf.c optional usb2_core +dev/usb2/core/usb2_msctest.c optional usb2_core +dev/usb2/core/usb2_parse.c optional usb2_core +dev/usb2/core/usb2_process.c optional usb2_core +dev/usb2/core/usb2_request.c optional usb2_core +dev/usb2/core/usb2_sw_transfer.c optional usb2_core +dev/usb2/core/usb2_transfer.c optional usb2_core +dev/usb2/core/usb2_util.c optional usb2_core +# +# USB2 ethernet drivers +# +dev/usb2/ethernet/if_aue2.c optional usb2_core usb2_ethernet usb2_ethernet_aue +dev/usb2/ethernet/if_axe2.c optional usb2_core usb2_ethernet usb2_ethernet_axe +dev/usb2/ethernet/if_cdce2.c optional usb2_core usb2_ethernet usb2_ethernet_cdce +dev/usb2/ethernet/if_cue2.c optional usb2_core usb2_ethernet usb2_ethernet_cue +dev/usb2/ethernet/if_kue2.c optional usb2_core usb2_ethernet usb2_ethernet_kue +dev/usb2/ethernet/if_rue2.c optional usb2_core usb2_ethernet usb2_ethernet_rue +dev/usb2/ethernet/if_udav2.c optional usb2_core usb2_ethernet usb2_ethernet_udav +dev/usb2/ethernet/usb2_ethernet.c optional usb2_core usb2_ethernet +# +# USB2 WLAN drivers +# +dev/usb2/wlan/if_rum2.c optional usb2_core usb2_wlan usb2_wlan_rum +dev/usb2/wlan/if_ural2.c optional usb2_core usb2_wlan usb2_wlan_ral +dev/usb2/wlan/if_zyd2.c optional usb2_core usb2_wlan usb2_wlan_zyd +dev/usb2/wlan/usb2_wlan.c optional usb2_core usb2_wlan +# +# USB2 serial and parallel port drivers +# +dev/usb2/serial/uark2.c optional usb2_core usb2_serial usb2_serial_ark +dev/usb2/serial/ubsa2.c optional usb2_core usb2_serial usb2_serial_bsa +dev/usb2/serial/ubser2.c optional usb2_core usb2_serial usb2_serial_bser +dev/usb2/serial/uchcom2.c optional usb2_core usb2_serial usb2_serial_chcom +dev/usb2/serial/ucycom2.c optional usb2_core usb2_serial usb2_serial_cycom +dev/usb2/serial/ufoma2.c optional usb2_core usb2_serial usb2_serial_foma +dev/usb2/serial/uftdi2.c optional usb2_core usb2_serial usb2_serial_ftdi +dev/usb2/serial/ugensa2.c optional usb2_core usb2_serial usb2_serial_gensa +dev/usb2/serial/uipaq2.c optional usb2_core usb2_serial usb2_serial_ipaq +dev/usb2/serial/ulpt2.c optional usb2_core usb2_serial usb2_serial_lpt +dev/usb2/serial/umct2.c optional usb2_core usb2_serial usb2_serial_mct +dev/usb2/serial/umodem2.c optional usb2_core usb2_serial usb2_serial_modem +dev/usb2/serial/umoscom2.c optional usb2_core usb2_serial usb2_serial_moscom +dev/usb2/serial/uplcom2.c optional usb2_core usb2_serial usb2_serial_plcom +dev/usb2/serial/usb2_serial.c optional usb2_core usb2_serial +dev/usb2/serial/uvisor2.c optional usb2_core usb2_serial usb2_serial_visor +dev/usb2/serial/uvscom2.c optional usb2_core usb2_serial usb2_serial_vscom +# +# USB2 bluetooth drivers +# +dev/usb2/bluetooth/usb2_bluetooth.c optional usb2_core usb2_bluetooth +dev/usb2/bluetooth/ng_ubt2.c optional usb2_core usb2_bluetooth usb2_bluetooth_ng +dev/usb2/bluetooth/ubtbcmfw2.c optional usb2_core usb2_bluetooth usb2_bluetooth_fw + +# +# USB2 misc drivers +# +dev/usb2/misc/usb2_misc.c optional usb2_core usb2_misc +dev/usb2/misc/ufm2.c optional usb2_core usb2_misc usb2_misc_fm +dev/usb2/misc/udbp2.c optional usb2_core usb2_misc usb2_misc_dbp +# +# USB2 input drivers +# +dev/usb2/input/uhid2.c optional usb2_core usb2_input usb2_input_hid +dev/usb2/input/ukbd2.c optional usb2_core usb2_input usb2_input_kbd +dev/usb2/input/ums2.c optional usb2_core usb2_input usb2_input_ms +dev/usb2/input/usb2_input.c optional usb2_core usb2_input +# +# USB2 quirks +# +dev/usb2/quirk/usb2_quirk.c optional usb2_core usb2_quirk +# +# USB2 templates +# +dev/usb2/template/usb2_template.c optional usb2_core usb2_template +dev/usb2/template/usb2_template_cdce.c optional usb2_core usb2_template +dev/usb2/template/usb2_template_msc.c optional usb2_core usb2_template +dev/usb2/template/usb2_template_mtp.c optional usb2_core usb2_template +# +# USB2 image drivers +# +dev/usb2/image/usb2_image.c optional usb2_core usb2_image +dev/usb2/image/uscanner2.c optional usb2_core usb2_image usb2_scanner +# +# USB2 sound and MIDI drivers +# +dev/usb2/sound/usb2_sound.c optional usb2_core usb2_sound +dev/usb2/sound/uaudio2.c optional usb2_core usb2_sound +dev/usb2/sound/uaudio2_pcm.c optional usb2_core usb2_sound +# +# USB2 END +# dev/utopia/idtphy.c optional utopia dev/utopia/suni.c optional utopia dev/utopia/utopia.c optional utopia diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index ffc0ffed1649..464330ae521e 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -570,13 +570,26 @@ chn_read(struct pcm_channel *c, struct uio *buf) void chn_intr(struct pcm_channel *c) { - CHN_LOCK(c); + uint8_t do_unlock; + if (CHN_LOCK_OWNED(c)) { + /* + * Allow sound drivers to call this function with + * "CHN_LOCK()" locked: + */ + do_unlock = 0; + } else { + do_unlock = 1; + CHN_LOCK(c); + } c->interrupts++; if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); - CHN_UNLOCK(c); + if (do_unlock) { + CHN_UNLOCK(c); + } + return; } u_int32_t diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h index 65175a38db74..8f9f8f662d40 100644 --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -258,11 +258,13 @@ int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); #endif #ifdef USING_MUTEX +#define CHN_LOCK_OWNED(c) mtx_owned((struct mtx *)((c)->lock)) #define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) #define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) #define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock)) #define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED) #else +#define CHN_LOCK_OWNED(c) 0 #define CHN_LOCK(c) #define CHN_UNLOCK(c) #define CHN_TRYLOCK(c) diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index c3ca4f0dea47..dd3b7bb3663f 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -589,7 +589,7 @@ mixer_delete(struct snd_mixer *m) KASSERT(m->type == MIXER_TYPE_SECONDARY, ("%s(): illegal mixer type=%d", __func__, m->type)); - snd_mtxlock(m->lock); + /* mixer uninit can sleep --hps */ MIXER_UNINIT(m); @@ -704,14 +704,24 @@ mixer_uninit(device_t dev) return EBUSY; } + /* destroy dev can sleep --hps */ + + snd_mtxunlock(m->lock); + pdev->si_drv1 = NULL; destroy_dev(pdev); + snd_mtxlock(m->lock); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, 0); mixer_setrecsrc(m, SOUND_MASK_MIC); + snd_mtxunlock(m->lock); + + /* mixer uninit can sleep --hps */ + MIXER_UNINIT(m); snd_mtxfree(m->lock); @@ -1280,3 +1290,16 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) return (EINVAL); } + +/* + * Allow the sound driver to use the mixer lock to protect its mixer + * data: + */ +struct mtx * +mixer_get_lock(struct snd_mixer *m) +{ + if (m->lock == NULL) { + return (&Giant); + } + return (m->lock); +} diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h index bcded4bd529b..d03338b198f6 100644 --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -56,6 +56,7 @@ void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev); u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev); u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev); void *mix_getdevinfo(struct snd_mixer *m); +struct mtx *mixer_get_lock(struct snd_mixer *m); extern int mixer_count; diff --git a/sys/dev/usb2/bluetooth/TODO.TXT b/sys/dev/usb2/bluetooth/TODO.TXT new file mode 100644 index 000000000000..b0d6695ca9db --- /dev/null +++ b/sys/dev/usb2/bluetooth/TODO.TXT @@ -0,0 +1,18 @@ +$Id: TODO,v 1.1 2002/11/24 19:46:56 max Exp $ +$FreeBSD$ + +1) SMP/Locking + + The code makes use of ng_send_fn() whenever possible. Just + need to verify and make sure i did it right + +2) Firmware upgrade + + According to Bluetooth spec device may present third interface + to perform firmware upgrade. 3Com USB Bluetooth dongle has + such interface. Need to implement set of Netgraph messages. + +3) Isochronous USB transfers (SCO data) + + Tried to fix isochrounous transfers, which are still disabled + by default. diff --git a/sys/dev/usb2/bluetooth/ng_ubt2.c b/sys/dev/usb2/bluetooth/ng_ubt2.c new file mode 100644 index 000000000000..1522ac9d2999 --- /dev/null +++ b/sys/dev/usb2/bluetooth/ng_ubt2.c @@ -0,0 +1,1774 @@ +/* + * ng_ubt.c + */ + +/*- + * Copyright (c) 2001-2002 Maksim Yevmenkin + * 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. + * + * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * USB methods + */ + +static device_probe_t ubt_probe; +static device_attach_t ubt_attach; +static device_detach_t ubt_detach; + +static devclass_t ubt_devclass; + +static device_method_t ubt_methods[] = { + DEVMETHOD(device_probe, ubt_probe), + DEVMETHOD(device_attach, ubt_attach), + DEVMETHOD(device_detach, ubt_detach), + {0, 0} +}; + +static driver_t ubt_driver = { + .name = "ubt", + .methods = ubt_methods, + .size = sizeof(struct ubt_softc), +}; + +/* + * Netgraph methods + */ + +static ng_constructor_t ng_ubt_constructor; +static ng_shutdown_t ng_ubt_shutdown; +static ng_newhook_t ng_ubt_newhook; +static ng_connect_t ng_ubt_connect; +static ng_disconnect_t ng_ubt_disconnect; +static ng_rcvmsg_t ng_ubt_rcvmsg; +static ng_rcvdata_t ng_ubt_rcvdata; + +/* Queue length */ +static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = +{ + {"queue", &ng_parse_int32_type,}, + {"qlen", &ng_parse_int32_type,}, + {NULL,} +}; +static const struct ng_parse_type ng_ubt_node_qlen_type = { + &ng_parse_struct_type, + &ng_ubt_node_qlen_type_fields +}; + +/* Stat info */ +static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = +{ + {"pckts_recv", &ng_parse_uint32_type,}, + {"bytes_recv", &ng_parse_uint32_type,}, + {"pckts_sent", &ng_parse_uint32_type,}, + {"bytes_sent", &ng_parse_uint32_type,}, + {"oerrors", &ng_parse_uint32_type,}, + {"ierrors", &ng_parse_uint32_type,}, + {NULL,} +}; +static const struct ng_parse_type ng_ubt_node_stat_type = { + &ng_parse_struct_type, + &ng_ubt_node_stat_type_fields +}; + +/* Netgraph node command list */ +static const struct ng_cmdlist ng_ubt_cmdlist[] = { + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_SET_DEBUG, + "set_debug", + &ng_parse_uint16_type, + NULL + }, + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_GET_DEBUG, + "get_debug", + NULL, + &ng_parse_uint16_type + }, + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_SET_QLEN, + "set_qlen", + &ng_ubt_node_qlen_type, + NULL + }, + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_GET_QLEN, + "get_qlen", + &ng_ubt_node_qlen_type, + &ng_ubt_node_qlen_type + }, + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_GET_STAT, + "get_stat", + NULL, + &ng_ubt_node_stat_type + }, + { + NGM_UBT_COOKIE, + NGM_UBT_NODE_RESET_STAT, + "reset_stat", + NULL, + NULL + }, + {0,} +}; + +/* Netgraph node type */ +static struct ng_type typestruct = { + .version = NG_ABI_VERSION, + .name = NG_UBT_NODE_TYPE, + .constructor = ng_ubt_constructor, + .rcvmsg = ng_ubt_rcvmsg, + .shutdown = ng_ubt_shutdown, + .newhook = ng_ubt_newhook, + .connect = ng_ubt_connect, + .rcvdata = ng_ubt_rcvdata, + .disconnect = ng_ubt_disconnect, + .cmdlist = ng_ubt_cmdlist +}; + +/* USB methods */ + +static usb2_callback_t ubt_ctrl_write_callback; +static usb2_callback_t ubt_intr_read_callback; +static usb2_callback_t ubt_intr_read_clear_stall_callback; +static usb2_callback_t ubt_bulk_read_callback; +static usb2_callback_t ubt_bulk_read_clear_stall_callback; +static usb2_callback_t ubt_bulk_write_callback; +static usb2_callback_t ubt_bulk_write_clear_stall_callback; +static usb2_callback_t ubt_isoc_read_callback; +static usb2_callback_t ubt_isoc_write_callback; + +static int ubt_modevent(module_t mod, int event, void *data); +static void ubt_intr_read_complete(node_p node, hook_p hook, void *arg1, int arg2); +static void ubt_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2); +static void ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2); + +/* USB config */ +static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, + .mh.flags = {.pipe_bof = 1,}, + .mh.callback = &ubt_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ubt_bulk_read_callback, + }, + + [2] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0x110, /* bytes */ + .mh.callback = &ubt_intr_read_callback, + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + UBT_CTRL_BUFFER_SIZE), + .mh.callback = &ubt_ctrl_write_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [4] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [6] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_intr_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +/* USB config */ +static const struct usb2_config + ubt_config_if_1_full_speed[UBT_IF_1_N_TRANSFER] = { + + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_read_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_read_callback, + }, + + [2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_write_callback, + }, + + [3] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_write_callback, + }, +}; + +/* USB config */ +static const struct usb2_config + ubt_config_if_1_high_speed[UBT_IF_1_N_TRANSFER] = { + + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES * 8, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_read_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES * 8, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_read_callback, + }, + + [2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES * 8, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_write_callback, + }, + + [3] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES * 8, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ubt_isoc_write_callback, + }, +}; + +/* + * Module + */ + +DRIVER_MODULE(ng_ubt, ushub, ubt_driver, ubt_devclass, ubt_modevent, 0); +MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +MODULE_DEPEND(ng_ubt, usb2_bluetooth, 1, 1, 1); +MODULE_DEPEND(ng_ubt, usb2_core, 1, 1, 1); + +/**************************************************************************** + **************************************************************************** + ** USB specific + **************************************************************************** + ****************************************************************************/ + +/* + * Load/Unload the driver module + */ + +static int +ubt_modevent(module_t mod, int event, void *data) +{ + int error; + + switch (event) { + case MOD_LOAD: + error = ng_newtype(&typestruct); + if (error != 0) { + printf("%s: Could not register " + "Netgraph node type, error=%d\n", + NG_UBT_NODE_TYPE, error); + } + break; + + case MOD_UNLOAD: + error = ng_rmtype(&typestruct); + break; + + default: + error = EOPNOTSUPP; + break; + } + return (error); +} /* ubt_modevent */ + +/* + * If for some reason device should not be attached then put + * VendorID/ProductID pair into the list below. The format is + * as follows: + * + * { VENDOR_ID, PRODUCT_ID }, + * + * where VENDOR_ID and PRODUCT_ID are hex numbers. + */ +static const struct usb2_device_id ubt_ignore_devs[] = { + /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ + {USB_VPI(USB_VENDOR_AVM, 0x2200, 0)}, +}; + +/* List of supported bluetooth devices */ +static const struct usb2_device_id ubt_devs[] = { + /* Generic Bluetooth class devices. */ + {USB_IFACE_CLASS(UDCLASS_WIRELESS), + USB_IFACE_SUBCLASS(UDSUBCLASS_RF), + USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH)}, + + /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ + {USB_VPI(USB_VENDOR_AVM, 0x3800, 0)}, +}; + +/* + * Probe for a USB Bluetooth device + */ + +static int +ubt_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != 0) { + return (ENXIO); + } + if (usb2_lookup_id_by_uaa(ubt_ignore_devs, + sizeof(ubt_ignore_devs), uaa) == 0) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa)); +} + +/* + * Attach the device + */ + +static int +ubt_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubt_softc *sc = device_get_softc(dev); + const struct usb2_config *isoc_setup; + struct usb2_endpoint_descriptor *ed; + uint16_t wMaxPacketSize; + uint8_t alt_index; + uint8_t iface_index; + uint8_t i; + uint8_t j; + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + /* + * Initialize device softc structure + */ + + /* state */ + sc->sc_debug = NG_UBT_WARN_LEVEL; + sc->sc_flags = 0; + NG_UBT_STAT_RESET(sc->sc_stat); + + /* control pipe */ + NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); + + /* bulk-out pipe */ + NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); + + /* isoc-out pipe */ + NG_BT_MBUFQ_INIT(&sc->sc_scoq, + (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? + (2 * UBT_ISOC_NFRAMES * 8) : + (2 * UBT_ISOC_NFRAMES)); + + /* isoc-in pipe */ + NG_BT_MBUFQ_INIT(&sc->sc_sciq, + (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? + (2 * UBT_ISOC_NFRAMES * 8) : + (2 * UBT_ISOC_NFRAMES)); + + /* netgraph part */ + sc->sc_node = NULL; + sc->sc_hook = NULL; + + /* + * Configure Bluetooth USB device. Discover all required USB + * interfaces and endpoints. + * + * USB device must present two interfaces: + * 1) Interface 0 that has 3 endpoints + * 1) Interrupt endpoint to receive HCI events + * 2) Bulk IN endpoint to receive ACL data + * 3) Bulk OUT endpoint to send ACL data + * + * 2) Interface 1 then has 2 endpoints + * 1) Isochronous IN endpoint to receive SCO data + * 2) Isochronous OUT endpoint to send SCO data + * + * Interface 1 (with isochronous endpoints) has several alternate + * configurations with different packet size. + */ + + /* + * Interface 0 + */ + + mtx_init(&sc->sc_mtx, "ubt lock", NULL, MTX_DEF | MTX_RECURSE); + + iface_index = 0; + if (usb2_transfer_setup + (uaa->device, &iface_index, sc->sc_xfer_if_0, ubt_config_if_0, + UBT_IF_0_N_TRANSFER, sc, &sc->sc_mtx)) { + device_printf(dev, "Could not allocate transfers " + "for interface 0!\n"); + goto detach; + } + /* + * Interface 1 + * (search alternate settings, and find + * the descriptor with the largest + * wMaxPacketSize) + */ + isoc_setup = + ((usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? + ubt_config_if_1_high_speed : + ubt_config_if_1_full_speed); + + wMaxPacketSize = 0; + + /* search through all the descriptors looking for bidir mode */ + + alt_index = 0 - 1; + i = 0; + j = 0; + while (1) { + uint16_t temp; + + ed = usb2_find_edesc( + usb2_get_config_descriptor(uaa->device), 1, i, j); + if (ed == NULL) { + if (j == 0) { + /* end of interfaces */ + break; + } else { + /* next interface */ + j = 0; + i++; + continue; + } + } + temp = UGETW(ed->wMaxPacketSize); + if (temp > wMaxPacketSize) { + wMaxPacketSize = temp; + alt_index = i; + } + j++; + } + + if (usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { + device_printf(dev, "Could not set alternate " + "setting %d for interface 1!\n", alt_index); + goto detach; + } + iface_index = 1; + if (usb2_transfer_setup + (uaa->device, &iface_index, sc->sc_xfer_if_1, + isoc_setup, UBT_IF_1_N_TRANSFER, sc, &sc->sc_mtx)) { + device_printf(dev, "Could not allocate transfers " + "for interface 1!\n"); + goto detach; + } + /* create Netgraph node */ + + if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { + printf("%s: Could not create Netgraph node\n", + sc->sc_name); + sc->sc_node = NULL; + goto detach; + } + /* name node */ + + if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { + printf("%s: Could not name Netgraph node\n", + sc->sc_name); + NG_NODE_UNREF(sc->sc_node); + sc->sc_node = NULL; + goto detach; + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); + + /* claim all interfaces on the device */ + + for (i = 1;; i++) { + + if (usb2_get_iface(uaa->device, i) == NULL) { + break; + } + usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + } + + return (0); /* success */ + +detach: + ubt_detach(dev); + + return (ENXIO); +} + +/* + * Detach the device + */ + +int +ubt_detach(device_t dev) +{ + struct ubt_softc *sc = device_get_softc(dev); + + /* destroy Netgraph node */ + + if (sc->sc_node != NULL) { + NG_NODE_SET_PRIVATE(sc->sc_node, NULL); + ng_rmnode_self(sc->sc_node); + sc->sc_node = NULL; + } + /* free USB transfers, if any */ + + usb2_transfer_unsetup(sc->sc_xfer_if_0, UBT_IF_0_N_TRANSFER); + + usb2_transfer_unsetup(sc->sc_xfer_if_1, UBT_IF_1_N_TRANSFER); + + mtx_destroy(&sc->sc_mtx); + + /* destroy queues */ + + NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); + NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); + NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); + NG_BT_MBUFQ_DESTROY(&sc->sc_sciq); + + return (0); +} + +static void +ubt_ctrl_write_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + struct mbuf *m; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + + if (xfer->error) { + NG_UBT_STAT_OERROR(sc->sc_stat); + } else { + NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); + NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); + } + + case USB_ST_SETUP: + + /* get next mbuf, if any */ + + NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); + + if (m == NULL) { + NG_UBT_INFO(sc, "HCI command queue is empty\n"); + return; + } + /* + * check HCI command frame size and + * copy it to USB transfer buffer: + */ + + if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE) { + panic("HCI command frame too big, size=%zd, len=%d\n", + UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); + } + /* initialize a USB control request and then schedule it */ + + bzero(&req, sizeof(req)); + + req.bmRequestType = UBT_HCI_REQUEST; + USETW(req.wLength, m->m_pkthdr.len); + + NG_UBT_INFO(sc, "Sending control request, bmRequestType=0x%02x, " + "wLength=%d\n", req.bmRequestType, UGETW(req.wLength)); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = m->m_pkthdr.len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + + NG_FREE_M(m); + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + /* ignore */ + return; + } + goto tr_transferred; + } +} + +static void +ubt_intr_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + uint32_t max_len; + uint8_t *ptr; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* allocate a new mbuf */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) { + goto tr_setup; + } + MCLGET(m, M_DONTWAIT); + + if (!(m->m_flags & M_EXT)) { + NG_FREE_M(m); + goto tr_setup; + } + if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { + *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; + m->m_pkthdr.len = m->m_len = 1; + } else { + m->m_pkthdr.len = m->m_len = 0; + } + + max_len = (MCLBYTES - m->m_len); + + if (xfer->actlen > max_len) { + xfer->actlen = max_len; + } + ptr = ((uint8_t *)(m->m_data)) + m->m_len; + + usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + + m->m_pkthdr.len += xfer->actlen; + m->m_len += xfer->actlen; + + NG_UBT_INFO(sc, "got %d bytes from interrupt " + "pipe\n", xfer->actlen); + + sc->sc_intr_buffer = m; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_intr_buffer) { + ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0); + return; + } + if (sc->sc_flags & UBT_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer_if_0[6]); + return; + } + xfer->frlengths[0] = xfer->max_data_length; + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UBT_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer_if_0[6]); + } + return; + + } +} + +static void +ubt_intr_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[2]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBT_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubt_intr_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct mbuf *m; + ng_hci_event_pkt_t *hdr; + int error; + + if (sc == NULL) { + return; + } + mtx_lock(&sc->sc_mtx); + + m = sc->sc_intr_buffer; + + if (m) { + + sc->sc_intr_buffer = NULL; + + hdr = mtod(m, ng_hci_event_pkt_t *); + + if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { + NG_UBT_INFO(sc, "No upstream hook\n"); + goto done; + } + NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + + if (m->m_pkthdr.len < sizeof(*hdr)) { + NG_UBT_INFO(sc, "Packet too short\n"); + goto done; + } + if (hdr->length == (m->m_pkthdr.len - sizeof(*hdr))) { + NG_UBT_INFO(sc, "Got complete HCI event frame, " + "pktlen=%d, length=%d\n", + m->m_pkthdr.len, hdr->length); + + NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); + + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + + m = NULL; + + if (error != 0) { + NG_UBT_STAT_IERROR(sc->sc_stat); + } + } else { + NG_UBT_ERR(sc, "Invalid HCI event frame size, " + "length=%d, pktlen=%d\n", + hdr->length, m->m_pkthdr.len); + + NG_UBT_STAT_IERROR(sc->sc_stat); + } + } +done: + if (m) { + NG_FREE_M(m); + } + /* start USB transfer if not already started */ + + usb2_transfer_start(sc->sc_xfer_if_0[2]); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ubt_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + uint32_t max_len; + uint8_t *ptr; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* allocate new mbuf */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) { + goto tr_setup; + } + MCLGET(m, M_DONTWAIT); + + if (!(m->m_flags & M_EXT)) { + NG_FREE_M(m); + goto tr_setup; + } + if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { + *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; + m->m_pkthdr.len = m->m_len = 1; + } else { + m->m_pkthdr.len = m->m_len = 0; + } + + max_len = (MCLBYTES - m->m_len); + + if (xfer->actlen > max_len) { + xfer->actlen = max_len; + } + ptr = ((uint8_t *)(m->m_data)) + m->m_len; + + usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + + m->m_pkthdr.len += xfer->actlen; + m->m_len += xfer->actlen; + + NG_UBT_INFO(sc, "got %d bytes from bulk-in " + "pipe\n", xfer->actlen); + + sc->sc_bulk_in_buffer = m; + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_bulk_in_buffer) { + ng_send_fn(sc->sc_node, NULL, ubt_bulk_read_complete, NULL, 0); + return; + } + if (sc->sc_flags & UBT_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer_if_0[5]); + return; + } + xfer->frlengths[0] = xfer->max_data_length; + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UBT_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer_if_0[5]); + } + return; + + } +} + +static void +ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBT_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubt_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct mbuf *m; + ng_hci_acldata_pkt_t *hdr; + uint16_t len; + int error; + + if (sc == NULL) { + return; + } + mtx_lock(&sc->sc_mtx); + + m = sc->sc_bulk_in_buffer; + + if (m) { + + sc->sc_bulk_in_buffer = NULL; + + hdr = mtod(m, ng_hci_acldata_pkt_t *); + + if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { + NG_UBT_INFO(sc, "No upstream hook\n"); + goto done; + } + NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + + if (m->m_pkthdr.len < sizeof(*hdr)) { + NG_UBT_INFO(sc, "Packet too short\n"); + goto done; + } + len = le16toh(hdr->length); + + if (len == (m->m_pkthdr.len - sizeof(*hdr))) { + NG_UBT_INFO(sc, "Got complete ACL data frame, " + "pktlen=%d, length=%d\n", + m->m_pkthdr.len, len); + + NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); + + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + + m = NULL; + + if (error != 0) { + NG_UBT_STAT_IERROR(sc->sc_stat); + } + } else { + NG_UBT_ERR(sc, "Invalid ACL frame size, " + "length=%d, pktlen=%d\n", + len, m->m_pkthdr.len); + + NG_UBT_STAT_IERROR(sc->sc_stat); + } + } +done: + if (m) { + NG_FREE_M(m); + } + /* start USB transfer if not already started */ + + usb2_transfer_start(sc->sc_xfer_if_0[1]); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ubt_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + NG_UBT_INFO(sc, "sent %d bytes to bulk-out " + "pipe\n", xfer->actlen); + NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); + NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); + + case USB_ST_SETUP: + if (sc->sc_flags & UBT_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer_if_0[4]); + return; + } + /* get next mbuf, if any */ + + NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); + + if (m == NULL) { + NG_UBT_INFO(sc, "ACL data queue is empty\n"); + return; + } + /* + * check ACL data frame size and + * copy it back to a linear USB + * transfer buffer: + */ + + if (m->m_pkthdr.len > UBT_BULK_WRITE_BUFFER_SIZE) { + panic("ACL data frame too big, size=%d, len=%d\n", + UBT_BULK_WRITE_BUFFER_SIZE, + m->m_pkthdr.len); + } + usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); + + NG_UBT_INFO(sc, "bulk-out transfer has been started, " + "len=%d\n", m->m_pkthdr.len); + + xfer->frlengths[0] = m->m_pkthdr.len; + + NG_FREE_M(m); + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + + NG_UBT_WARN(sc, "bulk-out transfer failed: %s\n", + usb2_errstr(xfer->error)); + + NG_UBT_STAT_OERROR(sc->sc_stat); + + /* try to clear stall first */ + sc->sc_flags |= UBT_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer_if_0[4]); + } + return; + + } +} + +static void +ubt_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBT_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubt_isoc_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + ng_hci_scodata_pkt_t hdr; + struct mbuf *m; + uint8_t *ptr; + uint32_t max_len; + uint32_t n; + uint32_t offset; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + offset = 0; + + for (n = 0; n < xfer->nframes; n++) { + + if (xfer->frlengths[n] >= sizeof(hdr)) { + + usb2_copy_out(xfer->frbuffers, offset, + &hdr, sizeof(hdr)); + + if (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) { + + NG_UBT_INFO(sc, "got complete SCO data " + "frame, length=%d\n", hdr.length); + + if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) { + + /* allocate a new mbuf */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) { + goto tr_setup; + } + MCLGET(m, M_DONTWAIT); + + if (!(m->m_flags & M_EXT)) { + NG_FREE_M(m); + goto tr_setup; + } + /* + * fix SCO data frame header + * if required + */ + + if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { + *mtod(m, uint8_t *)= NG_HCI_SCO_DATA_PKT; + m->m_pkthdr.len = m->m_len = 1; + } else { + m->m_pkthdr.len = m->m_len = 0; + } + + max_len = (MCLBYTES - m->m_len); + + if (xfer->frlengths[n] > max_len) { + xfer->frlengths[n] = max_len; + } + ptr = ((uint8_t *)(m->m_data)) + m->m_len; + + usb2_copy_out + (xfer->frbuffers, offset, + ptr, xfer->frlengths[n]); + + m->m_pkthdr.len += xfer->frlengths[n]; + m->m_len += xfer->frlengths[n]; + + NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m); + } + } + } + offset += xfer->max_frame_size; + } + + case USB_ST_SETUP: +tr_setup: + + if (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) { + ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0); + } + for (n = 0; n < xfer->nframes; n++) { + xfer->frlengths[n] = xfer->max_frame_size; + } + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + /* ignore */ + return; + } + goto tr_transferred; + } +} + +static void +ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = NG_NODE_PRIVATE(node); + struct mbuf *m; + int error; + + if (sc == NULL) { + return; + } + mtx_lock(&sc->sc_mtx); + + while (1) { + + NG_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m); + + if (m == NULL) { + break; + } + if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { + NG_UBT_INFO(sc, "No upstream hook\n"); + goto done; + } + NG_UBT_INFO(sc, "Got complete SCO data frame, " + "pktlen=%d bytes\n", m->m_pkthdr.len); + + NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); + NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + + m = NULL; + + if (error) { + NG_UBT_STAT_IERROR(sc->sc_stat); + } +done: + if (m) { + NG_FREE_M(m); + } + } + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ubt_isoc_write_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + uint32_t n; + uint32_t len; + uint32_t offset; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + if (xfer->error) { + NG_UBT_STAT_OERROR(sc->sc_stat); + } else { + NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); + NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); + } + + case USB_ST_SETUP: + offset = 0; + + for (n = 0; n < xfer->nframes; n++) { + + NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + + if (m) { + len = min(xfer->max_frame_size, m->m_pkthdr.len); + + usb2_m_copy_in(xfer->frbuffers, offset, m, 0, len); + + NG_FREE_M(m); + + xfer->frlengths[n] = len; + offset += len; + } else { + xfer->frlengths[n] = 0; + } + } + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + /* ignore */ + return; + } + goto tr_transferred; + } +} + +/**************************************************************************** + **************************************************************************** + ** Netgraph specific + **************************************************************************** + ****************************************************************************/ + +/* + * Netgraph node constructor. + * Do not allow to create node of this type: + */ + +static int +ng_ubt_constructor(node_p node) +{ + return (EINVAL); +} + +/* + * Netgraph node destructor. + * Destroy node only when device has been detached: + */ + +static int +ng_ubt_shutdown(node_p node) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + + /* Let old node go */ + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + + if (sc == NULL) { + goto done; + } + mtx_lock(&sc->sc_mtx); + + /* Create Netgraph node */ + if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { + printf("%s: Could not create Netgraph node\n", + sc->sc_name); + sc->sc_node = NULL; + goto done; + } + /* Name node */ + if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { + printf("%s: Could not name Netgraph node\n", + sc->sc_name); + NG_NODE_UNREF(sc->sc_node); + sc->sc_node = NULL; + goto done; + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); + +done: + if (sc) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +/* + * Create new hook. + * There can only be one. + */ + +static int +ng_ubt_newhook(node_p node, hook_p hook, char const *name) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + int error = 0; + + if (strcmp(name, NG_UBT_HOOK) != 0) { + return (EINVAL); + } + mtx_lock(&sc->sc_mtx); + + if (sc->sc_hook != NULL) { + error = EISCONN; + } else { + sc->sc_hook = hook; + } + + mtx_unlock(&sc->sc_mtx); + + return (error); +} + +/* + * Connect hook. + * Start incoming USB transfers + */ + +static int +ng_ubt_connect(hook_p hook) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= (UBT_FLAG_READ_STALL | + UBT_FLAG_WRITE_STALL | + UBT_FLAG_INTR_STALL); + + /* start intr transfer */ + usb2_transfer_start(sc->sc_xfer_if_0[2]); + + /* start bulk-in transfer */ + usb2_transfer_start(sc->sc_xfer_if_0[1]); + + /* start bulk-out transfer */ + usb2_transfer_start(sc->sc_xfer_if_0[0]); + + /* start control-out transfer */ + usb2_transfer_start(sc->sc_xfer_if_0[3]); +#if 0 + XXX can enable this XXX + + /* start isoc-in transfer */ + usb2_transfer_start(sc->sc_xfer_if_1[0]); + + usb2_transfer_start(sc->sc_xfer_if_1[1]); + + /* start isoc-out transfer */ + usb2_transfer_start(sc->sc_xfer_if_1[2]); + usb2_transfer_start(sc->sc_xfer_if_1[3]); +#endif + + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +/* + * Disconnect hook + */ + +static int +ng_ubt_disconnect(hook_p hook) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error = 0; + + if (sc != NULL) { + + mtx_lock(&sc->sc_mtx); + + if (hook != sc->sc_hook) { + error = EINVAL; + } else { + + /* stop intr transfer */ + usb2_transfer_stop(sc->sc_xfer_if_0[2]); + usb2_transfer_stop(sc->sc_xfer_if_0[6]); + + /* stop bulk-in transfer */ + usb2_transfer_stop(sc->sc_xfer_if_0[1]); + usb2_transfer_stop(sc->sc_xfer_if_0[5]); + + /* stop bulk-out transfer */ + usb2_transfer_stop(sc->sc_xfer_if_0[0]); + usb2_transfer_stop(sc->sc_xfer_if_0[4]); + + /* stop control transfer */ + usb2_transfer_stop(sc->sc_xfer_if_0[3]); + + /* stop isoc-in transfer */ + usb2_transfer_stop(sc->sc_xfer_if_1[0]); + usb2_transfer_stop(sc->sc_xfer_if_1[1]); + + /* stop isoc-out transfer */ + usb2_transfer_stop(sc->sc_xfer_if_1[2]); + usb2_transfer_stop(sc->sc_xfer_if_1[3]); + + /* cleanup queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); + NG_BT_MBUFQ_DRAIN(&sc->sc_sciq); + + sc->sc_hook = NULL; + } + + mtx_unlock(&sc->sc_mtx); + } + return (error); +} + +/* + * Process control message + */ + +static int +ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct ng_mesg *msg = NULL, *rsp = NULL; + struct ng_bt_mbufq *q = NULL; + int error = 0, queue, qlen; + + if (sc == NULL) { + NG_FREE_ITEM(item); + return (EHOSTDOWN); + } + mtx_lock(&sc->sc_mtx); + + NGI_GET_MSG(item, msg); + + switch (msg->header.typecookie) { + case NGM_GENERIC_COOKIE: + switch (msg->header.cmd) { + case NGM_TEXT_STATUS: + NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); + if (rsp == NULL) + error = ENOMEM; + else + snprintf(rsp->data, NG_TEXTRESPONSE, + "Hook: %s\n" \ + "Flags: %#x\n" \ + "Debug: %d\n" \ + "CMD queue: [have:%d,max:%d]\n" \ + "ACL queue: [have:%d,max:%d]\n" \ + "SCO queue: [have:%d,max:%d]", + (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", + sc->sc_flags, + sc->sc_debug, + NG_BT_MBUFQ_LEN(&sc->sc_cmdq), + sc->sc_cmdq.maxlen, + NG_BT_MBUFQ_LEN(&sc->sc_aclq), + sc->sc_aclq.maxlen, + NG_BT_MBUFQ_LEN(&sc->sc_scoq), + sc->sc_scoq.maxlen); + break; + + default: + error = EINVAL; + break; + } + break; + + case NGM_UBT_COOKIE: + switch (msg->header.cmd) { + case NGM_UBT_NODE_SET_DEBUG: + if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)) + error = EMSGSIZE; + else + sc->sc_debug = + *((ng_ubt_node_debug_ep *) (msg->data)); + break; + + case NGM_UBT_NODE_GET_DEBUG: + NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), + M_NOWAIT); + if (rsp == NULL) + error = ENOMEM; + else + *((ng_ubt_node_debug_ep *) (rsp->data)) = + sc->sc_debug; + break; + + case NGM_UBT_NODE_SET_QLEN: + if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) + error = EMSGSIZE; + else { + queue = ((ng_ubt_node_qlen_ep *) + (msg->data))->queue; + qlen = ((ng_ubt_node_qlen_ep *) + (msg->data))->qlen; + + if (qlen <= 0) { + error = EINVAL; + break; + } + switch (queue) { + case NGM_UBT_NODE_QUEUE_CMD: + q = &sc->sc_cmdq; + break; + + case NGM_UBT_NODE_QUEUE_ACL: + q = &sc->sc_aclq; + break; + + case NGM_UBT_NODE_QUEUE_SCO: + q = &sc->sc_scoq; + break; + + default: + q = NULL; + error = EINVAL; + break; + } + + if (q != NULL) { + q->maxlen = qlen; + } + } + break; + + case NGM_UBT_NODE_GET_QLEN: + if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { + error = EMSGSIZE; + break; + } + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + switch (queue) { + case NGM_UBT_NODE_QUEUE_CMD: + q = &sc->sc_cmdq; + break; + + case NGM_UBT_NODE_QUEUE_ACL: + q = &sc->sc_aclq; + break; + + case NGM_UBT_NODE_QUEUE_SCO: + q = &sc->sc_scoq; + break; + + default: + q = NULL; + error = EINVAL; + break; + } + + if (q != NULL) { + NG_MKRESPONSE(rsp, msg, + sizeof(ng_ubt_node_qlen_ep), M_NOWAIT); + if (rsp == NULL) { + error = ENOMEM; + break; + } + ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = + queue; + ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = + q->maxlen; + } + break; + + case NGM_UBT_NODE_GET_STAT: + NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), + M_NOWAIT); + if (rsp == NULL) + error = ENOMEM; + else { + bcopy(&sc->sc_stat, rsp->data, + sizeof(ng_ubt_node_stat_ep)); + } + break; + + case NGM_UBT_NODE_RESET_STAT: + + NG_UBT_STAT_RESET(sc->sc_stat); + break; + + default: + error = EINVAL; + break; + } + break; + + default: + error = EINVAL; + break; + } + + NG_RESPOND_MSG(error, node, item, rsp); + NG_FREE_MSG(msg); + + mtx_unlock(&sc->sc_mtx); + + return (error); +} + +/* + * Process data + */ + +static int +ng_ubt_rcvdata(hook_p hook, item_p item) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + struct ng_bt_mbufq *q; + struct usb2_xfer *xfer; + int error = 0; + + if (sc == NULL) { + error = EHOSTDOWN; + goto done; + } + mtx_lock(&sc->sc_mtx); + + if (hook != sc->sc_hook) { + error = EINVAL; + goto done; + } + /* deatch mbuf and get HCI frame type */ + NGI_GET_M(item, m); + + /* process HCI frame */ + switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ + case NG_HCI_CMD_PKT: + xfer = sc->sc_xfer_if_0[3]; + q = &sc->sc_cmdq; + break; + + case NG_HCI_ACL_DATA_PKT: + xfer = sc->sc_xfer_if_0[0]; + q = &sc->sc_aclq; + break; + + case NG_HCI_SCO_DATA_PKT: + xfer = NULL; + q = &sc->sc_scoq; + break; + + default: + NG_UBT_ERR(sc, "Dropping unsupported HCI frame, " + "type=0x%02x, pktlen=%d\n", + *mtod(m, uint8_t *), + m->m_pkthdr.len); + + NG_FREE_M(m); + error = EINVAL; + goto done; + } + + /* loose frame type, if required */ + if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE)) { + m_adj(m, sizeof(uint8_t)); + } + if (NG_BT_MBUFQ_FULL(q)) { + NG_UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. " + "Queue full\n", *mtod(m, uint8_t *), + m->m_pkthdr.len); + NG_FREE_M(m); + } else { + NG_BT_MBUFQ_ENQUEUE(q, m); + } + + if (xfer) { + usb2_transfer_start(xfer); + } +done: + NG_FREE_ITEM(item); + + if (sc) { + mtx_unlock(&sc->sc_mtx); + } + return (error); +} diff --git a/sys/dev/usb2/bluetooth/ng_ubt2_var.h b/sys/dev/usb2/bluetooth/ng_ubt2_var.h new file mode 100644 index 000000000000..8fecc41be8a9 --- /dev/null +++ b/sys/dev/usb2/bluetooth/ng_ubt2_var.h @@ -0,0 +1,126 @@ +/* + * ng_ubt_var.h + */ + +/*- + * Copyright (c) 2001-2002 Maksim Yevmenkin + * 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. + * + * $Id: ng_ubt_var.h,v 1.2 2003/03/22 23:44:36 max Exp $ + * $FreeBSD$ + */ + +#ifndef _NG_UBT_VAR_H_ +#define _NG_UBT_VAR_H_ + +/* pullup wrapper */ +#define NG_UBT_M_PULLUP(m, s) \ + do { \ + if ((m)->m_len < (s)) \ + (m) = m_pullup((m), (s)); \ + if ((m) == NULL) { \ + NG_UBT_ALERT("%s: %s - m_pullup(%d) failed\n", \ + __func__, sc->sc_name, (s)); \ + } \ + } while (0) + +/* Debug printf's */ +#define NG_UBT_DEBUG(level, sc, fmt, ...) do { \ + if ((sc)->sc_debug >= (level)) { \ + printf("%s:%s:%d: " fmt, (sc)->sc_name, \ + __FUNCTION__, __LINE__,## __VA_ARGS__); \ + } \ +} while (0) + +#define NG_UBT_ALERT(...) NG_UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__) +#define NG_UBT_ERR(...) NG_UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__) +#define NG_UBT_WARN(...) NG_UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__) +#define NG_UBT_INFO(...) NG_UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__) + +/* Bluetooth USB control request type */ +#define UBT_HCI_REQUEST 0x20 +#define UBT_DEFAULT_QLEN 12 + +/* Bluetooth USB defines */ +#define UBT_IF_0_N_TRANSFER 7 /* units */ +#define UBT_IF_1_N_TRANSFER 4 /* units */ +#define UBT_ISOC_NFRAMES 25 /* units */ + +/* USB device softc structure */ +struct ubt_softc { + /* State */ + ng_ubt_node_debug_ep sc_debug; /* debug level */ + uint32_t sc_flags; /* device flags */ +#define UBT_NEED_FRAME_TYPE (1 << 0)/* device required frame type */ +#define UBT_HAVE_FRAME_TYPE UBT_NEED_FRAME_TYPE +#define UBT_FLAG_READ_STALL (1 << 1)/* read transfer has stalled */ +#define UBT_FLAG_WRITE_STALL (1 << 2)/* write transfer has stalled */ +#define UBT_FLAG_INTR_STALL (1 << 3)/* interrupt transfer has stalled */ + + ng_ubt_node_stat_ep sc_stat; /* statistic */ +#define NG_UBT_STAT_PCKTS_SENT(s) (s).pckts_sent ++ +#define NG_UBT_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n) +#define NG_UBT_STAT_PCKTS_RECV(s) (s).pckts_recv ++ +#define NG_UBT_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n) +#define NG_UBT_STAT_OERROR(s) (s).oerrors ++ +#define NG_UBT_STAT_IERROR(s) (s).ierrors ++ +#define NG_UBT_STAT_RESET(s) bzero(&(s), sizeof((s))) + + uint8_t sc_name[16]; + + struct mtx sc_mtx; + + /* USB device specific */ + struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER]; + struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER]; + + /* Interrupt pipe (HCI events) */ + struct mbuf *sc_intr_buffer; /* interrupt buffer */ + + /* Control pipe (HCI commands) */ + struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ +#define UBT_CTRL_BUFFER_SIZE (sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) + + /* Bulk in pipe (ACL data) */ + struct mbuf *sc_bulk_in_buffer; /* bulk-in buffer */ + + /* Bulk out pipe (ACL data) */ + struct ng_bt_mbufq sc_aclq; /* ACL data queue */ +#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve one byte for ID-tag */ +#define UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES) + + /* Isoc. out pipe (ACL data) */ + struct ng_bt_mbufq sc_scoq; /* SCO data queue */ + + /* Isoc. in pipe (ACL data) */ + struct ng_bt_mbufq sc_sciq; /* SCO data queue */ + + /* Netgraph specific */ + node_p sc_node; /* pointer back to node */ + hook_p sc_hook; /* upstream hook */ +}; +typedef struct ubt_softc ubt_softc_t; +typedef struct ubt_softc *ubt_softc_p; + +#endif /* ndef _NG_UBT_VAR_H_ */ diff --git a/sys/dev/usb2/bluetooth/ubtbcmfw2.c b/sys/dev/usb2/bluetooth/ubtbcmfw2.c new file mode 100644 index 000000000000..440b5cda6566 --- /dev/null +++ b/sys/dev/usb2/bluetooth/ubtbcmfw2.c @@ -0,0 +1,448 @@ +/* + * ubtbcmfw.c + */ + +/*- + * Copyright (c) 2003 Maksim Yevmenkin + * 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. + * + * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Download firmware to BCM2033. + */ + +#define UBTBCMFW_CONFIG_NO 1 /* Config number */ +#define UBTBCMFW_IFACE_IDX 0 /* Control interface */ +#define UBTBCMFW_T_MAX 4 /* units */ + +struct ubtbcmfw_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[UBTBCMFW_T_MAX]; + + uint8_t sc_flags; +#define UBTBCMFW_FLAG_WRITE_STALL 0x01 +#define UBTBCMFW_FLAG_READ_STALL 0x02 +}; + +#define UBTBCMFW_BSIZE 1024 +#define UBTBCMFW_IFQ_MAXLEN 2 + +/* prototypes */ + +static device_probe_t ubtbcmfw_probe; +static device_attach_t ubtbcmfw_attach; +static device_detach_t ubtbcmfw_detach; + +static usb2_callback_t ubtbcmfw_write_callback; +static usb2_callback_t ubtbcmfw_write_clear_stall_callback; +static usb2_callback_t ubtbcmfw_read_callback; +static usb2_callback_t ubtbcmfw_read_clear_stall_callback; + +static usb2_fifo_close_t ubtbcmfw_close; +static usb2_fifo_cmd_t ubtbcmfw_start_read; +static usb2_fifo_cmd_t ubtbcmfw_start_write; +static usb2_fifo_cmd_t ubtbcmfw_stop_read; +static usb2_fifo_cmd_t ubtbcmfw_stop_write; +static usb2_fifo_ioctl_t ubtbcmfw_ioctl; +static usb2_fifo_open_t ubtbcmfw_open; + +static struct usb2_fifo_methods ubtbcmfw_fifo_methods = { + .f_close = &ubtbcmfw_close, + .f_ioctl = &ubtbcmfw_ioctl, + .f_open = &ubtbcmfw_open, + .f_start_read = &ubtbcmfw_start_read, + .f_start_write = &ubtbcmfw_start_write, + .f_stop_read = &ubtbcmfw_stop_read, + .f_stop_write = &ubtbcmfw_stop_write, + .basename[0] = "ubtbcmfw", + .basename[1] = "ubtbcmfw", + .basename[2] = "ubtbcmfw", + .postfix[0] = "", + .postfix[1] = ".1", + .postfix[2] = ".2", +}; + +static const struct usb2_config ubtbcmfw_config[UBTBCMFW_T_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = 0x02, /* fixed */ + .direction = UE_DIR_OUT, + .mh.bufsize = UBTBCMFW_BSIZE, + .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,}, + .mh.callback = &ubtbcmfw_write_callback, + }, + + [1] = { + .type = UE_INTERRUPT, + .endpoint = 0x01, /* fixed */ + .direction = UE_DIR_IN, + .mh.bufsize = UBTBCMFW_BSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, + .mh.callback = &ubtbcmfw_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ubtbcmfw_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ubtbcmfw_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +/* + * Module + */ + +static devclass_t ubtbcmfw_devclass; + +static device_method_t ubtbcmfw_methods[] = { + DEVMETHOD(device_probe, ubtbcmfw_probe), + DEVMETHOD(device_attach, ubtbcmfw_attach), + DEVMETHOD(device_detach, ubtbcmfw_detach), + {0, 0} +}; + +static driver_t ubtbcmfw_driver = { + .name = "ubtbcmfw", + .methods = ubtbcmfw_methods, + .size = sizeof(struct ubtbcmfw_softc), +}; + +DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0); +MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1); +MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1); + +/* + * Probe for a USB Bluetooth device + */ + +static int +ubtbcmfw_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != 0) + return (ENXIO); + + /* Match the boot device. */ + if (uaa->info.idVendor == USB_VENDOR_BROADCOM && + uaa->info.idProduct == USB_PRODUCT_BROADCOM_BCM2033) + return (0); + + return (ENXIO); +} + +/* + * Attach the device + */ + +static int +ubtbcmfw_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubtbcmfw_softc *sc = device_get_softc(dev); + int32_t err; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE); + + iface_index = UBTBCMFW_IFACE_IDX; + err = usb2_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, ubtbcmfw_config, + UBTBCMFW_T_MAX, sc, &sc->sc_mtx); + if (err) { + device_printf(dev, "allocating USB transfers " + "failed, err=%s\n", usb2_errstr(err)); + goto detach; + } + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &ubtbcmfw_fifo_methods, &sc->sc_fifo, + device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); + if (err) { + goto detach; + } + return (0); /* success */ + +detach: + ubtbcmfw_detach(dev); + return (ENOMEM); /* failure */ +} + +/* + * Detach the device + */ + +static int +ubtbcmfw_detach(device_t dev) +{ + struct ubtbcmfw_softc *sc = device_get_softc(dev); + + usb2_fifo_detach(&sc->sc_fifo); + + usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_T_MAX); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +ubtbcmfw_write_callback(struct usb2_xfer *xfer) +{ + struct ubtbcmfw_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: + if (sc->sc_flags & UBTBCMFW_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_fifo_get_data(f, xfer->frbuffers, 0, + UBTBCMFW_BSIZE, &actlen, 0)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + } +} + +static void +ubtbcmfw_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubtbcmfw_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBTBCMFW_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubtbcmfw_read_callback(struct usb2_xfer *xfer) +{ + struct ubtbcmfw_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_fifo_put_data(f, xfer->frbuffers, + 0, xfer->actlen, 1); + + case USB_ST_SETUP: + if (sc->sc_flags & UBTBCMFW_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + return; + } + if (usb2_fifo_put_bytes_max(f) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UBTBCMFW_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + } +} + +static void +ubtbcmfw_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubtbcmfw_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBTBCMFW_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubtbcmfw_start_read(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +ubtbcmfw_stop_read(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +ubtbcmfw_start_write(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ubtbcmfw_stop_write(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static int +ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + if (fflags & FREAD) { + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[1]->max_data_length, + UBTBCMFW_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + if (fflags & FWRITE) { + /* clear stall first */ + mtx_lock(&sc->sc_mtx); + sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; + mtx_unlock(&sc->sc_mtx); + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[0]->max_data_length, + UBTBCMFW_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + return (0); +} + +static void +ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & (FREAD | FWRITE)) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +static int +ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, + int fflags, struct thread *td) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + int error = 0; + + switch (cmd) { + case USB_GET_DEVICE_DESC: + *(struct usb2_device_descriptor *)data = + *usb2_get_device_descriptor(sc->sc_udev); + break; + + default: + error = EINVAL; + break; + } + return (error); +} diff --git a/sys/dev/usb2/bluetooth/usb2_bluetooth.c b/sys/dev/usb2/bluetooth/usb2_bluetooth.c new file mode 100644 index 000000000000..a8c9f545c994 --- /dev/null +++ b/sys/dev/usb2/bluetooth/usb2_bluetooth.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_bluetooth, 1); +MODULE_DEPEND(usb2_bluetooth, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/bluetooth/usb2_bluetooth.h b/sys/dev/usb2/bluetooth/usb2_bluetooth.h new file mode 100644 index 000000000000..b4b1761506ed --- /dev/null +++ b/sys/dev/usb2/bluetooth/usb2_bluetooth.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_BLUETOOTH_H_ +#define _USB2_BLUETOOTH_H_ + +#endif /* _USB2_BLUETOOTH_H_ */ diff --git a/sys/dev/usb2/controller/at91dci.c b/sys/dev/usb2/controller/at91dci.c new file mode 100644 index 000000000000..d5c7508ec517 --- /dev/null +++ b/sys/dev/usb2/controller/at91dci.c @@ -0,0 +1,2547 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2007-2008 Hans Petter Selasky. 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 file contains the driver for the AT91 series USB Device + * Controller + */ + +/* + * Thanks to "David Brownell" for helping out regarding the hardware + * endpoint profiles. + */ + +/* + * NOTE: The "fifo_bank" is not reset in hardware when the endpoint is + * reset ! + * + * NOTE: When the chip detects BUS-reset it will also reset the + * endpoints, Function-address and more. + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR at91dcidebug +#define usb2_config_td_cc at91dci_config_copy +#define usb2_config_td_softc at91dci_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AT9100_DCI_BUS2SC(bus) \ + ((struct at91dci_softc *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((struct at91dci_softc *)0)->sc_bus)))) + +#define AT9100_DCI_PC2SC(pc) \ + AT9100_DCI_BUS2SC((pc)->tag_parent->info->bus) + +#if USB_DEBUG +static int at91dcidebug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, at91dci, CTLFLAG_RW, 0, "USB at91dci"); +SYSCTL_INT(_hw_usb2_at91dci, OID_AUTO, debug, CTLFLAG_RW, + &at91dcidebug, 0, "at91dci debug level"); +#endif + +#define AT9100_DCI_INTR_ENDPT 1 + +/* prototypes */ + +struct usb2_bus_methods at91dci_bus_methods; +struct usb2_pipe_methods at91dci_device_bulk_methods; +struct usb2_pipe_methods at91dci_device_ctrl_methods; +struct usb2_pipe_methods at91dci_device_intr_methods; +struct usb2_pipe_methods at91dci_device_isoc_fs_methods; +struct usb2_pipe_methods at91dci_root_ctrl_methods; +struct usb2_pipe_methods at91dci_root_intr_methods; + +static at91dci_cmd_t at91dci_setup_rx; +static at91dci_cmd_t at91dci_data_rx; +static at91dci_cmd_t at91dci_data_tx; +static at91dci_cmd_t at91dci_data_tx_sync; +static void at91dci_device_done(struct usb2_xfer *xfer, usb2_error_t error); +static void at91dci_do_poll(struct usb2_bus *bus); +static void at91dci_root_ctrl_poll(struct at91dci_softc *sc); +static void at91dci_standard_done(struct usb2_xfer *xfer); + +static usb2_sw_transfer_func_t at91dci_root_intr_done; +static usb2_sw_transfer_func_t at91dci_root_ctrl_done; +static usb2_config_td_command_t at91dci_root_ctrl_task; + +/* + * NOTE: Some of the bits in the CSR register have inverse meaning so + * we need a helper macro when acknowledging events: + */ +#define AT91_CSR_ACK(csr, what) do { \ + (csr) &= ~((AT91_UDP_CSR_FORCESTALL| \ + AT91_UDP_CSR_TXPKTRDY| \ + AT91_UDP_CSR_RXBYTECNT) ^ (what));\ + (csr) |= ((AT91_UDP_CSR_RX_DATA_BK0| \ + AT91_UDP_CSR_RX_DATA_BK1| \ + AT91_UDP_CSR_TXCOMP| \ + AT91_UDP_CSR_RXSETUP| \ + AT91_UDP_CSR_STALLSENT) ^ (what)); \ +} while (0) + +/* + * Here is a list of what the chip supports. + * Probably it supports more than listed here! + */ +static const struct usb2_hw_ep_profile + at91dci_ep_profile[AT91_UDP_EP_MAX] = { + + [0] = { + .max_in_frame_size = 8, + .max_out_frame_size = 8, + .is_simplex = 1, + .support_control = 1, + }, + [1] = { + .max_in_frame_size = 64, + .max_out_frame_size = 64, + .is_simplex = 1, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_isochronous = 1, + .support_in = 1, + .support_out = 1, + }, + [2] = { + .max_in_frame_size = 64, + .max_out_frame_size = 64, + .is_simplex = 1, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_isochronous = 1, + .support_in = 1, + .support_out = 1, + }, + [3] = { + /* can also do BULK */ + .max_in_frame_size = 8, + .max_out_frame_size = 8, + .is_simplex = 1, + .support_interrupt = 1, + .support_in = 1, + .support_out = 1, + }, + [4] = { + .max_in_frame_size = 256, + .max_out_frame_size = 256, + .is_simplex = 1, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_isochronous = 1, + .support_in = 1, + .support_out = 1, + }, + [5] = { + .max_in_frame_size = 256, + .max_out_frame_size = 256, + .is_simplex = 1, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_isochronous = 1, + .support_in = 1, + .support_out = 1, + }, +}; + +static void +at91dci_get_hw_ep_profile(struct usb2_device *udev, + const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) +{ + if (ep_addr < AT91_UDP_EP_MAX) { + *ppf = (at91dci_ep_profile + ep_addr); + } else { + *ppf = NULL; + } + return; +} + +static void +at91dci_clocks_on(struct at91dci_softc *sc) +{ + if (sc->sc_flags.clocks_off && + sc->sc_flags.port_powered) { + + DPRINTFN(5, "\n"); + + if (sc->sc_clocks_on) { + (sc->sc_clocks_on) (sc->sc_clocks_arg); + } + sc->sc_flags.clocks_off = 0; + + /* enable Transceiver */ + AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, 0); + } + return; +} + +static void +at91dci_clocks_off(struct at91dci_softc *sc) +{ + if (!sc->sc_flags.clocks_off) { + + DPRINTFN(5, "\n"); + + /* disable Transceiver */ + AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); + + if (sc->sc_clocks_off) { + (sc->sc_clocks_off) (sc->sc_clocks_arg); + } + sc->sc_flags.clocks_off = 1; + } + return; +} + +static void +at91dci_pull_up(struct at91dci_softc *sc) +{ + /* pullup D+, if possible */ + + if (!sc->sc_flags.d_pulled_up && + sc->sc_flags.port_powered) { + sc->sc_flags.d_pulled_up = 1; + (sc->sc_pull_up) (sc->sc_pull_arg); + } + return; +} + +static void +at91dci_pull_down(struct at91dci_softc *sc) +{ + /* pulldown D+, if possible */ + + if (sc->sc_flags.d_pulled_up) { + sc->sc_flags.d_pulled_up = 0; + (sc->sc_pull_down) (sc->sc_pull_arg); + } + return; +} + +static void +at91dci_wakeup_peer(struct at91dci_softc *sc) +{ + uint32_t temp; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); + + if (!(temp & AT91_UDP_GSTATE_ESR)) { + return; + } + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); + + return; +} + +static void +at91dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) +{ + struct at91dci_softc *sc; + uint32_t temp; + + DPRINTFN(5, "is_on=%u\n", is_on); + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + sc = AT9100_DCI_BUS2SC(udev->bus); + + temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); + + if (is_on) { + temp |= AT91_UDP_GSTATE_ESR; + } else { + temp &= ~AT91_UDP_GSTATE_ESR; + } + + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); + + return; +} + +static void +at91dci_set_address(struct at91dci_softc *sc, uint8_t addr) +{ + DPRINTFN(5, "addr=%d\n", addr); + + AT91_UDP_WRITE_4(sc, AT91_UDP_FADDR, addr | + AT91_UDP_FADDR_EN); + + return; +} + +static uint8_t +at91dci_setup_rx(struct at91dci_td *td) +{ + struct at91dci_softc *sc; + struct usb2_device_request req; + uint32_t csr; + uint32_t temp; + uint16_t count; + + /* read out FIFO status */ + csr = bus_space_read_4(td->io_tag, td->io_hdl, + td->status_reg); + + DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); + + temp = csr; + temp &= (AT91_UDP_CSR_RX_DATA_BK0 | + AT91_UDP_CSR_RX_DATA_BK1 | + AT91_UDP_CSR_STALLSENT | + AT91_UDP_CSR_RXSETUP | + AT91_UDP_CSR_TXCOMP); + + if (!(csr & AT91_UDP_CSR_RXSETUP)) { + /* abort any ongoing transfer */ + if (!td->did_stall) { + DPRINTFN(5, "stalling\n"); + temp |= AT91_UDP_CSR_FORCESTALL; + td->did_stall = 1; + } + goto not_complete; + } + /* get the packet byte count */ + count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; + + /* verify data length */ + if (count != td->remainder) { + DPRINTFN(0, "Invalid SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + if (count != sizeof(req)) { + DPRINTFN(0, "Unsupported SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + /* receive data */ + bus_space_read_multi_1(td->io_tag, td->io_hdl, + td->fifo_reg, (void *)&req, sizeof(req)); + + /* copy data into real buffer */ + usb2_copy_in(td->pc, 0, &req, sizeof(req)); + + td->offset = sizeof(req); + td->remainder = 0; + + /* get pointer to softc */ + sc = AT9100_DCI_PC2SC(td->pc); + + /* sneak peek the set address */ + if ((req.bmRequestType == UT_WRITE_DEVICE) && + (req.bRequest == UR_SET_ADDRESS)) { + sc->sc_dv_addr = req.wValue[0] & 0x7F; + } else { + sc->sc_dv_addr = 0xFF; + } + + /* sneak peek the endpoint direction */ + if (req.bmRequestType & UE_DIR_IN) { + csr |= AT91_UDP_CSR_DIR; + } else { + csr &= ~AT91_UDP_CSR_DIR; + } + + /* write the direction of the control transfer */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + return (0); /* complete */ + +not_complete: + /* clear interrupts, if any */ + if (temp) { + DPRINTFN(5, "clearing 0x%08x\n", temp); + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + } + return (1); /* not complete */ + +} + +static uint8_t +at91dci_data_rx(struct at91dci_td *td) +{ + struct usb2_page_search buf_res; + uint32_t csr; + uint32_t temp; + uint16_t count; + uint8_t to; + uint8_t got_short; + + to = 2; /* don't loop forever! */ + got_short = 0; + + /* check if any of the FIFO banks have data */ +repeat: + /* read out FIFO status */ + csr = bus_space_read_4(td->io_tag, td->io_hdl, + td->status_reg); + + DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); + + if (csr & AT91_UDP_CSR_RXSETUP) { + if (td->remainder == 0) { + /* + * We are actually complete and have + * received the next SETUP + */ + DPRINTFN(5, "faking complete\n"); + return (0); /* complete */ + } + /* + * USB Host Aborted the transfer. + */ + td->error = 1; + return (0); /* complete */ + } + /* Make sure that "STALLSENT" gets cleared */ + temp = csr; + temp &= AT91_UDP_CSR_STALLSENT; + + /* check status */ + if (!(csr & (AT91_UDP_CSR_RX_DATA_BK0 | + AT91_UDP_CSR_RX_DATA_BK1))) { + if (temp) { + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + } + return (1); /* not complete */ + } + /* get the packet byte count */ + count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; + + /* verify the packet byte count */ + if (count != td->max_packet_size) { + if (count < td->max_packet_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + while (count > 0) { + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* receive data */ + bus_space_read_multi_1(td->io_tag, td->io_hdl, + td->fifo_reg, buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* clear status bits */ + if (td->support_multi_buffer) { + if (td->fifo_bank) { + td->fifo_bank = 0; + temp |= AT91_UDP_CSR_RX_DATA_BK1; + } else { + td->fifo_bank = 1; + temp |= AT91_UDP_CSR_RX_DATA_BK0; + } + } else { + temp |= (AT91_UDP_CSR_RX_DATA_BK0 | + AT91_UDP_CSR_RX_DATA_BK1); + } + + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + + /* + * NOTE: We may have to delay a little bit before + * proceeding after clearing the DATA_BK bits. + */ + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + return (0); + } + /* else need to receive a zero length packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +at91dci_data_tx(struct at91dci_td *td) +{ + struct usb2_page_search buf_res; + uint32_t csr; + uint32_t temp; + uint16_t count; + uint8_t to; + + to = 2; /* don't loop forever! */ + +repeat: + + /* read out FIFO status */ + csr = bus_space_read_4(td->io_tag, td->io_hdl, + td->status_reg); + + DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); + + if (csr & AT91_UDP_CSR_RXSETUP) { + /* + * The current transfer was aborted + * by the USB Host + */ + td->error = 1; + return (0); /* complete */ + } + /* Make sure that "STALLSENT" gets cleared */ + temp = csr; + temp &= AT91_UDP_CSR_STALLSENT; + + if (csr & AT91_UDP_CSR_TXPKTRDY) { + if (temp) { + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + } + return (1); /* not complete */ + } else { + /* clear TXCOMP and set TXPKTRDY */ + temp |= (AT91_UDP_CSR_TXCOMP | + AT91_UDP_CSR_TXPKTRDY); + } + + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + while (count > 0) { + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* transmit data */ + bus_space_write_multi_1(td->io_tag, td->io_hdl, + td->fifo_reg, buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +at91dci_data_tx_sync(struct at91dci_td *td) +{ + struct at91dci_softc *sc; + uint32_t csr; + uint32_t temp; + +#if 0 +repeat: +#endif + + /* read out FIFO status */ + csr = bus_space_read_4(td->io_tag, td->io_hdl, + td->status_reg); + + DPRINTFN(5, "csr=0x%08x\n", csr); + + if (csr & AT91_UDP_CSR_RXSETUP) { + DPRINTFN(5, "faking complete\n"); + /* Race condition */ + return (0); /* complete */ + } + temp = csr; + temp &= (AT91_UDP_CSR_STALLSENT | + AT91_UDP_CSR_TXCOMP); + + /* check status */ + if (csr & AT91_UDP_CSR_TXPKTRDY) { + goto not_complete; + } + if (!(csr & AT91_UDP_CSR_TXCOMP)) { + goto not_complete; + } + sc = AT9100_DCI_PC2SC(td->pc); + if (sc->sc_dv_addr != 0xFF) { + /* + * The AT91 has a special requirement with regard to + * setting the address and that is to write the new + * address before clearing TXCOMP: + */ + at91dci_set_address(sc, sc->sc_dv_addr); + } + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + + return (0); /* complete */ + +not_complete: + if (temp) { + /* write command */ + AT91_CSR_ACK(csr, temp); + bus_space_write_4(td->io_tag, td->io_hdl, + td->status_reg, csr); + } + return (1); /* not complete */ +} + +static uint8_t +at91dci_xfer_do_fifo(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc; + struct at91dci_td *td; + uint8_t temp; + + DPRINTFN(9, "\n"); + + td = xfer->td_transfer_cache; + while (1) { + if ((td->func) (td)) { + /* operation in progress */ + break; + } + if (((void *)td) == xfer->td_transfer_last) { + goto done; + } + if (td->error) { + goto done; + } else if (td->remainder > 0) { + /* + * We had a short transfer. If there is no alternate + * next, stop processing ! + */ + if (!td->alt_next) { + goto done; + } + } + /* + * Fetch the next transfer descriptor and transfer + * some flags to the next transfer descriptor + */ + temp = 0; + if (td->fifo_bank) + temp |= 1; + td = td->obj_next; + xfer->td_transfer_cache = td; + if (temp & 1) + td->fifo_bank = 1; + } + return (1); /* not complete */ + +done: + sc = xfer->usb2_sc; + temp = (xfer->endpoint & UE_ADDR); + + /* update FIFO bank flag and multi buffer */ + if (td->fifo_bank) { + sc->sc_ep_flags[temp].fifo_bank = 1; + } else { + sc->sc_ep_flags[temp].fifo_bank = 0; + } + + /* compute all actual lengths */ + + at91dci_standard_done(xfer); + + return (0); /* complete */ +} + +static void +at91dci_interrupt_poll(struct at91dci_softc *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!at91dci_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + return; +} + +static void +at91dci_vbus_interrupt(struct usb2_bus *bus, uint8_t is_on) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); + + DPRINTFN(5, "vbus = %u\n", is_on); + + mtx_lock(&sc->sc_bus.mtx); + if (is_on) { + if (!sc->sc_flags.status_vbus) { + sc->sc_flags.status_vbus = 1; + + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &at91dci_root_intr_done); + } + } else { + if (sc->sc_flags.status_vbus) { + sc->sc_flags.status_vbus = 0; + sc->sc_flags.status_bus_reset = 0; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &at91dci_root_intr_done); + } + } + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +void +at91dci_interrupt(struct at91dci_softc *sc) +{ + uint32_t status; + + mtx_lock(&sc->sc_bus.mtx); + + status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); + status &= AT91_UDP_INT_DEFAULT; + + if (!status) { + mtx_unlock(&sc->sc_bus.mtx); + return; + } + /* acknowledge interrupts */ + + AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status); + + /* check for any bus state change interrupts */ + + if (status & AT91_UDP_INT_BUS) { + + DPRINTFN(5, "real bus interrupt 0x%08x\n", status); + + if (status & AT91_UDP_INT_END_BR) { + + /* set correct state */ + sc->sc_flags.status_bus_reset = 1; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + /* disable resume interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, + AT91_UDP_INT_RXRSM); + /* enable suspend interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IER, + AT91_UDP_INT_RXSUSP); + } + /* + * If RXRSM and RXSUSP is set at the same time we interpret + * that like RESUME. Resume is set when there is at least 3 + * milliseconds of inactivity on the USB BUS. + */ + if (status & AT91_UDP_INT_RXRSM) { + if (sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 1; + + /* disable resume interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, + AT91_UDP_INT_RXRSM); + /* enable suspend interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IER, + AT91_UDP_INT_RXSUSP); + } + } else if (status & AT91_UDP_INT_RXSUSP) { + if (!sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 1; + sc->sc_flags.change_suspend = 1; + + /* disable suspend interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, + AT91_UDP_INT_RXSUSP); + + /* enable resume interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IER, + AT91_UDP_INT_RXRSM); + } + } + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &at91dci_root_intr_done); + } + /* check for any endpoint interrupts */ + + if (status & AT91_UDP_INT_EPS) { + + DPRINTFN(5, "real endpoint interrupt 0x%08x\n", status); + + at91dci_interrupt_poll(sc); + } + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +at91dci_setup_standard_chain_sub(struct at91dci_std_temp *temp) +{ + struct at91dci_td *td; + + /* get current Transfer Descriptor */ + td = temp->td_next; + temp->td = td; + + /* prepare for next TD */ + temp->td_next = td->obj_next; + + /* fill out the Transfer Descriptor */ + td->func = temp->func; + td->pc = temp->pc; + td->offset = temp->offset; + td->remainder = temp->len; + td->fifo_bank = 0; + td->error = 0; + td->did_stall = 0; + td->short_pkt = temp->short_pkt; + td->alt_next = temp->setup_alt_next; + return; +} + +static void +at91dci_setup_standard_chain(struct usb2_xfer *xfer) +{ + struct at91dci_std_temp temp; + struct at91dci_softc *sc; + struct at91dci_td *td; + uint32_t x; + uint8_t ep_no; + uint8_t need_sync; + + DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.max_frame_size = xfer->max_frame_size; + + td = xfer->td_start[0]; + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + /* setup temp */ + + temp.td = NULL; + temp.td_next = xfer->td_start[0]; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.offset = 0; + + sc = xfer->usb2_sc; + ep_no = (xfer->endpoint & UE_ADDR); + + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + temp.func = &at91dci_setup_rx; + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.short_pkt = temp.len ? 1 : 0; + + at91dci_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + if (x != xfer->nframes) { + if (xfer->endpoint & UE_DIR_IN) { + temp.func = &at91dci_data_tx; + need_sync = 1; + } else { + temp.func = &at91dci_data_rx; + need_sync = 0; + } + + /* setup "pc" pointer */ + temp.pc = xfer->frbuffers + x; + } else { + need_sync = 0; + } + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.short_pkt = 0; + + } else { + + /* regular data transfer */ + + temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + at91dci_setup_standard_chain_sub(&temp); + + if (xfer->flags_int.isochronous_xfr) { + temp.offset += temp.len; + } else { + /* get next Page Cache pointer */ + temp.pc = xfer->frbuffers + x; + } + } + + /* always setup a valid "pc" pointer for status and sync */ + temp.pc = xfer->frbuffers + 0; + + /* check if we need to sync */ + if (need_sync && xfer->flags_int.control_xfr) { + + /* we need a SYNC point after TX */ + temp.func = &at91dci_data_tx_sync; + temp.len = 0; + temp.short_pkt = 0; + + at91dci_setup_standard_chain_sub(&temp); + } + /* check if we should append a status stage */ + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + /* + * Send a DATA1 message and invert the current + * endpoint direction. + */ + if (xfer->endpoint & UE_DIR_IN) { + temp.func = &at91dci_data_rx; + need_sync = 0; + } else { + temp.func = &at91dci_data_tx; + need_sync = 1; + } + temp.len = 0; + temp.short_pkt = 0; + + at91dci_setup_standard_chain_sub(&temp); + if (need_sync) { + /* we need a SYNC point after TX */ + temp.func = &at91dci_data_tx_sync; + temp.len = 0; + temp.short_pkt = 0; + + at91dci_setup_standard_chain_sub(&temp); + } + } + /* must have at least one frame! */ + td = temp.td; + xfer->td_transfer_last = td; + + /* setup the correct fifo bank */ + if (sc->sc_ep_flags[ep_no].fifo_bank) { + td = xfer->td_transfer_first; + td->fifo_bank = 1; + } + return; +} + +static void +at91dci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + struct at91dci_softc *sc = xfer->usb2_sc; + + DPRINTF("xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + at91dci_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +at91dci_start_standard_chain(struct usb2_xfer *xfer) +{ + DPRINTFN(9, "\n"); + + /* poll one time */ + if (at91dci_xfer_do_fifo(xfer)) { + + struct at91dci_softc *sc = xfer->usb2_sc; + uint8_t ep_no = xfer->endpoint & UE_ADDR; + + /* + * Only enable the endpoint interrupt when we are actually + * waiting for data, hence we are dealing with level + * triggered interrupts ! + */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_EP(ep_no)); + + DPRINTFN(15, "enable interrupts on endpoint %d\n", ep_no); + + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &at91dci_timeout, xfer->timeout); + } + } + return; +} + +static void +at91dci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + + DPRINTFN(9, "\n"); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + at91dci_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); + + /* set port bit */ + sc->sc_hub_idata[0] = 0x02; /* we only have one port */ + +done: + return; +} + +static usb2_error_t +at91dci_standard_done_sub(struct usb2_xfer *xfer) +{ + struct at91dci_td *td; + uint32_t len; + uint8_t error; + + DPRINTFN(9, "\n"); + + td = xfer->td_transfer_cache; + + do { + len = td->remainder; + + if (xfer->aframes != xfer->nframes) { + /* + * Verify the length and subtract + * the remainder from "frlengths[]": + */ + if (len > xfer->frlengths[xfer->aframes]) { + td->error = 1; + } else { + xfer->frlengths[xfer->aframes] -= len; + } + } + /* Check for transfer error */ + if (td->error) { + /* the transfer is finished */ + error = 1; + td = NULL; + break; + } + /* Check for short transfer */ + if (len > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + td = td->obj_next; + } else { + td = NULL; + } + } else { + /* the transfer is finished */ + td = NULL; + } + error = 0; + break; + } + td = td->obj_next; + + /* this USB frame is complete */ + error = 0; + break; + + } while (0); + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + return (error ? + USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); +} + +static void +at91dci_standard_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + err = at91dci_standard_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = at91dci_standard_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = at91dci_standard_done_sub(xfer); + } +done: + at91dci_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * at91dci_device_done + * + * NOTE: this function can be called more than one time on the + * same USB transfer! + *------------------------------------------------------------------------*/ +static void +at91dci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + uint8_t ep_no; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + ep_no = (xfer->endpoint & UE_ADDR); + + /* disable endpoint interrupt */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_EP(ep_no)); + + DPRINTFN(15, "disable interrupts on endpoint %d\n", ep_no); + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +static void +at91dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, + struct usb2_pipe *pipe) +{ + struct at91dci_softc *sc; + uint32_t csr_val; + uint8_t csr_reg; + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + DPRINTFN(5, "pipe=%p\n", pipe); + + if (xfer) { + /* cancel any ongoing transfers */ + at91dci_device_done(xfer, USB_ERR_STALLED); + } + /* set FORCESTALL */ + sc = AT9100_DCI_BUS2SC(udev->bus); + csr_reg = (pipe->edesc->bEndpointAddress & UE_ADDR); + csr_reg = AT91_UDP_CSR(csr_reg); + csr_val = AT91_UDP_READ_4(sc, csr_reg); + AT91_CSR_ACK(csr_val, AT91_UDP_CSR_FORCESTALL); + AT91_UDP_WRITE_4(sc, csr_reg, csr_val); + return; +} + +static void +at91dci_clear_stall_sub(struct at91dci_softc *sc, uint8_t ep_no, + uint8_t ep_type, uint8_t ep_dir) +{ + const struct usb2_hw_ep_profile *pf; + uint32_t csr_val; + uint32_t temp; + uint8_t csr_reg; + uint8_t to; + + if (ep_type == UE_CONTROL) { + /* clearing stall is not needed */ + return; + } + /* compute CSR register offset */ + csr_reg = AT91_UDP_CSR(ep_no); + + /* compute default CSR value */ + csr_val = 0; + AT91_CSR_ACK(csr_val, 0); + + /* disable endpoint */ + AT91_UDP_WRITE_4(sc, csr_reg, csr_val); + + /* get endpoint profile */ + at91dci_get_hw_ep_profile(NULL, &pf, ep_no); + + /* reset FIFO */ + AT91_UDP_WRITE_4(sc, AT91_UDP_RST, AT91_UDP_RST_EP(ep_no)); + AT91_UDP_WRITE_4(sc, AT91_UDP_RST, 0); + + /* + * NOTE: One would assume that a FIFO reset would release the + * FIFO banks aswell, but it doesn't! We have to do this + * manually! + */ + + /* release FIFO banks, if any */ + for (to = 0; to != 2; to++) { + + /* get csr value */ + csr_val = AT91_UDP_READ_4(sc, csr_reg); + + if (csr_val & (AT91_UDP_CSR_RX_DATA_BK0 | + AT91_UDP_CSR_RX_DATA_BK1)) { + /* clear status bits */ + if (pf->support_multi_buffer) { + if (sc->sc_ep_flags[ep_no].fifo_bank) { + sc->sc_ep_flags[ep_no].fifo_bank = 0; + temp = AT91_UDP_CSR_RX_DATA_BK1; + } else { + sc->sc_ep_flags[ep_no].fifo_bank = 1; + temp = AT91_UDP_CSR_RX_DATA_BK0; + } + } else { + temp = (AT91_UDP_CSR_RX_DATA_BK0 | + AT91_UDP_CSR_RX_DATA_BK1); + } + } else { + temp = 0; + } + + /* clear FORCESTALL */ + temp |= AT91_UDP_CSR_STALLSENT; + + AT91_CSR_ACK(csr_val, temp); + AT91_UDP_WRITE_4(sc, csr_reg, csr_val); + } + + /* compute default CSR value */ + csr_val = 0; + AT91_CSR_ACK(csr_val, 0); + + /* enable endpoint */ + csr_val &= ~AT91_UDP_CSR_ET_MASK; + csr_val |= AT91_UDP_CSR_EPEDS; + + if (ep_type == UE_CONTROL) { + csr_val |= AT91_UDP_CSR_ET_CTRL; + } else { + if (ep_type == UE_BULK) { + csr_val |= AT91_UDP_CSR_ET_BULK; + } else if (ep_type == UE_INTERRUPT) { + csr_val |= AT91_UDP_CSR_ET_INT; + } else { + csr_val |= AT91_UDP_CSR_ET_ISO; + } + if (ep_dir & UE_DIR_IN) { + csr_val |= AT91_UDP_CSR_ET_DIR_IN; + } + } + + /* enable endpoint */ + AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(ep_no), csr_val); + + return; +} + +static void +at91dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + struct at91dci_softc *sc; + struct usb2_endpoint_descriptor *ed; + + DPRINTFN(5, "pipe=%p\n", pipe); + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + /* check mode */ + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + /* get softc */ + sc = AT9100_DCI_BUS2SC(udev->bus); + + /* get endpoint descriptor */ + ed = pipe->edesc; + + /* reset endpoint */ + at91dci_clear_stall_sub(sc, + (ed->bEndpointAddress & UE_ADDR), + (ed->bmAttributes & UE_XFERTYPE), + (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); + return; +} + +usb2_error_t +at91dci_init(struct at91dci_softc *sc) +{ + uint32_t csr_val; + uint8_t n; + + DPRINTF("start\n"); + + /* set up the bus structure */ + sc->sc_bus.usbrev = USB_REV_1_1; + sc->sc_bus.methods = &at91dci_bus_methods; + + mtx_lock(&sc->sc_bus.mtx); + + /* turn on clocks */ + + if (sc->sc_clocks_on) { + (sc->sc_clocks_on) (sc->sc_clocks_arg); + } + /* wait a little for things to stabilise */ + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + + /* disable and clear all interrupts */ + + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); + AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); + + /* compute default CSR value */ + + csr_val = 0; + AT91_CSR_ACK(csr_val, 0); + + /* disable all endpoints */ + + for (n = 0; n != AT91_UDP_EP_MAX; n++) { + + /* disable endpoint */ + AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(n), csr_val); + } + + /* enable the control endpoint */ + + AT91_CSR_ACK(csr_val, AT91_UDP_CSR_ET_CTRL | + AT91_UDP_CSR_EPEDS); + + /* write to FIFO control register */ + + AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(0), csr_val); + + /* enable the interrupts we want */ + + AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_BUS); + + /* turn off clocks */ + + at91dci_clocks_off(sc); + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch any lost interrupts */ + + at91dci_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +at91dci_uninit(struct at91dci_softc *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + + /* disable and clear all interrupts */ + AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); + AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); + + sc->sc_flags.port_powered = 0; + sc->sc_flags.status_vbus = 0; + sc->sc_flags.status_bus_reset = 0; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + at91dci_pull_down(sc); + at91dci_clocks_off(sc); + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +void +at91dci_suspend(struct at91dci_softc *sc) +{ + return; +} + +void +at91dci_resume(struct at91dci_softc *sc) +{ + return; +} + +static void +at91dci_do_poll(struct usb2_bus *bus) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + at91dci_interrupt_poll(sc); + at91dci_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/*------------------------------------------------------------------------* + * at91dci bulk support + *------------------------------------------------------------------------*/ +static void +at91dci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_bulk_close(struct usb2_xfer *xfer) +{ + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +at91dci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_bulk_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + at91dci_setup_standard_chain(xfer); + at91dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods at91dci_device_bulk_methods = +{ + .open = at91dci_device_bulk_open, + .close = at91dci_device_bulk_close, + .enter = at91dci_device_bulk_enter, + .start = at91dci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci control support + *------------------------------------------------------------------------*/ +static void +at91dci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_ctrl_close(struct usb2_xfer *xfer) +{ + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +at91dci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_ctrl_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + at91dci_setup_standard_chain(xfer); + at91dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods at91dci_device_ctrl_methods = +{ + .open = at91dci_device_ctrl_open, + .close = at91dci_device_ctrl_close, + .enter = at91dci_device_ctrl_enter, + .start = at91dci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci interrupt support + *------------------------------------------------------------------------*/ +static void +at91dci_device_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_intr_close(struct usb2_xfer *xfer) +{ + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +at91dci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_intr_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + at91dci_setup_standard_chain(xfer); + at91dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods at91dci_device_intr_methods = +{ + .open = at91dci_device_intr_open, + .close = at91dci_device_intr_close, + .enter = at91dci_device_intr_enter, + .start = at91dci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci full speed isochronous support + *------------------------------------------------------------------------*/ +static void +at91dci_device_isoc_fs_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_device_isoc_fs_close(struct usb2_xfer *xfer) +{ + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +at91dci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + uint32_t temp; + uint32_t nframes; + + DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + /* get the current frame index */ + + nframes = AT91_UDP_READ_4(sc, AT91_UDP_FRM); + + /* + * check if the frame index is within the window where the frames + * will be inserted + */ + temp = (nframes - xfer->pipe->isoc_next) & AT91_UDP_FRM_MASK; + + if ((xfer->pipe->is_synced == 0) || + (temp < xfer->nframes)) { + /* + * If there is data underflow or the pipe queue is + * empty we schedule the transfer a few frames ahead + * of the current frame position. Else two isochronous + * transfers might overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & AT91_UDP_FRM_MASK; + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + temp = (xfer->pipe->isoc_next - nframes) & AT91_UDP_FRM_MASK; + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + + xfer->nframes; + + /* compute frame number for next insertion */ + xfer->pipe->isoc_next += xfer->nframes; + + /* setup TDs */ + at91dci_setup_standard_chain(xfer); + return; +} + +static void +at91dci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + at91dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods at91dci_device_isoc_fs_methods = +{ + .open = at91dci_device_isoc_fs_open, + .close = at91dci_device_isoc_fs_close, + .enter = at91dci_device_isoc_fs_enter, + .start = at91dci_device_isoc_fs_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci root control support + *------------------------------------------------------------------------* + * simulate a hardware HUB by handling + * all the necessary requests + *------------------------------------------------------------------------*/ + +static void +at91dci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_root_ctrl_close(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* + * USB descriptors for the virtual Root HUB: + */ + +static const struct usb2_device_descriptor at91dci_devd = { + .bLength = sizeof(struct usb2_device_descriptor), + .bDescriptorType = UDESC_DEVICE, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_HSHUBSTT, + .bMaxPacketSize = 64, + .bcdDevice = {0x00, 0x01}, + .iManufacturer = 1, + .iProduct = 2, + .bNumConfigurations = 1, +}; + +static const struct usb2_device_qualifier at91dci_odevd = { + .bLength = sizeof(struct usb2_device_qualifier), + .bDescriptorType = UDESC_DEVICE_QUALIFIER, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_FSHUB, + .bMaxPacketSize0 = 0, + .bNumConfigurations = 0, +}; + +static const struct at91dci_config_desc at91dci_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(at91dci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0, + }, + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_HSHUBSTT, + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = (UE_DIR_IN | AT9100_DCI_INTR_ENDPT), + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, + .bInterval = 255, + }, +}; + +static const struct usb2_hub_descriptor_min at91dci_hubd = { + .bDescLength = sizeof(at91dci_hubd), + .bDescriptorType = UDESC_HUB, + .bNbrPorts = 1, + .wHubCharacteristics[0] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, + .wHubCharacteristics[1] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, + .bPwrOn2PwrGood = 50, + .bHubContrCurrent = 0, + .DeviceRemovable = {0}, /* port is removable */ +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_VENDOR \ + 'A', 0, 'T', 0, 'M', 0, 'E', 0, 'L', 0 + +#define STRING_PRODUCT \ + 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \ + 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ + 'U', 0, 'B', 0, + +USB_MAKE_STRING_DESC(STRING_LANG, at91dci_langtab); +USB_MAKE_STRING_DESC(STRING_VENDOR, at91dci_vendor); +USB_MAKE_STRING_DESC(STRING_PRODUCT, at91dci_product); + +static void +at91dci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_root_ctrl_start(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command( + &sc->sc_config_td, NULL, &at91dci_root_ctrl_task, 0, 0); + + return; +} + +static void +at91dci_root_ctrl_task(struct at91dci_softc *sc, + struct at91dci_config_copy *cc, uint16_t refcount) +{ + at91dci_root_ctrl_poll(sc); + return; +} + +static void +at91dci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + uint16_t value; + uint16_t index; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + at91dci_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + /* demultiplex the control request */ + + switch (std->req.bmRequestType) { + case UT_READ_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_descriptor; + case UR_GET_CONFIG: + goto tr_handle_get_config; + case UR_GET_STATUS: + goto tr_handle_get_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_DEVICE: + switch (std->req.bRequest) { + case UR_SET_ADDRESS: + goto tr_handle_set_address; + case UR_SET_CONFIG: + goto tr_handle_set_config; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_DESCRIPTOR: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_WRITE_ENDPOINT: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_clear_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_clear_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SET_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_set_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_set_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SYNCH_FRAME: + goto tr_valid; /* nop */ + default: + goto tr_stalled; + } + break; + + case UT_READ_ENDPOINT: + switch (std->req.bRequest) { + case UR_GET_STATUS: + goto tr_handle_get_ep_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_INTERFACE: + switch (std->req.bRequest) { + case UR_SET_INTERFACE: + goto tr_handle_set_interface; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_READ_INTERFACE: + switch (std->req.bRequest) { + case UR_GET_INTERFACE: + goto tr_handle_get_interface; + case UR_GET_STATUS: + goto tr_handle_get_iface_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_INTERFACE: + case UT_WRITE_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_READ_CLASS_INTERFACE: + case UT_READ_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_WRITE_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_valid; + case UR_SET_DESCRIPTOR: + case UR_SET_FEATURE: + break; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_handle_clear_port_feature; + case UR_SET_FEATURE: + goto tr_handle_set_port_feature; + case UR_CLEAR_TT_BUFFER: + case UR_RESET_TT: + case UR_STOP_TT: + goto tr_valid; + + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_GET_TT_STATE: + goto tr_handle_get_tt_state; + case UR_GET_STATUS: + goto tr_handle_get_port_status; + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_class_descriptor; + case UR_GET_STATUS: + goto tr_handle_get_class_status; + + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + goto tr_valid; + +tr_handle_get_descriptor: + switch (value >> 8) { + case UDESC_DEVICE: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(at91dci_devd); + std->ptr = USB_ADD_BYTES(&at91dci_devd, 0); + goto tr_valid; + case UDESC_CONFIG: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(at91dci_confd); + std->ptr = USB_ADD_BYTES(&at91dci_confd, 0); + goto tr_valid; + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + std->len = sizeof(at91dci_langtab); + std->ptr = USB_ADD_BYTES(&at91dci_langtab, 0); + goto tr_valid; + + case 1: /* Vendor */ + std->len = sizeof(at91dci_vendor); + std->ptr = USB_ADD_BYTES(&at91dci_vendor, 0); + goto tr_valid; + + case 2: /* Product */ + std->len = sizeof(at91dci_product); + std->ptr = USB_ADD_BYTES(&at91dci_product, 0); + goto tr_valid; + default: + break; + } + break; + default: + goto tr_stalled; + } + goto tr_stalled; + +tr_handle_get_config: + std->len = 1; + sc->sc_hub_temp.wValue[0] = sc->sc_conf; + goto tr_valid; + +tr_handle_get_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); + goto tr_valid; + +tr_handle_set_address: + if (value & 0xFF00) { + goto tr_stalled; + } + sc->sc_rt_addr = value; + goto tr_valid; + +tr_handle_set_config: + if (value >= 2) { + goto tr_stalled; + } + sc->sc_conf = value; + goto tr_valid; + +tr_handle_get_interface: + std->len = 1; + sc->sc_hub_temp.wValue[0] = 0; + goto tr_valid; + +tr_handle_get_tt_state: +tr_handle_get_class_status: +tr_handle_get_iface_status: +tr_handle_get_ep_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, 0); + goto tr_valid; + +tr_handle_set_halt: +tr_handle_set_interface: +tr_handle_set_wakeup: +tr_handle_clear_wakeup: +tr_handle_clear_halt: + goto tr_valid; + +tr_handle_clear_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); + + switch (value) { + case UHF_PORT_SUSPEND: + at91dci_wakeup_peer(sc); + break; + + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 0; + break; + + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 0; + at91dci_pull_down(sc); + at91dci_clocks_off(sc); + break; + case UHF_C_PORT_CONNECTION: + sc->sc_flags.change_connect = 0; + break; + case UHF_C_PORT_SUSPEND: + sc->sc_flags.change_suspend = 0; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_set_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); + + switch (value) { + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 1; + break; + case UHF_PORT_SUSPEND: + case UHF_PORT_RESET: + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 1; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_get_port_status: + + DPRINTFN(9, "UR_GET_PORT_STATUS\n"); + + if (index != 1) { + goto tr_stalled; + } + if (sc->sc_flags.status_vbus) { + at91dci_clocks_on(sc); + at91dci_pull_up(sc); + } else { + at91dci_pull_down(sc); + at91dci_clocks_off(sc); + } + + /* Select FULL-speed and Device Side Mode */ + + value = UPS_PORT_MODE_DEVICE; + + if (sc->sc_flags.port_powered) { + value |= UPS_PORT_POWER; + } + if (sc->sc_flags.port_enabled) { + value |= UPS_PORT_ENABLED; + } + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + value |= UPS_CURRENT_CONNECT_STATUS; + } + if (sc->sc_flags.status_suspend) { + value |= UPS_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortStatus, value); + + value = 0; + + if (sc->sc_flags.change_connect) { + value |= UPS_C_CONNECT_STATUS; + + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + /* reset endpoint flags */ + bzero(sc->sc_ep_flags, sizeof(sc->sc_ep_flags)); + } + } + if (sc->sc_flags.change_suspend) { + value |= UPS_C_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortChange, value); + std->len = sizeof(sc->sc_hub_temp.ps); + goto tr_valid; + +tr_handle_get_class_descriptor: + if (value & 0xFF) { + goto tr_stalled; + } + std->ptr = USB_ADD_BYTES(&at91dci_hubd, 0); + std->len = sizeof(at91dci_hubd); + goto tr_valid; + +tr_stalled: + std->err = USB_ERR_STALLED; +tr_valid: +done: + return; +} + +static void +at91dci_root_ctrl_poll(struct at91dci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &at91dci_root_ctrl_done); + return; +} + +struct usb2_pipe_methods at91dci_root_ctrl_methods = +{ + .open = at91dci_root_ctrl_open, + .close = at91dci_root_ctrl_close, + .enter = at91dci_root_ctrl_enter, + .start = at91dci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * at91dci root interrupt support + *------------------------------------------------------------------------*/ +static void +at91dci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_root_intr_close(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + at91dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +at91dci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_root_intr_start(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + return; +} + +struct usb2_pipe_methods at91dci_root_intr_methods = +{ + .open = at91dci_root_intr_open, + .close = at91dci_root_intr_close, + .enter = at91dci_root_intr_enter, + .start = at91dci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +at91dci_xfer_setup(struct usb2_setup_params *parm) +{ + const struct usb2_hw_ep_profile *pf; + struct at91dci_softc *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t n; + uint8_t ep_no; + + sc = AT9100_DCI_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + /* + * NOTE: This driver does not use any of the parameters that + * are computed from the following values. Just set some + * reasonable dummies: + */ + parm->hc_max_packet_size = 0x500; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = 0x500; + + usb2_transfer_setup_sub(parm); + + /* + * compute maximum number of TDs + */ + if (parm->methods == &at91dci_device_ctrl_methods) { + + ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ + + 1 /* SYNC 2 */ ; + + } else if (parm->methods == &at91dci_device_bulk_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &at91dci_device_intr_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &at91dci_device_isoc_fs_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else { + + ntd = 0; + } + + /* + * check if "usb2_transfer_setup_sub" set an error + */ + if (parm->err) { + return; + } + /* + * allocate transfer descriptors + */ + last_obj = NULL; + + /* + * get profile stuff + */ + if (ntd) { + + ep_no = xfer->endpoint & UE_ADDR; + at91dci_get_hw_ep_profile(parm->udev, &pf, ep_no); + + if (pf == NULL) { + /* should not happen */ + parm->err = USB_ERR_INVAL; + return; + } + } else { + ep_no = 0; + pf = NULL; + } + + /* align data */ + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); + + for (n = 0; n != ntd; n++) { + + struct at91dci_td *td; + + if (parm->buf) { + + td = USB_ADD_BYTES(parm->buf, parm->size[0]); + + /* init TD */ + td->io_tag = sc->sc_io_tag; + td->io_hdl = sc->sc_io_hdl; + td->max_packet_size = xfer->max_packet_size; + td->status_reg = AT91_UDP_CSR(ep_no); + td->fifo_reg = AT91_UDP_FDR(ep_no); + if (pf->support_multi_buffer) { + td->support_multi_buffer = 1; + } + td->obj_next = last_obj; + + last_obj = td; + } + parm->size[0] += sizeof(*td); + } + + xfer->td_start[0] = last_obj; + return; +} + +static void +at91dci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +at91dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_rt_addr); + + if (udev->device_index == sc->sc_rt_addr) { + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + switch (edesc->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &at91dci_root_ctrl_methods; + break; + case UE_DIR_IN | AT9100_DCI_INTR_ENDPT: + pipe->methods = &at91dci_root_intr_methods; + break; + default: + /* do nothing */ + break; + } + } else { + + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + if (udev->speed != USB_SPEED_FULL) { + /* not supported */ + return; + } + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &at91dci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &at91dci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + pipe->methods = &at91dci_device_isoc_fs_methods; + break; + case UE_BULK: + pipe->methods = &at91dci_device_bulk_methods; + break; + default: + /* do nothing */ + break; + } + } + return; +} + +struct usb2_bus_methods at91dci_bus_methods = +{ + .pipe_init = &at91dci_pipe_init, + .xfer_setup = &at91dci_xfer_setup, + .xfer_unsetup = &at91dci_xfer_unsetup, + .do_poll = &at91dci_do_poll, + .get_hw_ep_profile = &at91dci_get_hw_ep_profile, + .set_stall = &at91dci_set_stall, + .clear_stall = &at91dci_clear_stall, + .vbus_interrupt = &at91dci_vbus_interrupt, + .rem_wakeup_set = &at91dci_rem_wakeup_set, +}; diff --git a/sys/dev/usb2/controller/at91dci.h b/sys/dev/usb2/controller/at91dci.h new file mode 100644 index 000000000000..bade80a38309 --- /dev/null +++ b/sys/dev/usb2/controller/at91dci.h @@ -0,0 +1,242 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2006 ATMEL + * Copyright (c) 2007 Hans Petter Selasky + * 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. + */ + +/* + * USB Device Port (UDP) register definition, based on + * "AT91RM9200.h" provided by ATMEL. + */ + +#ifndef _AT9100_DCI_H_ +#define _AT9100_DCI_H_ + +#define AT91_UDP_FRM 0x00 /* Frame number register */ +#define AT91_UDP_FRM_MASK (0x7FF << 0) /* Frame Number as Defined in + * the Packet Field Formats */ +#define AT91_UDP_FRM_ERR (0x1 << 16) /* Frame Error */ +#define AT91_UDP_FRM_OK (0x1 << 17) /* Frame OK */ + +#define AT91_UDP_GSTATE 0x04 /* Global state register */ +#define AT91_UDP_GSTATE_ADDR (0x1 << 0) /* Addressed state */ +#define AT91_UDP_GSTATE_CONFG (0x1 << 1) /* Configured */ +#define AT91_UDP_GSTATE_ESR (0x1 << 2) /* Enable Send Resume */ +#define AT91_UDP_GSTATE_RSM (0x1 << 3) /* A Resume Has Been Sent to + * the Host */ +#define AT91_UDP_GSTATE_RMW (0x1 << 4) /* Remote Wake Up Enable */ + +#define AT91_UDP_FADDR 0x08 /* Function Address Register */ +#define AT91_UDP_FADDR_MASK (0x7F << 0)/* Function Address Mask */ +#define AT91_UDP_FADDR_EN (0x1 << 8)/* Function Enable */ + +#define AT91_UDP_RES0 0x0C /* Reserved 0 */ + +#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */ +#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */ +#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */ +#define AT91_UDP_ISR 0x1C /* Interrupt Status Register */ +#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */ +#define AT91_UDP_INT_EP(n) (0x1 <<(n))/* Endpoint "n" Interrupt */ +#define AT91_UDP_INT_RXSUSP (0x1 << 8)/* USB Suspend Interrupt */ +#define AT91_UDP_INT_RXRSM (0x1 << 9)/* USB Resume Interrupt */ +#define AT91_UDP_INT_EXTRSM (0x1 << 10)/* USB External Resume Interrupt */ +#define AT91_UDP_INT_SOFINT (0x1 << 11)/* USB Start Of frame Interrupt */ +#define AT91_UDP_INT_END_BR (0x1 << 12)/* USB End Of Bus Reset Interrupt */ +#define AT91_UDP_INT_WAKEUP (0x1 << 13)/* USB Resume Interrupt */ + +#define AT91_UDP_INT_BUS \ + (AT91_UDP_INT_RXSUSP|AT91_UDP_INT_RXRSM| \ + AT91_UDP_INT_END_BR) + +#define AT91_UDP_INT_EPS \ + (AT91_UDP_INT_EP(0)|AT91_UDP_INT_EP(1)| \ + AT91_UDP_INT_EP(2)|AT91_UDP_INT_EP(3)| \ + AT91_UDP_INT_EP(4)|AT91_UDP_INT_EP(5)) + +#define AT91_UDP_INT_DEFAULT \ + (AT91_UDP_INT_EPS|AT91_UDP_INT_BUS) + +#define AT91_UDP_RES1 0x24 /* Reserved 1 */ +#define AT91_UDP_RST 0x28 /* Reset Endpoint Register */ +#define AT91_UDP_RST_EP(n) (0x1 << (n))/* Reset Endpoint "n" */ + +#define AT91_UDP_RES2 0x2C /* Reserved 2 */ + +#define AT91_UDP_CSR(n) (0x30 + (4*(n)))/* Endpoint Control and Status + * Register */ +#define AT91_UDP_CSR_TXCOMP (0x1 << 0) /* Generates an IN packet with data + * previously written in the DPR */ +#define AT91_UDP_CSR_RX_DATA_BK0 (0x1 << 1) /* Receive Data Bank 0 */ +#define AT91_UDP_CSR_RXSETUP (0x1 << 2) /* Sends STALL to the Host + * (Control endpoints) */ +#define AT91_UDP_CSR_ISOERROR (0x1 << 3) /* Isochronous error + * (Isochronous endpoints) */ +#define AT91_UDP_CSR_STALLSENT (0x1 << 3) /* Stall sent (Control, bulk, + * interrupt endpoints) */ +#define AT91_UDP_CSR_TXPKTRDY (0x1 << 4) /* Transmit Packet Ready */ +#define AT91_UDP_CSR_FORCESTALL (0x1 << 5) /* Force Stall (used by + * Control, Bulk and + * Isochronous endpoints). */ +#define AT91_UDP_CSR_RX_DATA_BK1 (0x1 << 6) /* Receive Data Bank 1 (only + * used by endpoints with + * ping-pong attributes). */ +#define AT91_UDP_CSR_DIR (0x1 << 7) /* Transfer Direction */ +#define AT91_UDP_CSR_ET_MASK (0x7 << 8) /* Endpoint transfer type mask */ +#define AT91_UDP_CSR_ET_CTRL (0x0 << 8) /* Control IN+OUT */ +#define AT91_UDP_CSR_ET_ISO (0x1 << 8) /* Isochronous */ +#define AT91_UDP_CSR_ET_BULK (0x2 << 8) /* Bulk */ +#define AT91_UDP_CSR_ET_INT (0x3 << 8) /* Interrupt */ +#define AT91_UDP_CSR_ET_DIR_OUT (0x0 << 8) /* OUT tokens */ +#define AT91_UDP_CSR_ET_DIR_IN (0x4 << 8) /* IN tokens */ +#define AT91_UDP_CSR_DTGLE (0x1 << 11) /* Data Toggle */ +#define AT91_UDP_CSR_EPEDS (0x1 << 15) /* Endpoint Enable Disable */ +#define AT91_UDP_CSR_RXBYTECNT (0x7FF << 16) /* Number Of Bytes Available + * in the FIFO */ + +#define AT91_UDP_FDR(n) (0x50 + (4*(n)))/* Endpoint FIFO Data Register */ +#define AT91_UDP_RES3 0x70 /* Reserved 3 */ +#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */ +#define AT91_UDP_TXVC_DIS (0x1 << 8) + +#define AT91_UDP_EP_MAX 6 /* maximum number of endpoints + * supported */ + +#define AT91_UDP_READ_4(sc, reg) \ + bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) + +#define AT91_UDP_WRITE_4(sc, reg, data) \ + bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) + +struct at91dci_td; + +typedef uint8_t (at91dci_cmd_t)(struct at91dci_td *td); + +struct at91dci_td { + bus_space_tag_t io_tag; + bus_space_handle_t io_hdl; + struct at91dci_td *obj_next; + at91dci_cmd_t *func; + struct usb2_page_cache *pc; + uint32_t offset; + uint32_t remainder; + uint16_t max_packet_size; + uint8_t status_reg; + uint8_t fifo_reg; + uint8_t fifo_bank:1; + uint8_t error:1; + uint8_t alt_next:1; + uint8_t short_pkt:1; + uint8_t support_multi_buffer:1; + uint8_t did_stall:1; +}; + +struct at91dci_std_temp { + at91dci_cmd_t *func; + struct usb2_page_cache *pc; + struct at91dci_td *td; + struct at91dci_td *td_next; + uint32_t len; + uint32_t offset; + uint16_t max_frame_size; + uint8_t short_pkt; + /* + * short_pkt = 0: transfer should be short terminated + * short_pkt = 1: transfer should not be short terminated + */ + uint8_t setup_alt_next; +}; + +struct at91dci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union at91dci_hub_temp { + uWord wValue; + struct usb2_port_status ps; +}; + +struct at91dci_ep_flags { + uint8_t fifo_bank:1; /* hardware specific */ +}; + +struct at91dci_flags { + uint8_t change_connect:1; + uint8_t change_suspend:1; + uint8_t status_suspend:1; /* set if suspended */ + uint8_t status_vbus:1; /* set if present */ + uint8_t status_bus_reset:1; /* set if reset complete */ + uint8_t remote_wakeup:1; + uint8_t self_powered:1; + uint8_t clocks_off:1; + uint8_t port_powered:1; + uint8_t port_enabled:1; + uint8_t d_pulled_up:1; +}; + +struct at91dci_softc { + struct usb2_bus sc_bus; + union at91dci_hub_temp sc_hub_temp; + LIST_HEAD(, usb2_xfer) sc_interrupt_list_head; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + struct usb2_config_td sc_config_td; + + struct resource *sc_io_res; + struct resource *sc_irq_res; + void *sc_intr_hdl; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + void (*sc_clocks_on) (void *arg); + void (*sc_clocks_off) (void *arg); + void *sc_clocks_arg; + + void (*sc_pull_up) (void *arg); + void (*sc_pull_down) (void *arg); + void *sc_pull_arg; + + uint8_t sc_rt_addr; /* root HUB address */ + uint8_t sc_dv_addr; /* device address */ + uint8_t sc_conf; /* root HUB config */ + + uint8_t sc_hub_idata[1]; + + struct at91dci_flags sc_flags; + struct at91dci_ep_flags sc_ep_flags[AT91_UDP_EP_MAX]; +}; + +/* prototypes */ + +usb2_error_t at91dci_init(struct at91dci_softc *sc); +void at91dci_uninit(struct at91dci_softc *sc); +void at91dci_suspend(struct at91dci_softc *sc); +void at91dci_resume(struct at91dci_softc *sc); +void at91dci_interrupt(struct at91dci_softc *sc); + +#endif /* _AT9100_DCI_H_ */ diff --git a/sys/dev/usb2/controller/at91dci_atmelarm.c b/sys/dev/usb2/controller/at91dci_atmelarm.c new file mode 100644 index 000000000000..0a6f96112b1a --- /dev/null +++ b/sys/dev/usb2/controller/at91dci_atmelarm.c @@ -0,0 +1,361 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2007-2008 Hans Petter Selasky. 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define MEM_RID 0 + +/* Pin Definitions - do they belong here or somewhere else ? */ + +#define VBUS_MASK AT91C_PIO_PB24 +#define VBUS_BASE AT91RM92_PIOB_BASE + +#define PULLUP_MASK AT91C_PIO_PB22 +#define PULLUP_BASE AT91RM92_PIOB_BASE + +static device_probe_t at91_udp_probe; +static device_attach_t at91_udp_attach; +static device_detach_t at91_udp_detach; +static device_shutdown_t at91_udp_shutdown; + +struct at91_udp_softc { + struct at91dci_softc sc_dci; /* must be first */ + struct at91_pmc_clock *sc_iclk; + struct at91_pmc_clock *sc_fclk; + struct resource *sc_vbus_irq_res; + void *sc_vbus_intr_hdl; +}; + +static void +at91_vbus_interrupt(struct at91_udp_softc *sc) +{ + uint32_t temp; + uint8_t vbus_val; + + /* XXX temporary clear interrupts here */ + + temp = at91_pio_gpio_clear_interrupt(VBUS_BASE); + + /* just forward it */ + + vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK); + (sc->sc_dci.sc_bus.methods->vbus_interrupt) + (&sc->sc_dci.sc_bus, vbus_val); + return; +} + +static void +at91_udp_clocks_on(void *arg) +{ + struct at91_udp_softc *sc = arg; + + at91_pmc_clock_enable(sc->sc_iclk); + at91_pmc_clock_enable(sc->sc_fclk); + return; +} + +static void +at91_udp_clocks_off(void *arg) +{ + struct at91_udp_softc *sc = arg; + + at91_pmc_clock_disable(sc->sc_fclk); + at91_pmc_clock_disable(sc->sc_iclk); + return; +} + +static void +at91_udp_pull_up(void *arg) +{ + at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); + return; +} + +static void +at91_udp_pull_down(void *arg) +{ + at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); + return; +} + +static int +at91_udp_probe(device_t dev) +{ + device_set_desc(dev, "AT91 integrated AT91_UDP controller"); + return (0); +} + +static int +at91_udp_attach(device_t dev) +{ + struct at91_udp_softc *sc = device_get_softc(dev); + int err; + int rid; + + if (sc == NULL) { + return (ENXIO); + } + /* setup AT9100 USB device controller interface softc */ + + sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on; + sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off; + sc->sc_dci.sc_clocks_arg = sc; + sc->sc_dci.sc_pull_up = &at91_udp_pull_up; + sc->sc_dci.sc_pull_down = &at91_udp_pull_down; + sc->sc_dci.sc_pull_arg = sc; + + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_dci.sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + return (ENOMEM); + } + /* + * configure VBUS input pin, enable deglitch and enable + * interrupt : + */ + at91_pio_use_gpio(VBUS_BASE, VBUS_MASK); + at91_pio_gpio_input(VBUS_BASE, VBUS_MASK); + at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1); + at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 1); + + /* + * configure PULLUP output pin : + */ + at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK); + at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0); + + at91_udp_pull_down(sc); + + /* wait 10ms for pulldown to stabilise */ + usb2_pause_mtx(NULL, 10); + + sc->sc_iclk = at91_pmc_clock_ref("udc_clk"); + sc->sc_fclk = at91_pmc_clock_ref("udpck"); + + rid = MEM_RID; + sc->sc_dci.sc_io_res = + bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + + if (!(sc->sc_dci.sc_io_res)) { + err = ENOMEM; + goto error; + } + sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res); + sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res); + sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res); + + rid = 0; + sc->sc_dci.sc_irq_res = + bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (!(sc->sc_dci.sc_irq_res)) { + goto error; + } + rid = 1; + sc->sc_vbus_irq_res = + bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (!(sc->sc_vbus_irq_res)) { + goto error; + } + sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_dci.sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); + + err = usb2_config_td_setup(&sc->sc_dci.sc_config_td, sc, + &sc->sc_dci.sc_bus.mtx, NULL, 0, 4); + if (err) { + device_printf(dev, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); +#endif + if (err) { + sc->sc_dci.sc_intr_hdl = NULL; + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)at91_vbus_interrupt, sc, &sc->sc_vbus_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)at91_vbus_interrupt, sc, &sc->sc_vbus_intr_hdl); +#endif + if (err) { + sc->sc_vbus_intr_hdl = NULL; + goto error; + } + err = at91dci_init(&sc->sc_dci); + if (!err) { + err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); + } + if (err) { + goto error; + } else { + /* poll VBUS one time */ + at91_vbus_interrupt(sc); + } + return (0); + +error: + at91_udp_detach(dev); + return (ENXIO); +} + +static int +at91_udp_detach(device_t dev) +{ + struct at91_udp_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_dci.sc_bus.bdev) { + bdev = sc->sc_dci.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + /* disable Transceiver */ + AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); + + /* disable and clear all interrupts */ + AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF); + AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF); + + /* disable VBUS interrupt */ + at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0); + + if (sc->sc_vbus_irq_res && sc->sc_vbus_intr_hdl) { + err = bus_teardown_intr(dev, sc->sc_vbus_irq_res, + sc->sc_vbus_intr_hdl); + sc->sc_vbus_intr_hdl = NULL; + } + if (sc->sc_vbus_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 1, + sc->sc_vbus_irq_res); + sc->sc_vbus_irq_res = NULL; + } + if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) { + /* + * only call at91_udp_uninit() after at91_udp_init() + */ + at91dci_uninit(&sc->sc_dci); + + err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res, + sc->sc_dci.sc_intr_hdl); + sc->sc_dci.sc_intr_hdl = NULL; + } + if (sc->sc_dci.sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->sc_dci.sc_irq_res); + sc->sc_dci.sc_irq_res = NULL; + } + if (sc->sc_dci.sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, + sc->sc_dci.sc_io_res); + sc->sc_dci.sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_dci.sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); + + /* disable clocks */ + at91_pmc_clock_disable(sc->sc_iclk); + at91_pmc_clock_disable(sc->sc_fclk); + at91_pmc_clock_deref(sc->sc_fclk); + at91_pmc_clock_deref(sc->sc_iclk); + + return (0); +} + +static int +at91_udp_shutdown(device_t dev) +{ + struct at91_udp_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_shutdown(dev); + if (err) + return (err); + + at91dci_uninit(&sc->sc_dci); + + return (0); +} + +static device_method_t at91_udp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, at91_udp_probe), + DEVMETHOD(device_attach, at91_udp_attach), + DEVMETHOD(device_detach, at91_udp_detach), + DEVMETHOD(device_shutdown, at91_udp_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t at91_udp_driver = { + "at91_udp", + at91_udp_methods, + sizeof(struct at91_udp_softc), +}; + +static devclass_t at91_udp_devclass; + +DRIVER_MODULE(at91_udp, atmelarm, at91_udp_driver, at91_udp_devclass, 0, 0); diff --git a/sys/dev/usb2/controller/ehci2.c b/sys/dev/usb2/controller/ehci2.c new file mode 100644 index 000000000000..218a3620e511 --- /dev/null +++ b/sys/dev/usb2/controller/ehci2.c @@ -0,0 +1,3854 @@ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2004 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 2004 Lennart Augustsson. All rights reserved. + * Copyright (c) 2004 Charles M. Hannum. 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. + */ + +/* + * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. + * + * The EHCI 0.96 spec can be found at + * http://developer.intel.com/technology/usb/download/ehci-r096.pdf + * 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) command failures are not recovered correctly + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#define USB_DEBUG_VAR ehcidebug +#define usb2_config_td_cc ehci_config_copy +#define usb2_config_td_softc ehci_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define EHCI_BUS2SC(bus) ((ehci_softc_t *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((ehci_softc_t *)0)->sc_bus)))) + +#if USB_DEBUG +static int ehcidebug = 0; +static int ehcinohighspeed = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); +SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, debug, CTLFLAG_RW, + &ehcidebug, 0, "Debug level"); +SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, no_hs, CTLFLAG_RW, + &ehcinohighspeed, 0, "Disable High Speed USB"); + +static void ehci_dump_regs(ehci_softc_t *sc); +static void ehci_dump_sqh(ehci_qh_t *sqh); + +#endif + +#define EHCI_INTR_ENDPT 1 + +extern struct usb2_bus_methods ehci_bus_methods; +extern struct usb2_pipe_methods ehci_device_bulk_methods; +extern struct usb2_pipe_methods ehci_device_ctrl_methods; +extern struct usb2_pipe_methods ehci_device_intr_methods; +extern struct usb2_pipe_methods ehci_device_isoc_fs_methods; +extern struct usb2_pipe_methods ehci_device_isoc_hs_methods; +extern struct usb2_pipe_methods ehci_root_ctrl_methods; +extern struct usb2_pipe_methods ehci_root_intr_methods; + +static usb2_config_td_command_t ehci_root_ctrl_task; +static void ehci_do_poll(struct usb2_bus *bus); +static void ehci_root_ctrl_poll(struct ehci_softc *sc); +static void ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error); +static uint8_t ehci_check_transfer(struct usb2_xfer *xfer); +static void ehci_timeout(void *arg); + +static usb2_sw_transfer_func_t ehci_root_intr_done; +static usb2_sw_transfer_func_t ehci_root_ctrl_done; + +struct ehci_std_temp { + struct usb2_page_cache *pc; + ehci_qtd_t *td; + ehci_qtd_t *td_next; + uint32_t average; + uint32_t qtd_status; + uint32_t len; + uint16_t max_frame_size; + uint8_t shortpkt; + uint8_t auto_data_toggle; + uint8_t setup_alt_next; + uint8_t short_frames_ok; +}; + +void +ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) +{ + struct ehci_softc *sc = EHCI_BUS2SC(bus); + uint32_t i; + + cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, + sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN); + + cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg, + sizeof(ehci_qh_t), EHCI_QH_ALIGN); + + for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { + cb(bus, sc->sc_hw.intr_start_pc + i, + sc->sc_hw.intr_start_pg + i, + sizeof(ehci_qh_t), EHCI_QH_ALIGN); + } + + for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { + cb(bus, sc->sc_hw.isoc_hs_start_pc + i, + sc->sc_hw.isoc_hs_start_pg + i, + sizeof(ehci_itd_t), EHCI_ITD_ALIGN); + } + + for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { + cb(bus, sc->sc_hw.isoc_fs_start_pc + i, + sc->sc_hw.isoc_fs_start_pg + i, + sizeof(ehci_sitd_t), EHCI_SITD_ALIGN); + } + return; +} + +static usb2_error_t +ehci_hc_reset(ehci_softc_t *sc) +{ + uint32_t hcr; + uint32_t n; + + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ + + for (n = 0; n != 100; n++) { + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + hcr = EOREAD4(sc, EHCI_USBSTS); + if (hcr & EHCI_STS_HCH) { + hcr = 0; + break; + } + } + + /* + * Fall through and try reset anyway even though + * Table 2-9 in the EHCI spec says this will result + * in undefined behavior. + */ + + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); + for (n = 0; n != 100; n++) { + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + hcr = EOREAD4(sc, EHCI_USBCMD); + if (!(hcr & EHCI_CMD_HCRESET)) { + hcr = 0; + break; + } + } + + if (hcr) { + return (USB_ERR_IOERROR); + } + return (0); +} + +usb2_error_t +ehci_init(ehci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint32_t version; + uint32_t sparams; + uint32_t cparams; + uint32_t hcr; + uint16_t i; + uint16_t x; + uint16_t y; + uint16_t bit; + usb2_error_t err = 0; + + mtx_lock(&sc->sc_bus.mtx); + + DPRINTF("start\n"); + + usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.mtx, + CALLOUT_RETURNUNLOCKED); + +#if USB_DEBUG + if (ehcidebug > 2) { + ehci_dump_regs(sc); + } +#endif + + 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("sparams=0x%x\n", sparams); + + sc->sc_noport = EHCI_HCS_N_PORTS(sparams); + cparams = EREAD4(sc, EHCI_HCCPARAMS); + DPRINTF("cparams=0x%x\n", cparams); + + if (EHCI_HCC_64BIT(cparams)) { + DPRINTF("HCC uses 64-bit structures\n"); + + /* MUST clear segment register if 64 bit capable */ + EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + } + sc->sc_bus.usbrev = USB_REV_2_0; + + /* Reset the controller */ + DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)); + + err = ehci_hc_reset(sc); + if (err) { + device_printf(sc->sc_bus.bdev, "reset timeout\n"); + goto done; + } + /* + * use current frame-list-size selection 0: 1024*4 bytes 1: 512*4 + * bytes 2: 256*4 bytes 3: unknown + */ + if (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) { + device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n"); + err = USB_ERR_IOERROR; + goto done; + } + /* set up the bus struct */ + sc->sc_bus.methods = &ehci_bus_methods; + + sc->sc_eintrs = EHCI_NORMAL_INTRS; + + for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { + ehci_qh_t *qh; + + usb2_get_page(sc->sc_hw.intr_start_pc + i, 0, &buf_res); + + qh = buf_res.buffer; + + /* initialize page cache pointer */ + + qh->page_cache = sc->sc_hw.intr_start_pc + i; + + /* store a pointer to queue head */ + + sc->sc_intr_p_last[i] = qh; + + qh->qh_self = + htole32(buf_res.physaddr) | + htole32(EHCI_LINK_QH); + + qh->qh_endp = + htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); + qh->qh_endphub = + htole32(EHCI_QH_SET_MULT(1)); + qh->qh_curqtd = 0; + + qh->qh_qtd.qtd_next = + htole32(EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = + htole32(EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_status = + htole32(EHCI_QTD_HALTED); + } + + /* + * the QHs are arranged to give poll intervals that are + * powers of 2 times 1ms + */ + bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; + while (bit) { + x = bit; + while (x & bit) { + ehci_qh_t *qh_x; + ehci_qh_t *qh_y; + + y = (x ^ bit) | (bit / 2); + + qh_x = sc->sc_intr_p_last[x]; + qh_y = sc->sc_intr_p_last[y]; + + /* + * the next QH has half the poll interval + */ + qh_x->qh_link = qh_y->qh_self; + + x++; + } + bit >>= 1; + } + + if (1) { + ehci_qh_t *qh; + + qh = sc->sc_intr_p_last[0]; + + /* the last (1ms) QH terminates */ + qh->qh_link = htole32(EHCI_LINK_TERMINATE); + } + for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { + ehci_sitd_t *sitd; + ehci_itd_t *itd; + + usb2_get_page(sc->sc_hw.isoc_fs_start_pc + i, 0, &buf_res); + + sitd = buf_res.buffer; + + /* initialize page cache pointer */ + + sitd->page_cache = sc->sc_hw.isoc_fs_start_pc + i; + + /* store a pointer to the transfer descriptor */ + + sc->sc_isoc_fs_p_last[i] = sitd; + + /* initialize full speed isochronous */ + + sitd->sitd_self = + htole32(buf_res.physaddr) | + htole32(EHCI_LINK_SITD); + + sitd->sitd_back = + htole32(EHCI_LINK_TERMINATE); + + sitd->sitd_next = + sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self; + + + usb2_get_page(sc->sc_hw.isoc_hs_start_pc + i, 0, &buf_res); + + itd = buf_res.buffer; + + /* initialize page cache pointer */ + + itd->page_cache = sc->sc_hw.isoc_hs_start_pc + i; + + /* store a pointer to the transfer descriptor */ + + sc->sc_isoc_hs_p_last[i] = itd; + + /* initialize high speed isochronous */ + + itd->itd_self = + htole32(buf_res.physaddr) | + htole32(EHCI_LINK_ITD); + + itd->itd_next = + sitd->sitd_self; + } + + usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); + + if (1) { + uint32_t *pframes; + + pframes = buf_res.buffer; + + /* + * execution order: + * pframes -> high speed isochronous -> + * full speed isochronous -> interrupt QH's + */ + for (i = 0; i < EHCI_FRAMELIST_COUNT; i++) { + pframes[i] = sc->sc_isoc_hs_p_last + [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self; + } + } + /* setup sync list pointer */ + EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); + + usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); + + if (1) { + + ehci_qh_t *qh; + + qh = buf_res.buffer; + + /* initialize page cache pointer */ + + qh->page_cache = &sc->sc_hw.async_start_pc; + + /* store a pointer to the queue head */ + + sc->sc_async_p_last = qh; + + /* init dummy QH that starts the async list */ + + qh->qh_self = + htole32(buf_res.physaddr) | + htole32(EHCI_LINK_QH); + + /* fill the QH */ + qh->qh_endp = + htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); + qh->qh_endphub = htole32(EHCI_QH_SET_MULT(1)); + qh->qh_link = qh->qh_self; + qh->qh_curqtd = 0; + + /* fill the overlay qTD */ + qh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); + } + /* flush all cache into memory */ + + usb2_bus_mem_flush_all(&sc->sc_bus, &ehci_iterate_hw_softc); + +#if USB_DEBUG + if (ehcidebug) { + ehci_dump_sqh(sc->sc_async_p_last); + } +#endif + + /* setup async list pointer */ + EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); + + + /* enable interrupts */ + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + /* turn on controller */ + EOWRITE4(sc, EHCI_USBCMD, + EHCI_CMD_ITC_1 | /* 1 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++) { + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; + if (!hcr) { + break; + } + } + if (hcr) { + device_printf(sc->sc_bus.bdev, "run timeout\n"); + err = USB_ERR_IOERROR; + goto done; + } +done: + mtx_unlock(&sc->sc_bus.mtx); + + if (!err) { + /* catch any lost interrupts */ + ehci_do_poll(&sc->sc_bus); + } + return (err); +} + +/* + * shut down the controller when the system is going down + */ +void +ehci_detach(struct ehci_softc *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + + usb2_callout_stop(&sc->sc_tmo_pcd); + + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + if (ehci_hc_reset(sc)) { + DPRINTF("reset failed!\n"); + } + /* XXX let stray task complete */ + usb2_pause_mtx(&sc->sc_bus.mtx, 50); + + mtx_unlock(&sc->sc_bus.mtx); + + usb2_callout_drain(&sc->sc_tmo_pcd); + + return; +} + +void +ehci_suspend(struct ehci_softc *sc) +{ + uint32_t cmd; + uint32_t hcr; + uint8_t i; + + mtx_lock(&sc->sc_bus.mtx); + + 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; + } + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + } + + if (hcr != 0) { + device_printf(sc->sc_bus.bdev, "reset timeout\n"); + } + 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; + } + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + } + + if (hcr != EHCI_STS_HCH) { + device_printf(sc->sc_bus.bdev, + "config timeout\n"); + } + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +void +ehci_resume(struct ehci_softc *sc) +{ + struct usb2_page_search buf_res; + uint32_t cmd; + uint32_t hcr; + uint8_t i; + + mtx_lock(&sc->sc_bus.mtx); + + /* restore things in case the bios doesn't */ + EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + + usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); + EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); + + usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); + EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.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) { + usb2_pause_mtx(&sc->sc_bus.mtx, + 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; + } + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + } + if (hcr == EHCI_STS_HCH) { + device_printf(sc->sc_bus.bdev, "config timeout\n"); + } + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_RESUME_WAIT); + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch any lost interrupts */ + ehci_do_poll(&sc->sc_bus); + + return; +} + +void +ehci_shutdown(ehci_softc_t *sc) +{ + DPRINTF("stopping the HC\n"); + + mtx_lock(&sc->sc_bus.mtx); + + if (ehci_hc_reset(sc)) { + DPRINTF("reset failed!\n"); + } + mtx_unlock(&sc->sc_bus.mtx); +} + +#if USB_DEBUG +static void +ehci_dump_regs(ehci_softc_t *sc) +{ + uint32_t i; + + i = EOREAD4(sc, EHCI_USBCMD); + printf("cmd=0x%08x\n", i); + + if (i & EHCI_CMD_ITC_1) + printf(" EHCI_CMD_ITC_1\n"); + if (i & EHCI_CMD_ITC_2) + printf(" EHCI_CMD_ITC_2\n"); + if (i & EHCI_CMD_ITC_4) + printf(" EHCI_CMD_ITC_4\n"); + if (i & EHCI_CMD_ITC_8) + printf(" EHCI_CMD_ITC_8\n"); + if (i & EHCI_CMD_ITC_16) + printf(" EHCI_CMD_ITC_16\n"); + if (i & EHCI_CMD_ITC_32) + printf(" EHCI_CMD_ITC_32\n"); + if (i & EHCI_CMD_ITC_64) + printf(" EHCI_CMD_ITC_64\n"); + if (i & EHCI_CMD_ASPME) + printf(" EHCI_CMD_ASPME\n"); + if (i & EHCI_CMD_ASPMC) + printf(" EHCI_CMD_ASPMC\n"); + if (i & EHCI_CMD_LHCR) + printf(" EHCI_CMD_LHCR\n"); + if (i & EHCI_CMD_IAAD) + printf(" EHCI_CMD_IAAD\n"); + if (i & EHCI_CMD_ASE) + printf(" EHCI_CMD_ASE\n"); + if (i & EHCI_CMD_PSE) + printf(" EHCI_CMD_PSE\n"); + if (i & EHCI_CMD_FLS_M) + printf(" EHCI_CMD_FLS_M\n"); + if (i & EHCI_CMD_HCRESET) + printf(" EHCI_CMD_HCRESET\n"); + if (i & EHCI_CMD_RS) + printf(" EHCI_CMD_RS\n"); + + i = EOREAD4(sc, EHCI_USBSTS); + + printf("sts=0x%08x\n", i); + + if (i & EHCI_STS_ASS) + printf(" EHCI_STS_ASS\n"); + if (i & EHCI_STS_PSS) + printf(" EHCI_STS_PSS\n"); + if (i & EHCI_STS_REC) + printf(" EHCI_STS_REC\n"); + if (i & EHCI_STS_HCH) + printf(" EHCI_STS_HCH\n"); + if (i & EHCI_STS_IAA) + printf(" EHCI_STS_IAA\n"); + if (i & EHCI_STS_HSE) + printf(" EHCI_STS_HSE\n"); + if (i & EHCI_STS_FLR) + printf(" EHCI_STS_FLR\n"); + if (i & EHCI_STS_PCD) + printf(" EHCI_STS_PCD\n"); + if (i & EHCI_STS_ERRINT) + printf(" EHCI_STS_ERRINT\n"); + if (i & EHCI_STS_INT) + printf(" EHCI_STS_INT\n"); + + printf("ien=0x%08x\n", + EOREAD4(sc, EHCI_USBINTR)); + 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++) { + printf("port %d status=0x%08x\n", i, + EOREAD4(sc, EHCI_PORTSC(i))); + } + return; +} + +static void +ehci_dump_link(uint32_t link, int type) +{ + link = le32toh(link); + printf("0x%08x", link); + if (link & EHCI_LINK_TERMINATE) + printf(""); + else { + printf("<"); + if (type) { + switch (EHCI_LINK_TYPE(link)) { + case EHCI_LINK_ITD: + printf("ITD"); + break; + case EHCI_LINK_QH: + printf("QH"); + break; + case EHCI_LINK_SITD: + printf("SITD"); + break; + case EHCI_LINK_FSTN: + printf("FSTN"); + break; + } + } + printf(">"); + } + return; +} + +static void +ehci_dump_qtd(ehci_qtd_t *qtd) +{ + uint32_t s; + + printf(" next="); + ehci_dump_link(qtd->qtd_next, 0); + printf(" altnext="); + ehci_dump_link(qtd->qtd_altnext, 0); + printf("\n"); + s = le32toh(qtd->qtd_status); + 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)); + printf(" cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n", + EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), + (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE", + (s & EHCI_QTD_HALTED) ? "-HALTED" : "", + (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "", + (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "", + (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "", + (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "", + (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "", + (s & EHCI_QTD_PINGSTATE) ? "-PING" : ""); + + for (s = 0; s < 5; s++) { + printf(" buffer[%d]=0x%08x\n", s, + le32toh(qtd->qtd_buffer[s])); + } + for (s = 0; s < 5; s++) { + printf(" buffer_hi[%d]=0x%08x\n", s, + le32toh(qtd->qtd_buffer_hi[s])); + } + return; +} + +static uint8_t +ehci_dump_sqtd(ehci_qtd_t *sqtd) +{ + uint8_t temp; + + usb2_pc_cpu_invalidate(sqtd->page_cache); + printf("QTD(%p) at 0x%08x:\n", sqtd, le32toh(sqtd->qtd_self)); + ehci_dump_qtd(sqtd); + temp = (sqtd->qtd_next & htole32(EHCI_LINK_TERMINATE)) ? 1 : 0; + return (temp); +} + +static void +ehci_dump_sqtds(ehci_qtd_t *sqtd) +{ + uint16_t i; + uint8_t stop; + + stop = 0; + for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) { + stop = ehci_dump_sqtd(sqtd); + } + if (sqtd) { + printf("dump aborted, too many TDs\n"); + } + return; +} + +static void +ehci_dump_sqh(ehci_qh_t *qh) +{ + uint32_t endp, endphub; + + usb2_pc_cpu_invalidate(qh->page_cache); + printf("QH(%p) at 0x%08x:\n", qh, le32toh(qh->qh_self) & ~0x1F); + printf(" link="); + ehci_dump_link(qh->qh_link, 1); + printf("\n"); + endp = le32toh(qh->qh_endp); + printf(" endp=0x%08x\n", endp); + 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)); + 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 = le32toh(qh->qh_endphub); + printf(" endphub=0x%08x\n", endphub); + 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)); + printf(" curqtd="); + ehci_dump_link(qh->qh_curqtd, 0); + printf("\n"); + printf("Overlay qTD:\n"); + ehci_dump_qtd((void *)&qh->qh_qtd); + return; +} + +static void +ehci_dump_sitd(ehci_sitd_t *sitd) +{ + usb2_pc_cpu_invalidate(sitd->page_cache); + printf("SITD(%p) at 0x%08x\n", sitd, le32toh(sitd->sitd_self) & ~0x1F); + printf(" next=0x%08x\n", le32toh(sitd->sitd_next)); + printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", + le32toh(sitd->sitd_portaddr), + (sitd->sitd_portaddr & htole32(EHCI_SITD_SET_DIR_IN)) + ? "in" : "out", + EHCI_SITD_GET_ADDR(le32toh(sitd->sitd_portaddr)), + EHCI_SITD_GET_ENDPT(le32toh(sitd->sitd_portaddr)), + EHCI_SITD_GET_PORT(le32toh(sitd->sitd_portaddr)), + EHCI_SITD_GET_HUBA(le32toh(sitd->sitd_portaddr))); + printf(" mask=0x%08x\n", le32toh(sitd->sitd_mask)); + printf(" status=0x%08x <%s> len=0x%x\n", le32toh(sitd->sitd_status), + (sitd->sitd_status & htole32(EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", + EHCI_SITD_GET_LEN(le32toh(sitd->sitd_status))); + printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", + le32toh(sitd->sitd_back), + le32toh(sitd->sitd_bp[0]), + le32toh(sitd->sitd_bp[1]), + le32toh(sitd->sitd_bp_hi[0]), + le32toh(sitd->sitd_bp_hi[1])); + return; +} + +static void +ehci_dump_itd(ehci_itd_t *itd) +{ + usb2_pc_cpu_invalidate(itd->page_cache); + printf("ITD(%p) at 0x%08x\n", itd, le32toh(itd->itd_self) & ~0x1F); + printf(" next=0x%08x\n", le32toh(itd->itd_next)); + printf(" status[0]=0x%08x; <%s>\n", le32toh(itd->itd_status[0]), + (itd->itd_status[0] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[1]=0x%08x; <%s>\n", le32toh(itd->itd_status[1]), + (itd->itd_status[1] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[2]=0x%08x; <%s>\n", le32toh(itd->itd_status[2]), + (itd->itd_status[2] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[3]=0x%08x; <%s>\n", le32toh(itd->itd_status[3]), + (itd->itd_status[3] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[4]=0x%08x; <%s>\n", le32toh(itd->itd_status[4]), + (itd->itd_status[4] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[5]=0x%08x; <%s>\n", le32toh(itd->itd_status[5]), + (itd->itd_status[5] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[6]=0x%08x; <%s>\n", le32toh(itd->itd_status[6]), + (itd->itd_status[6] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[7]=0x%08x; <%s>\n", le32toh(itd->itd_status[7]), + (itd->itd_status[7] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" bp[0]=0x%08x\n", le32toh(itd->itd_bp[0])); + printf(" addr=0x%02x; endpt=0x%01x\n", + EHCI_ITD_GET_ADDR(le32toh(itd->itd_bp[0])), + EHCI_ITD_GET_ENDPT(le32toh(itd->itd_bp[0]))); + printf(" bp[1]=0x%08x\n", le32toh(itd->itd_bp[1])); + printf(" dir=%s; mpl=0x%02x\n", + (le32toh(itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", + EHCI_ITD_GET_MPL(le32toh(itd->itd_bp[1]))); + printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", + le32toh(itd->itd_bp[2]), + le32toh(itd->itd_bp[3]), + le32toh(itd->itd_bp[4]), + le32toh(itd->itd_bp[5]), + le32toh(itd->itd_bp[6])); + printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" + " 0x%08x,0x%08x,0x%08x\n", + le32toh(itd->itd_bp_hi[0]), + le32toh(itd->itd_bp_hi[1]), + le32toh(itd->itd_bp_hi[2]), + le32toh(itd->itd_bp_hi[3]), + le32toh(itd->itd_bp_hi[4]), + le32toh(itd->itd_bp_hi[5]), + le32toh(itd->itd_bp_hi[6])); + return; +} + +static void +ehci_dump_isoc(ehci_softc_t *sc) +{ + ehci_itd_t *itd; + ehci_sitd_t *sitd; + uint16_t max = 1000; + uint16_t pos; + + pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + printf("%s: isochronous dump from frame 0x%03x:\n", + __FUNCTION__, pos); + + itd = sc->sc_isoc_hs_p_last[pos]; + sitd = sc->sc_isoc_fs_p_last[pos]; + + while (itd && max && max--) { + ehci_dump_itd(itd); + itd = itd->prev; + } + + while (sitd && max && max--) { + ehci_dump_sitd(sitd); + sitd = sitd->prev; + } + return; +} + +#endif + +static void +ehci_transfer_intr_enqueue(struct usb2_xfer *xfer) +{ + /* check for early completion */ + if (ehci_check_transfer(xfer)) { + return; + } + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout); + } + return; +} + +#define EHCI_APPEND_FS_TD(std,last) (last) = _ehci_append_fs_td(std,last) +static ehci_sitd_t * +_ehci_append_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) +{ + DPRINTFN(11, "%p to %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->next = last->next; + std->sitd_next = last->sitd_next; + + std->prev = last; + + usb2_pc_cpu_flush(std->page_cache); + + /* + * the last->next->prev is never followed: std->next->prev = std; + */ + last->next = std; + last->sitd_next = std->sitd_self; + + usb2_pc_cpu_flush(last->page_cache); + + return (std); +} + +#define EHCI_APPEND_HS_TD(std,last) (last) = _ehci_append_hs_td(std,last) +static ehci_itd_t * +_ehci_append_hs_td(ehci_itd_t *std, ehci_itd_t *last) +{ + DPRINTFN(11, "%p to %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->next = last->next; + std->itd_next = last->itd_next; + + std->prev = last; + + usb2_pc_cpu_flush(std->page_cache); + + /* + * the last->next->prev is never followed: std->next->prev = std; + */ + last->next = std; + last->itd_next = std->itd_self; + + usb2_pc_cpu_flush(last->page_cache); + + return (std); +} + +#define EHCI_APPEND_QH(sqh,last) (last) = _ehci_append_qh(sqh,last) +static ehci_qh_t * +_ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) +{ + DPRINTFN(11, "%p to %p\n", sqh, last); + + /* (sc->sc_bus.mtx) must be locked */ + + sqh->next = last->next; + sqh->qh_link = last->qh_link; + + sqh->prev = last; + + usb2_pc_cpu_flush(sqh->page_cache); + + /* + * the last->next->prev is never followed: sqh->next->prev = sqh; + */ + + last->next = sqh; + last->qh_link = sqh->qh_self; + + usb2_pc_cpu_flush(last->page_cache); + +#if USB_DEBUG + if (ehcidebug > 5) { + printf("%s:\n", __FUNCTION__); + ehci_dump_sqh(sqh); + } +#endif + return (sqh); +} + +#define EHCI_REMOVE_FS_TD(std,last) (last) = _ehci_remove_fs_td(std,last) +static ehci_sitd_t * +_ehci_remove_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) +{ + DPRINTFN(11, "%p from %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->prev->next = std->next; + std->prev->sitd_next = std->sitd_next; + + usb2_pc_cpu_flush(std->prev->page_cache); + + if (std->next) { + std->next->prev = std->prev; + usb2_pc_cpu_flush(std->next->page_cache); + } + return ((last == std) ? std->prev : last); +} + +#define EHCI_REMOVE_HS_TD(std,last) (last) = _ehci_remove_hs_td(std,last) +static ehci_itd_t * +_ehci_remove_hs_td(ehci_itd_t *std, ehci_itd_t *last) +{ + DPRINTFN(11, "%p from %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->prev->next = std->next; + std->prev->itd_next = std->itd_next; + + usb2_pc_cpu_flush(std->prev->page_cache); + + if (std->next) { + std->next->prev = std->prev; + usb2_pc_cpu_flush(std->next->page_cache); + } + return ((last == std) ? std->prev : last); +} + +#define EHCI_REMOVE_QH(sqh,last) (last) = _ehci_remove_qh(sqh,last) +static ehci_qh_t * +_ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) +{ + DPRINTFN(11, "%p from %p\n", sqh, last); + + /* (sc->sc_bus.mtx) must be locked */ + + /* only remove if not removed from a queue */ + if (sqh->prev) { + + sqh->prev->next = sqh->next; + sqh->prev->qh_link = sqh->qh_link; + + usb2_pc_cpu_flush(sqh->prev->page_cache); + + if (sqh->next) { + sqh->next->prev = sqh->prev; + usb2_pc_cpu_flush(sqh->next->page_cache); + } + /* + * set the Terminate-bit in the e_next of the QH, in case + * the transferred packet was short so that the QH still + * points at the last used TD + */ + + sqh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); + + last = ((last == sqh) ? sqh->prev : last); + + sqh->prev = 0; + + usb2_pc_cpu_flush(sqh->page_cache); + } + return (last); +} + +static usb2_error_t +ehci_non_isoc_done_sub(struct usb2_xfer *xfer) +{ + ehci_qtd_t *td; + ehci_qtd_t *td_alt_next; + uint32_t status; + uint16_t len; + + td = xfer->td_transfer_cache; + td_alt_next = td->alt_next; + + while (1) { + + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->qtd_status); + + len = EHCI_QTD_GET_BYTES(status); + + /* + * Verify the status length and subtract + * the remainder from "frlengths[]": + */ + if (len > td->len) { + /* should not happen */ + DPRINTF("Invalid status length, " + "0x%04x/0x%04x bytes\n", len, td->len); + status |= EHCI_QTD_HALTED; + } else if (xfer->aframes != xfer->nframes) { + xfer->frlengths[xfer->aframes] -= len; + } + /* Check for last transfer */ + if (((void *)td) == xfer->td_transfer_last) { + if (len == 0) { + /* + * Halt is ok if descriptor is last, + * and complete: + */ + status &= ~EHCI_QTD_HALTED; + } + td = NULL; + break; + } + /* Check for transfer error */ + if (status & EHCI_QTD_HALTED) { + /* the transfer is finished */ + td = NULL; + break; + } + /* Check for short transfer */ + if (len > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + td = td->alt_next; + } else { + /* the transfer is finished */ + td = NULL; + } + break; + } + td = td->obj_next; + + if (td->alt_next != td_alt_next) { + /* this USB frame is complete */ + break; + } + } + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + /* update data toggle */ + + xfer->pipe->toggle_next = + (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0; + +#if USB_DEBUG + if (status & EHCI_QTD_STATERRS) { + DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x" + "status=%s%s%s%s%s%s%s%s\n", + xfer->address, xfer->endpoint, xfer->aframes, + (status & EHCI_QTD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", + (status & EHCI_QTD_HALTED) ? "[HALTED]" : "", + (status & EHCI_QTD_BUFERR) ? "[BUFERR]" : "", + (status & EHCI_QTD_BABBLE) ? "[BABBLE]" : "", + (status & EHCI_QTD_XACTERR) ? "[XACTERR]" : "", + (status & EHCI_QTD_MISSEDMICRO) ? "[MISSED]" : "", + (status & EHCI_QTD_SPLITXSTATE) ? "[SPLIT]" : "", + (status & EHCI_QTD_PINGSTATE) ? "[PING]" : ""); + } +#endif + + return ((status & EHCI_QTD_HALTED) ? + USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); +} + +static void +ehci_non_isoc_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + +#if USB_DEBUG + if (ehcidebug > 10) { + ehci_dump_sqtds(xfer->td_transfer_first); + } +#endif + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + err = ehci_non_isoc_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = ehci_non_isoc_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = ehci_non_isoc_done_sub(xfer); + } +done: + ehci_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * ehci_check_transfer + * + * Return values: + * 0: USB transfer is not finished + * Else: USB transfer is finished + *------------------------------------------------------------------------*/ +static uint8_t +ehci_check_transfer(struct usb2_xfer *xfer) +{ + struct usb2_pipe_methods *methods = xfer->pipe->methods; + + uint32_t status; + + DPRINTFN(13, "xfer=%p checking transfer\n", xfer); + + if (methods == &ehci_device_isoc_fs_methods) { + ehci_sitd_t *td; + + /* isochronous full speed transfer */ + + td = xfer->td_transfer_last; + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->sitd_status); + + /* also check if first is complete */ + + td = xfer->td_transfer_first; + usb2_pc_cpu_invalidate(td->page_cache); + status |= le32toh(td->sitd_status); + + if (!(status & EHCI_SITD_ACTIVE)) { + ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); + goto transferred; + } + } else if (methods == &ehci_device_isoc_hs_methods) { + ehci_itd_t *td; + + /* isochronous high speed transfer */ + + td = xfer->td_transfer_last; + usb2_pc_cpu_invalidate(td->page_cache); + status = + td->itd_status[0] | td->itd_status[1] | + td->itd_status[2] | td->itd_status[3] | + td->itd_status[4] | td->itd_status[5] | + td->itd_status[6] | td->itd_status[7]; + + /* also check first transfer */ + td = xfer->td_transfer_first; + usb2_pc_cpu_invalidate(td->page_cache); + status |= + td->itd_status[0] | td->itd_status[1] | + td->itd_status[2] | td->itd_status[3] | + td->itd_status[4] | td->itd_status[5] | + td->itd_status[6] | td->itd_status[7]; + + /* if no transactions are active we continue */ + if (!(status & htole32(EHCI_ITD_ACTIVE))) { + ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); + goto transferred; + } + } else { + ehci_qtd_t *td; + + /* non-isochronous transfer */ + + /* + * check whether there is an error somewhere in the middle, + * or whether there was a short packet (SPD and not ACTIVE) + */ + td = xfer->td_transfer_cache; + + while (1) { + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->qtd_status); + + /* + * if there is an active TD the transfer isn't done + */ + if (status & EHCI_QTD_ACTIVE) { + /* update cache */ + xfer->td_transfer_cache = td; + goto done; + } + /* + * last transfer descriptor makes the transfer done + */ + if (((void *)td) == xfer->td_transfer_last) { + break; + } + /* + * any kind of error makes the transfer done + */ + if (status & EHCI_QTD_HALTED) { + break; + } + /* + * if there is no alternate next transfer, a short + * packet also makes the transfer done + */ + if (EHCI_QTD_GET_BYTES(status)) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + td = td->alt_next; + continue; + } + } + /* transfer is done */ + break; + } + td = td->obj_next; + } + ehci_non_isoc_done(xfer); + goto transferred; + } + +done: + DPRINTFN(13, "xfer=%p is still active\n", xfer); + return (0); + +transferred: + return (1); +} + +static void +ehci_pcd_enable(ehci_softc_t *sc) +{ + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + sc->sc_eintrs |= EHCI_STS_PCD; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + /* acknowledge any PCD interrupt */ + EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD); + + usb2_sw_transfer(&sc->sc_root_intr, + &ehci_root_intr_done); + + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +static void +ehci_interrupt_poll(ehci_softc_t *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + /* + * check if transfer is transferred + */ + if (ehci_check_transfer(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + return; +} + +/*------------------------------------------------------------------------* + * ehci_interrupt - EHCI interrupt handler + * + * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, + * hence the interrupt handler will be setup before "sc->sc_bus.bdev" + * is present ! + *------------------------------------------------------------------------*/ +void +ehci_interrupt(ehci_softc_t *sc) +{ + uint32_t status; + + mtx_lock(&sc->sc_bus.mtx); + + DPRINTFN(16, "real interrupt\n"); + +#if USB_DEBUG + if (ehcidebug > 15) { + ehci_dump_regs(sc); + } +#endif + + status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + if (status == 0) { + /* the interrupt was not for us */ + goto done; + } + if (!(status & sc->sc_eintrs)) { + goto done; + } + EOWRITE4(sc, EHCI_USBSTS, status); /* acknowledge */ + + status &= sc->sc_eintrs; + + if (status & EHCI_STS_HSE) { + printf("%s: unrecoverable error, " + "controller halted\n", __FUNCTION__); +#if USB_DEBUG + ehci_dump_regs(sc); + ehci_dump_isoc(sc); +#endif + } + if (status & EHCI_STS_PCD) { + /* + * Disable PCD interrupt for now, because it will be + * on until the port has been reset. + */ + sc->sc_eintrs &= ~EHCI_STS_PCD; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + usb2_sw_transfer(&sc->sc_root_intr, + &ehci_root_intr_done); + + /* do not allow RHSC interrupts > 1 per second */ + usb2_callout_reset(&sc->sc_tmo_pcd, hz, + (void *)&ehci_pcd_enable, sc); + } + status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA); + + if (status != 0) { + /* block unprocessed interrupts */ + sc->sc_eintrs &= ~status; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + printf("%s: blocking interrupts 0x%x\n", __FUNCTION__, status); + } + /* poll all the USB transfers */ + ehci_interrupt_poll(sc); + +done: + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/* + * called when a request does not complete + */ +static void +ehci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + ehci_softc_t *sc = xfer->usb2_sc; + + DPRINTF("xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + ehci_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +ehci_do_poll(struct usb2_bus *bus) +{ + struct ehci_softc *sc = EHCI_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + ehci_interrupt_poll(sc); + ehci_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +static void +ehci_setup_standard_chain_sub(struct ehci_std_temp *temp) +{ + struct usb2_page_search buf_res; + ehci_qtd_t *td; + ehci_qtd_t *td_next; + ehci_qtd_t *td_alt_next; + uint32_t qtd_altnext; + uint32_t buf_offset; + uint32_t average; + uint32_t len_old; + uint8_t shortpkt_old; + uint8_t precompute; + + qtd_altnext = htole32(EHCI_LINK_TERMINATE); + td_alt_next = NULL; + buf_offset = 0; + shortpkt_old = temp->shortpkt; + len_old = temp->len; + precompute = 1; + +restart: + + td = temp->td; + td_next = temp->td_next; + + while (1) { + + if (temp->len == 0) { + + if (temp->shortpkt) { + break; + } + /* send a Zero Length Packet, ZLP, last */ + + temp->shortpkt = 1; + average = 0; + + } else { + + average = temp->average; + + if (temp->len < average) { + if (temp->len % temp->max_frame_size) { + temp->shortpkt = 1; + } + average = temp->len; + } + } + + if (td_next == NULL) { + panic("%s: out of EHCI transfer descriptors!", __FUNCTION__); + } + /* get next TD */ + + td = td_next; + td_next = td->obj_next; + + /* check if we are pre-computing */ + + if (precompute) { + + /* update remaining length */ + + temp->len -= average; + + continue; + } + /* fill out current TD */ + + td->qtd_status = + temp->qtd_status | htole32(EHCI_QTD_SET_BYTES(average)); + + if (average == 0) { + + if (temp->auto_data_toggle == 0) { + + /* update data toggle, ZLP case */ + + temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); + } + td->len = 0; + + td->qtd_buffer[0] = 0; + td->qtd_buffer_hi[0] = 0; + + td->qtd_buffer[1] = 0; + td->qtd_buffer_hi[1] = 0; + + } else { + + uint8_t x; + + if (temp->auto_data_toggle == 0) { + + /* update data toggle */ + + if (((average + temp->max_frame_size - 1) / + temp->max_frame_size) & 1) { + temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); + } + } + td->len = average; + + /* update remaining length */ + + temp->len -= average; + + /* fill out buffer pointers */ + + usb2_get_page(temp->pc, buf_offset, &buf_res); + td->qtd_buffer[0] = htole32(buf_res.physaddr); + td->qtd_buffer_hi[0] = 0; + + x = 1; + + while (average > EHCI_PAGE_SIZE) { + average -= EHCI_PAGE_SIZE; + buf_offset += EHCI_PAGE_SIZE; + usb2_get_page(temp->pc, buf_offset, &buf_res); + td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); + td->qtd_buffer_hi[x] = 0; + x++; + } + + /* + * NOTE: The "average" variable is never zero after + * exiting the loop above ! + * + * NOTE: We have to subtract one from the offset to + * ensure that we are computing the physical address + * of a valid page ! + */ + buf_offset += average; + usb2_get_page(temp->pc, buf_offset - 1, &buf_res); + td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); + td->qtd_buffer_hi[x] = 0; + } + + if (td_next) { + /* link the current TD with the next one */ + td->qtd_next = td_next->qtd_self; + } + td->qtd_altnext = qtd_altnext; + td->alt_next = td_alt_next; + + usb2_pc_cpu_flush(td->page_cache); + } + + if (precompute) { + precompute = 0; + + /* setup alt next pointer, if any */ + if (temp->short_frames_ok) { + if (temp->setup_alt_next) { + td_alt_next = td_next; + qtd_altnext = td_next->qtd_self; + } + } else { + /* we use this field internally */ + td_alt_next = td_next; + } + + /* restore */ + temp->shortpkt = shortpkt_old; + temp->len = len_old; + goto restart; + } + temp->td = td; + temp->td_next = td_next; + + return; +} + +static void +ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) +{ + struct ehci_std_temp temp; + struct usb2_pipe_methods *methods; + ehci_qh_t *qh; + ehci_qtd_t *td; + uint32_t qh_endp; + uint32_t qh_endphub; + uint32_t x; + + DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.average = xfer->max_usb2_frame_size; + temp.max_frame_size = xfer->max_frame_size; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + temp.td = NULL; + temp.td_next = td; + temp.qtd_status = 0; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.short_frames_ok = xfer->flags_int.short_frames_ok; + + if (xfer->flags_int.control_xfr) { + if (xfer->pipe->toggle_next) { + /* DATA1 is next */ + temp.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); + } + temp.auto_data_toggle = 0; + } else { + temp.auto_data_toggle = 1; + } + + if (usb2_get_speed(xfer->udev) != USB_SPEED_HIGH) { + /* max 3 retries */ + temp.qtd_status |= htole32(EHCI_QTD_SET_CERR(3)); + } + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)); + temp.qtd_status |= htole32 + (EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | + EHCI_QTD_SET_TOGGLE(0)); + + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.shortpkt = temp.len ? 1 : 0; + + ehci_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + temp.pc = xfer->frbuffers + x; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + /* keep previous data toggle and error count */ + + temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(1)); + + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.shortpkt = 0; + + } else { + + /* regular data transfer */ + + temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + /* set endpoint direction */ + + temp.qtd_status |= + (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? + htole32(EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : + htole32(EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT)); + + ehci_setup_standard_chain_sub(&temp); + } + + /* check if we should append a status stage */ + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + /* + * Send a DATA1 message and invert the current endpoint + * direction. + */ + + temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(1)); + temp.qtd_status |= + (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? + htole32(EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | + EHCI_QTD_SET_TOGGLE(1)) : + htole32(EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) | + EHCI_QTD_SET_TOGGLE(1)); + + temp.len = 0; + temp.pc = NULL; + temp.shortpkt = 0; + + ehci_setup_standard_chain_sub(&temp); + } + td = temp.td; + + /* the last TD terminates the transfer: */ + td->qtd_next = htole32(EHCI_LINK_TERMINATE); + td->qtd_altnext = htole32(EHCI_LINK_TERMINATE); + td->qtd_status |= htole32(EHCI_QTD_IOC); + + usb2_pc_cpu_flush(td->page_cache); + + /* must have at least one frame! */ + + xfer->td_transfer_last = td; + +#if USB_DEBUG + if (ehcidebug > 8) { + DPRINTF("nexttog=%d; data before transfer:\n", + xfer->pipe->toggle_next); + ehci_dump_sqtds(xfer->td_transfer_first); + } +#endif + + methods = xfer->pipe->methods; + + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + /* the "qh_link" field is filled when the QH is added */ + + qh_endp = + (EHCI_QH_SET_ADDR(xfer->address) | + EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | + EHCI_QH_SET_MPL(xfer->max_packet_size)); + + if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | + EHCI_QH_DTC | EHCI_QH_SET_NRL(8)); + } else { + + if (usb2_get_speed(xfer->udev) == USB_SPEED_FULL) { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL) | + EHCI_QH_DTC); + } else { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW) | + EHCI_QH_DTC); + } + + if (methods == &ehci_device_ctrl_methods) { + qh_endp |= EHCI_QH_CTL; + } + if (methods != &ehci_device_intr_methods) { + /* Only try one time per microframe! */ + qh_endp |= EHCI_QH_SET_NRL(1); + } + } + + qh->qh_endp = htole32(qh_endp); + + qh_endphub = + (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) | + EHCI_QH_SET_CMASK(xfer->usb2_cmask) | + EHCI_QH_SET_SMASK(xfer->usb2_smask) | + EHCI_QH_SET_HUBA(xfer->udev->hs_hub_addr) | + EHCI_QH_SET_PORT(xfer->udev->hs_port_no)); + + qh->qh_endphub = htole32(qh_endphub); + qh->qh_curqtd = htole32(0); + + /* fill the overlay qTD */ + qh->qh_qtd.qtd_status = htole32(0); + + if (temp.auto_data_toggle) { + + /* let the hardware compute the data toggle */ + + qh->qh_endp &= ~htole32(EHCI_QH_DTC); + + if (xfer->pipe->toggle_next) { + /* DATA1 is next */ + qh->qh_qtd.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); + } + } + td = xfer->td_transfer_first; + + qh->qh_qtd.qtd_next = td->qtd_self; + qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); + + usb2_pc_cpu_flush(qh->page_cache); + + EHCI_APPEND_QH(qh, *qh_last); + return; +} + +static void +ehci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct ehci_softc *sc = xfer->usb2_sc; + uint16_t i; + uint16_t m; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + ehci_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); + + /* clear any old interrupt data */ + bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); + + /* set bits */ + m = (sc->sc_noport + 1); + if (m > (8 * sizeof(sc->sc_hub_idata))) { + m = (8 * sizeof(sc->sc_hub_idata)); + } + for (i = 1; i < m; i++) { + /* pick out CHANGE bits from the status register */ + if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) { + sc->sc_hub_idata[i / 8] |= 1 << (i % 8); + DPRINTF("port %d changed\n", i); + } + } +done: + return; +} + +static void +ehci_isoc_fs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) +{ + uint32_t nframes = xfer->nframes; + uint32_t status; + uint32_t *plen = xfer->frlengths; + uint16_t len = 0; + ehci_sitd_t *td = xfer->td_transfer_first; + ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos]; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_fs_p_last[0]; + } +#if USB_DEBUG + if (ehcidebug > 15) { + DPRINTF("isoc FS-TD\n"); + ehci_dump_sitd(td); + } +#endif + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->sitd_status); + + len = EHCI_SITD_GET_LEN(status); + + if (*plen >= len) { + len = *plen - len; + } else { + len = 0; + } + + *plen = len; + + /* remove FS-TD from schedule */ + EHCI_REMOVE_FS_TD(td, *pp_last); + + pp_last++; + plen++; + td = td->obj_next; + } + + xfer->aframes = xfer->nframes; + + return; +} + +static void +ehci_isoc_hs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) +{ + uint32_t nframes = xfer->nframes; + uint32_t status; + uint32_t *plen = xfer->frlengths; + uint16_t len = 0; + uint8_t td_no = 0; + ehci_itd_t *td = xfer->td_transfer_first; + ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos]; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_hs_p_last[0]; + } +#if USB_DEBUG + if (ehcidebug > 15) { + DPRINTF("isoc HS-TD\n"); + ehci_dump_itd(td); + } +#endif + + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->itd_status[td_no]); + + len = EHCI_ITD_GET_LEN(status); + + if (*plen >= len) { + /* + * The length is valid. NOTE: The complete + * length is written back into the status + * field, and not the remainder like with + * other transfer descriptor types. + */ + } else { + /* Invalid length - truncate */ + len = 0; + } + + *plen = len; + + plen++; + td_no++; + + if ((td_no == 8) || (nframes == 0)) { + /* remove HS-TD from schedule */ + EHCI_REMOVE_HS_TD(td, *pp_last); + pp_last++; + + td_no = 0; + td = td->obj_next; + } + } + xfer->aframes = xfer->nframes; + + return; +} + +/* NOTE: "done" can be run two times in a row, + * from close and from interrupt + */ +static void +ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct usb2_pipe_methods *methods = xfer->pipe->methods; + ehci_softc_t *sc = xfer->usb2_sc; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + if ((methods == &ehci_device_bulk_methods) || + (methods == &ehci_device_ctrl_methods)) { +#if USB_DEBUG + if (ehcidebug > 8) { + DPRINTF("nexttog=%d; data after transfer:\n", + xfer->pipe->toggle_next); + ehci_dump_sqtds(xfer->td_transfer_first); + } +#endif + + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_async_p_last); + } + if (methods == &ehci_device_intr_methods) { + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_intr_p_last[xfer->qh_pos]); + } + /* + * Only finish isochronous transfers once which will update + * "xfer->frlengths". + */ + if (xfer->td_transfer_first && + xfer->td_transfer_last) { + if (methods == &ehci_device_isoc_fs_methods) { + ehci_isoc_fs_done(sc, xfer); + } + if (methods == &ehci_device_isoc_hs_methods) { + ehci_isoc_hs_done(sc, xfer); + } + xfer->td_transfer_first = NULL; + xfer->td_transfer_last = NULL; + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +/*------------------------------------------------------------------------* + * ehci bulk support + *------------------------------------------------------------------------*/ +static void +ehci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_device_bulk_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_device_bulk_start(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); + + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ehci_device_bulk_methods = +{ + .open = ehci_device_bulk_open, + .close = ehci_device_bulk_close, + .enter = ehci_device_bulk_enter, + .start = ehci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ehci control support + *------------------------------------------------------------------------*/ +static void +ehci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_device_ctrl_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_device_ctrl_start(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); + + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ehci_device_ctrl_methods = +{ + .open = ehci_device_ctrl_open, + .close = ehci_device_ctrl_close, + .enter = ehci_device_ctrl_enter, + .start = ehci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ehci interrupt support + *------------------------------------------------------------------------*/ +static void +ehci_device_intr_open(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + uint16_t best; + uint16_t bit; + uint16_t x; + uint8_t slot; + + /* Allocate a microframe slot first: */ + + slot = usb2_intr_schedule_adjust + (xfer->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); + + if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { + xfer->usb2_uframe = slot; + xfer->usb2_smask = (1 << slot) & 0xFF; + xfer->usb2_cmask = 0; + } else { + xfer->usb2_uframe = slot; + xfer->usb2_smask = (1 << slot) & 0x3F; + xfer->usb2_cmask = (-(4 << slot)) & 0xFE; + } + + /* + * Find the best QH position corresponding to the given interval: + */ + + best = 0; + bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; + while (bit) { + if (xfer->interval >= bit) { + x = bit; + best = bit; + while (x & bit) { + if (sc->sc_intr_stat[x] < + sc->sc_intr_stat[best]) { + best = x; + } + x++; + } + break; + } + bit >>= 1; + } + + sc->sc_intr_stat[best]++; + xfer->qh_pos = best; + + DPRINTFN(3, "best=%d interval=%d\n", + best, xfer->interval); + return; +} + +static void +ehci_device_intr_close(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + uint8_t slot; + + slot = usb2_intr_schedule_adjust + (xfer->udev, -(xfer->max_frame_size), xfer->usb2_uframe); + + sc->sc_intr_stat[xfer->qh_pos]--; + + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_device_intr_start(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); + + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ehci_device_intr_methods = +{ + .open = ehci_device_intr_open, + .close = ehci_device_intr_close, + .enter = ehci_device_intr_enter, + .start = ehci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ehci full speed isochronous support + *------------------------------------------------------------------------*/ +static void +ehci_device_isoc_fs_open(struct usb2_xfer *xfer) +{ + ehci_sitd_t *td; + uint32_t sitd_portaddr; + uint8_t ds; + + sitd_portaddr = + EHCI_SITD_SET_ADDR(xfer->address) | + EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | + EHCI_SITD_SET_HUBA(xfer->udev->hs_hub_addr) | + EHCI_SITD_SET_PORT(xfer->udev->hs_port_no); + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + sitd_portaddr |= EHCI_SITD_SET_DIR_IN; + } + sitd_portaddr = htole32(sitd_portaddr); + + /* initialize all TD's */ + + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { + + td->sitd_portaddr = sitd_portaddr; + + /* + * TODO: make some kind of automatic + * SMASK/CMASK selection based on micro-frame + * usage + * + * micro-frame usage (8 microframes per 1ms) + */ + td->sitd_back = htole32(EHCI_LINK_TERMINATE); + + usb2_pc_cpu_flush(td->page_cache); + } + } + return; +} + +static void +ehci_device_isoc_fs_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ehci_softc_t *sc = xfer->usb2_sc; + struct usb2_fs_isoc_schedule *fss_start; + struct usb2_fs_isoc_schedule *fss_end; + struct usb2_fs_isoc_schedule *fss; + ehci_sitd_t *td; + ehci_sitd_t *td_last = NULL; + ehci_sitd_t **pp_last; + uint32_t *plen; + uint32_t buf_offset; + uint32_t nframes; + uint32_t temp; + uint32_t sitd_mask; + uint16_t tlen; + uint8_t sa; + uint8_t sb; + uint8_t error; + +#if USB_DEBUG + uint8_t once = 1; + +#endif + + DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + /* get the current frame index */ + + nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; + + /* + * check if the frame index is within the window where the frames + * will be inserted + */ + buf_offset = (nframes - xfer->pipe->isoc_next) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + if ((xfer->pipe->is_synced == 0) || + (buf_offset < xfer->nframes)) { + /* + * If there is data underflow or the pipe queue is empty we + * schedule the transfer a few frames ahead of the current + * frame position. Else two isochronous transfers might + * overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + buf_offset = (xfer->pipe->isoc_next - nframes) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_fs_isoc_schedule_isoc_time_expand + (xfer->udev, &fss_start, &fss_end, nframes) + buf_offset + + xfer->nframes; + + /* get the real number of frames */ + + nframes = xfer->nframes; + + buf_offset = 0; + + plen = xfer->frlengths; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; + + pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next]; + + /* store starting position */ + + xfer->qh_pos = xfer->pipe->isoc_next; + + fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX); + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_fs_p_last[0]; + } + if (fss >= fss_end) { + fss = fss_start; + } + /* reuse sitd_portaddr and sitd_back from last transfer */ + + if (*plen > xfer->max_frame_size) { +#if USB_DEBUG + if (once) { + once = 0; + printf("%s: frame length(%d) exceeds %d " + "bytes (frame truncated)\n", + __FUNCTION__, *plen, + xfer->max_frame_size); + } +#endif + *plen = xfer->max_frame_size; + } + /* + * We currently don't care if the ISOCHRONOUS schedule is + * full! + */ + error = usb2_fs_isoc_schedule_alloc(fss, &sa, *plen); + if (error) { + /* + * The FULL speed schedule is FULL! Set length + * to zero. + */ + *plen = 0; + } + if (*plen) { + /* + * only call "usb2_get_page()" when we have a + * non-zero length + */ + usb2_get_page(xfer->frbuffers, buf_offset, &buf_res); + td->sitd_bp[0] = htole32(buf_res.physaddr); + buf_offset += *plen; + /* + * NOTE: We need to subtract one from the offset so + * that we are on a valid page! + */ + usb2_get_page(xfer->frbuffers, buf_offset - 1, + &buf_res); + temp = buf_res.physaddr & ~0xFFF; + } else { + td->sitd_bp[0] = 0; + temp = 0; + } + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { + tlen = *plen; + if (tlen <= 188) { + temp |= 1; /* T-count = 1, TP = ALL */ + tlen = 1; + } else { + tlen += 187; + tlen /= 188; + temp |= tlen; /* T-count = [1..6] */ + temp |= 8; /* TP = Begin */ + } + + tlen += sa; + + if (tlen >= 8) { + sb = 0; + } else { + sb = (1 << tlen); + } + + sa = (1 << sa); + sa = (sb - sa) & 0x3F; + sb = 0; + } else { + sb = (-(4 << sa)) & 0xFE; + sa = (1 << sa) & 0x3F; + } + + sitd_mask = (EHCI_SITD_SET_SMASK(sa) | + EHCI_SITD_SET_CMASK(sb)); + + td->sitd_bp[1] = htole32(temp); + + td->sitd_mask = htole32(sitd_mask); + + if (nframes == 0) { + td->sitd_status = htole32 + (EHCI_SITD_IOC | + EHCI_SITD_ACTIVE | + EHCI_SITD_SET_LEN(*plen)); + } else { + td->sitd_status = htole32 + (EHCI_SITD_ACTIVE | + EHCI_SITD_SET_LEN(*plen)); + } + usb2_pc_cpu_flush(td->page_cache); + +#if USB_DEBUG + if (ehcidebug > 15) { + DPRINTF("FS-TD %d\n", nframes); + ehci_dump_sitd(td); + } +#endif + /* insert TD into schedule */ + EHCI_APPEND_FS_TD(td, *pp_last); + pp_last++; + + plen++; + fss++; + td_last = td; + td = td->obj_next; + } + + xfer->td_transfer_last = td_last; + + /* update isoc_next */ + xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + return; +} + +static void +ehci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ehci_device_isoc_fs_methods = +{ + .open = ehci_device_isoc_fs_open, + .close = ehci_device_isoc_fs_close, + .enter = ehci_device_isoc_fs_enter, + .start = ehci_device_isoc_fs_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ehci high speed isochronous support + *------------------------------------------------------------------------*/ +static void +ehci_device_isoc_hs_open(struct usb2_xfer *xfer) +{ + ehci_itd_t *td; + uint32_t temp; + uint8_t ds; + + /* initialize all TD's */ + + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { + + /* set TD inactive */ + td->itd_status[0] = 0; + td->itd_status[1] = 0; + td->itd_status[2] = 0; + td->itd_status[3] = 0; + td->itd_status[4] = 0; + td->itd_status[5] = 0; + td->itd_status[6] = 0; + td->itd_status[7] = 0; + + /* set endpoint and address */ + td->itd_bp[0] = htole32 + (EHCI_ITD_SET_ADDR(xfer->address) | + EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))); + + temp = + EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF); + + /* set direction */ + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + temp |= EHCI_ITD_SET_DIR_IN; + } + /* set maximum packet size */ + td->itd_bp[1] = htole32(temp); + + /* set transfer multiplier */ + td->itd_bp[2] = htole32(xfer->max_packet_count & 3); + + usb2_pc_cpu_flush(td->page_cache); + } + } + return; +} + +static void +ehci_device_isoc_hs_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ehci_softc_t *sc = xfer->usb2_sc; + ehci_itd_t *td; + ehci_itd_t *td_last = NULL; + ehci_itd_t **pp_last; + bus_size_t page_addr; + uint32_t *plen; + uint32_t status; + uint32_t buf_offset; + uint32_t nframes; + uint32_t itd_offset[8 + 1]; + uint8_t x; + uint8_t td_no; + uint8_t page_no; + +#if USB_DEBUG + uint8_t once = 1; + +#endif + + DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + /* get the current frame index */ + + nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; + + /* + * check if the frame index is within the window where the frames + * will be inserted + */ + buf_offset = (nframes - xfer->pipe->isoc_next) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + if ((xfer->pipe->is_synced == 0) || + (buf_offset < ((xfer->nframes + 7) / 8))) { + /* + * If there is data underflow or the pipe queue is empty we + * schedule the transfer a few frames ahead of the current + * frame position. Else two isochronous transfers might + * overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + buf_offset = (xfer->pipe->isoc_next - nframes) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + + ((xfer->nframes + 7) / 8); + + /* get the real number of frames */ + + nframes = xfer->nframes; + + buf_offset = 0; + td_no = 0; + + plen = xfer->frlengths; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; + + pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next]; + + /* store starting position */ + + xfer->qh_pos = xfer->pipe->isoc_next; + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_hs_p_last[0]; + } + /* range check */ + if (*plen > xfer->max_frame_size) { +#if USB_DEBUG + if (once) { + once = 0; + printf("%s: frame length(%d) exceeds %d bytes " + "(frame truncated)\n", + __FUNCTION__, *plen, xfer->max_frame_size); + } +#endif + *plen = xfer->max_frame_size; + } + status = (EHCI_ITD_SET_LEN(*plen) | + EHCI_ITD_ACTIVE | + EHCI_ITD_SET_PG(0)); + td->itd_status[td_no] = htole32(status); + itd_offset[td_no] = buf_offset; + buf_offset += *plen; + plen++; + td_no++; + + if ((td_no == 8) || (nframes == 0)) { + + /* the rest of the transfers are not active, if any */ + for (x = td_no; x != 8; x++) { + td->itd_status[x] = 0; /* not active */ + } + + /* check if there is any data to be transferred */ + if (itd_offset[0] != buf_offset) { + page_no = 0; + itd_offset[td_no] = buf_offset; + + /* get first page offset */ + usb2_get_page(xfer->frbuffers, itd_offset[0], &buf_res); + /* get page address */ + page_addr = buf_res.physaddr & ~0xFFF; + /* update page address */ + td->itd_bp[0] &= htole32(0xFFF); + td->itd_bp[0] |= htole32(page_addr); + + for (x = 0; x != td_no; x++) { + /* set page number and page offset */ + status = (EHCI_ITD_SET_PG(page_no) | + (buf_res.physaddr & 0xFFF)); + td->itd_status[x] |= htole32(status); + + /* get next page offset */ + if (itd_offset[x + 1] == buf_offset) { + /* + * We subtract one so that + * we don't go off the last + * page! + */ + usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); + } else { + usb2_get_page(xfer->frbuffers, itd_offset[x + 1], &buf_res); + } + + /* check if we need a new page */ + if ((buf_res.physaddr ^ page_addr) & ~0xFFF) { + /* new page needed */ + page_addr = buf_res.physaddr & ~0xFFF; + if (page_no == 6) { + panic("%s: too many pages\n", __FUNCTION__); + } + page_no++; + /* update page address */ + td->itd_bp[page_no] &= htole32(0xFFF); + td->itd_bp[page_no] |= htole32(page_addr); + } + } + } + /* set IOC bit if we are complete */ + if (nframes == 0) { + td->itd_status[7] |= htole32(EHCI_ITD_IOC); + } + usb2_pc_cpu_flush(td->page_cache); +#if USB_DEBUG + if (ehcidebug > 15) { + DPRINTF("HS-TD %d\n", nframes); + ehci_dump_itd(td); + } +#endif + /* insert TD into schedule */ + EHCI_APPEND_HS_TD(td, *pp_last); + pp_last++; + + td_no = 0; + td_last = td; + td = td->obj_next; + } + } + + xfer->td_transfer_last = td_last; + + /* update isoc_next */ + xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) & + (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + return; +} + +static void +ehci_device_isoc_hs_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ehci_device_isoc_hs_methods = +{ + .open = ehci_device_isoc_hs_open, + .close = ehci_device_isoc_hs_close, + .enter = ehci_device_isoc_hs_enter, + .start = ehci_device_isoc_hs_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ehci root control support + *------------------------------------------------------------------------* + * simulate a hardware hub by handling + * all the necessary requests + *------------------------------------------------------------------------*/ + +static void +ehci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_root_ctrl_close(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* data structures and routines + * to emulate the root hub: + */ + +static const +struct usb2_device_descriptor ehci_devd = +{ + sizeof(struct usb2_device_descriptor), + 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 const +struct usb2_device_qualifier ehci_odevd = +{ + sizeof(struct usb2_device_qualifier), + UDESC_DEVICE_QUALIFIER, /* type */ + {0x00, 0x02}, /* USB version */ + UDCLASS_HUB, /* class */ + UDSUBCLASS_HUB, /* subclass */ + UDPROTO_FSHUB, /* protocol */ + 0, /* max packet */ + 0, /* # of configurations */ + 0 +}; + +static const struct ehci_config_desc ehci_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(ehci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0 /* max power */ + }, + + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_HSHUBSTT, + 0 + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = UE_DIR_IN | EHCI_INTR_ENDPT, + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ + .bInterval = 255, + }, +}; + +static const +struct usb2_hub_descriptor ehci_hubd = +{ + 0, /* dynamic length */ + UDESC_HUB, + 0, + {0, 0}, + 0, + 0, + {0}, +}; + +static void +ehci_disown(ehci_softc_t *sc, uint16_t index, uint8_t lowspeed) +{ + uint32_t port; + uint32_t v; + + DPRINTF("index=%d lowspeed=%d\n", index, lowspeed); + + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; + EOWRITE4(sc, port, v | EHCI_PS_PO); +} + +static void +ehci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_root_ctrl_start(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + DPRINTF("\n"); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &ehci_root_ctrl_task, 0, 0); + + return; +} + +static void +ehci_root_ctrl_task(struct ehci_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + ehci_root_ctrl_poll(sc); + return; +} + +static void +ehci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct ehci_softc *sc = xfer->usb2_sc; + char *ptr; + uint32_t port; + uint32_t v; + uint16_t i; + uint16_t value; + uint16_t index; + uint8_t l; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + ehci_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = sc->sc_hub_desc.temp; + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " + "wValue=0x%04x wIndex=0x%04x\n", + std->req.bmRequestType, std->req.bRequest, + UGETW(std->req.wLength), value, index); + +#define C(x,y) ((x) | ((y) << 8)) + switch (C(std->req.bRequest, std->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): + std->len = 1; + sc->sc_hub_desc.temp[0] = sc->sc_conf; + break; + case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): + switch (value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(ehci_devd); + sc->sc_hub_desc.devd = ehci_devd; + break; + /* + * We can't really operate at another speed, + * but the specification says we need this + * descriptor: + */ + case UDESC_DEVICE_QUALIFIER: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(ehci_odevd); + sc->sc_hub_desc.odevd = ehci_odevd; + break; + + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(ehci_confd); + std->ptr = USB_ADD_BYTES(&ehci_confd, 0); + break; + + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + ptr = "\001"; + break; + + case 1: /* Vendor */ + ptr = sc->sc_vendor; + break; + + case 2: /* Product */ + ptr = "EHCI root HUB"; + break; + + default: + ptr = ""; + break; + } + + std->len = usb2_make_str_desc + (sc->sc_hub_desc.temp, + sizeof(sc->sc_hub_desc.temp), + ptr); + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + case C(UR_GET_INTERFACE, UT_READ_INTERFACE): + std->len = 1; + sc->sc_hub_desc.temp[0] = 0; + break; + case C(UR_GET_STATUS, UT_READ_DEVICE): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); + break; + case C(UR_GET_STATUS, UT_READ_INTERFACE): + case C(UR_GET_STATUS, UT_READ_ENDPOINT): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, 0); + break; + case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): + if (value >= USB_MAX_DEVICES) { + std->err = USB_ERR_IOERROR; + goto done; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if ((value != 0) && (value != 1)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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): + std->err = USB_ERR_IOERROR; + goto done; + 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(9, "UR_CLEAR_PORT_FEATURE\n"); + + if ((index < 1) || + (index > sc->sc_noport)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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(3, "clear port test " + "%d\n", index); + break; + case UHF_PORT_INDICATOR: + DPRINTFN(3, "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: + EOWRITE4(sc, port, v | EHCI_PS_SUSP); + 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: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + v = EOREAD4(sc, EHCI_HCSPARAMS); + + sc->sc_hub_desc.hubd = ehci_hubd; + sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; + USETW(sc->sc_hub_desc.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)); + sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ + for (l = 0; l < sc->sc_noport; l++) { + /* XXX can't find out? */ + sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8)); + } + sc->sc_hub_desc.hubd.bDescLength = + 8 + ((sc->sc_noport + 7) / 8); + std->len = sc->sc_hub_desc.hubd.bDescLength; + break; + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + std->len = 16; + bzero(sc->sc_hub_desc.temp, 16); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + DPRINTFN(9, "get port status i=%d\n", + index); + if ((index < 1) || + (index > sc->sc_noport)) { + std->err = USB_ERR_IOERROR; + goto done; + } + v = EOREAD4(sc, EHCI_PORTSC(index)); + DPRINTFN(9, "port status=0x%04x\n", v); + i = UPS_HIGH_SPEED; + 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(sc->sc_hub_desc.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(sc->sc_hub_desc.ps.wPortChange, i); + std->len = sizeof(sc->sc_hub_desc.ps); + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + std->err = USB_ERR_IOERROR; + goto done; + 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)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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(6, "reset port %d\n", index); +#if USB_DEBUG + if (ehcinohighspeed) { + /* + * Connect USB device to companion + * controller. + */ + ehci_disown(sc, index, 1); + break; + } +#endif + if (EHCI_PS_IS_LOWSPEED(v)) { + /* 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); + + if (use_polling) { + /* polling */ + DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); + } else { + /* Wait for reset to complete. */ + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_PORT_ROOT_RESET_DELAY); + } + + /* Terminate reset sequence. */ + EOWRITE4(sc, port, v); + + if (use_polling) { + /* polling */ + DELAY(EHCI_PORT_RESET_COMPLETE * 1000); + } else { + /* Wait for HC to complete reset. */ + usb2_pause_mtx(&sc->sc_bus.mtx, + EHCI_PORT_RESET_COMPLETE); + } + + v = EOREAD4(sc, port); + DPRINTF("ehci after reset, status=0x%08x\n", v); + if (v & EHCI_PS_PR) { + device_printf(sc->sc_bus.bdev, + "port reset timeout\n"); + std->err = USB_ERR_TIMEOUT; + goto done; + } + if (!(v & EHCI_PS_PE)) { + /* + * 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(3, "set port power %d\n", index); + EOWRITE4(sc, port, v | EHCI_PS_PP); + break; + + case UHF_PORT_TEST: + DPRINTFN(3, "set port test %d\n", index); + break; + + case UHF_PORT_INDICATOR: + DPRINTFN(3, "set port ind %d\n", index); + EOWRITE4(sc, port, v | EHCI_PS_PIC); + break; + + default: + std->err = USB_ERR_IOERROR; + goto done; + } + 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: + std->err = USB_ERR_IOERROR; + goto done; + } +done: + return; +} + +static void +ehci_root_ctrl_poll(struct ehci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &ehci_root_ctrl_done); + return; +} + +struct usb2_pipe_methods ehci_root_ctrl_methods = +{ + .open = ehci_root_ctrl_open, + .close = ehci_root_ctrl_close, + .enter = ehci_root_ctrl_enter, + .start = ehci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * ehci root interrupt support + *------------------------------------------------------------------------*/ +static void +ehci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_root_intr_close(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + ehci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ehci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_root_intr_start(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + return; +} + +struct usb2_pipe_methods ehci_root_intr_methods = +{ + .open = ehci_root_intr_open, + .close = ehci_root_intr_close, + .enter = ehci_root_intr_enter, + .start = ehci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +ehci_xfer_setup(struct usb2_setup_params *parm) +{ + struct usb2_page_search page_info; + struct usb2_page_cache *pc; + ehci_softc_t *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t nqtd; + uint32_t nqh; + uint32_t nsitd; + uint32_t nitd; + uint32_t n; + + sc = EHCI_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + nqtd = 0; + nqh = 0; + nsitd = 0; + nitd = 0; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + /* + * compute maximum number of some structures + */ + if (parm->methods == &ehci_device_ctrl_methods) { + + /* + * The proof for the "nqtd" formula is illustrated like + * this: + * + * +------------------------------------+ + * | | + * | |remainder -> | + * | +-----+---+ | + * | | xxx | x | frm 0 | + * | +-----+---++ | + * | | xxx | xx | frm 1 | + * | +-----+----+ | + * | ... | + * +------------------------------------+ + * + * "xxx" means a completely full USB transfer descriptor + * + * "x" and "xx" means a short USB packet + * + * For the remainder of an USB transfer modulo + * "max_data_length" we need two USB transfer descriptors. + * One to transfer the remaining data and one to finalise + * with a zero length packet in case the "force_short_xfer" + * flag is set. We only need two USB transfer descriptors in + * the case where the transfer length of the first one is a + * factor of "max_frame_size". The rest of the needed USB + * transfer descriptors is given by the buffer size divided + * by the maximum data payload. + */ + parm->hc_max_packet_size = 0x400; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 1; + nqtd = ((2 * xfer->nframes) + 1 /* STATUS */ + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + + } else if (parm->methods == &ehci_device_bulk_methods) { + + parm->hc_max_packet_size = 0x400; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 1; + nqtd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + + } else if (parm->methods == &ehci_device_intr_methods) { + + if (parm->speed == USB_SPEED_HIGH) { + parm->hc_max_packet_size = 0x400; + parm->hc_max_packet_count = 3; + } else if (parm->speed == USB_SPEED_FULL) { + parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME; + parm->hc_max_packet_count = 1; + } else { + parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME / 8; + parm->hc_max_packet_count = 1; + } + + parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 1; + nqtd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + + } else if (parm->methods == &ehci_device_isoc_fs_methods) { + + parm->hc_max_packet_size = 0x3FF; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = 0x3FF; + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nsitd = xfer->nframes; + + } else if (parm->methods == &ehci_device_isoc_hs_methods) { + + parm->hc_max_packet_size = 0x400; + parm->hc_max_packet_count = 3; + parm->hc_max_frame_size = 0xC00; + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nitd = (xfer->nframes + 7) / 8; + + } else { + + parm->hc_max_packet_size = 0x400; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = 0x400; + + usb2_transfer_setup_sub(parm); + } + +alloc_dma_set: + + if (parm->err) { + return; + } + /* + * Allocate queue heads and transfer descriptors + */ + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ehci_itd_t), + EHCI_ITD_ALIGN, nitd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nitd; n++) { + ehci_itd_t *td; + + usb2_get_page(pc + n, 0, &page_info); + + td = page_info.buffer; + + /* init TD */ + td->itd_self = htole32(page_info.physaddr | EHCI_LINK_ITD); + td->obj_next = last_obj; + td->page_cache = pc + n; + + last_obj = td; + + usb2_pc_cpu_flush(pc + n); + } + } + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ehci_sitd_t), + EHCI_SITD_ALIGN, nsitd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nsitd; n++) { + ehci_sitd_t *td; + + usb2_get_page(pc + n, 0, &page_info); + + td = page_info.buffer; + + /* init TD */ + td->sitd_self = htole32(page_info.physaddr | EHCI_LINK_SITD); + td->obj_next = last_obj; + td->page_cache = pc + n; + + last_obj = td; + + usb2_pc_cpu_flush(pc + n); + } + } + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ehci_qtd_t), + EHCI_QTD_ALIGN, nqtd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nqtd; n++) { + ehci_qtd_t *qtd; + + usb2_get_page(pc + n, 0, &page_info); + + qtd = page_info.buffer; + + /* init TD */ + qtd->qtd_self = htole32(page_info.physaddr); + qtd->obj_next = last_obj; + qtd->page_cache = pc + n; + + last_obj = qtd; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; + + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ehci_qh_t), + EHCI_QH_ALIGN, nqh)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nqh; n++) { + ehci_qh_t *qh; + + usb2_get_page(pc + n, 0, &page_info); + + qh = page_info.buffer; + + /* init QH */ + qh->qh_self = htole32(page_info.physaddr | EHCI_LINK_QH); + qh->obj_next = last_obj; + qh->page_cache = pc + n; + + last_obj = qh; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; + + if (!xfer->flags_int.curr_dma_set) { + xfer->flags_int.curr_dma_set = 1; + goto alloc_dma_set; + } + return; +} + +static void +ehci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +ehci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_addr); + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + if (udev->device_index == sc->sc_addr) { + switch (edesc->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: + /* do nothing */ + break; + } + } else { + if ((udev->speed != USB_SPEED_HIGH) && + ((udev->hs_hub_addr == 0) || + (udev->hs_port_no == 0) || + (udev->bus->devices[udev->hs_hub_addr] == NULL) || + (udev->bus->devices[udev->hs_hub_addr]->hub == NULL))) { + /* We need a transaction translator */ + goto done; + } + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &ehci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &ehci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + if (udev->speed == USB_SPEED_HIGH) { + pipe->methods = &ehci_device_isoc_hs_methods; + } else if (udev->speed == USB_SPEED_FULL) { + pipe->methods = &ehci_device_isoc_fs_methods; + } + break; + case UE_BULK: + if (udev->speed != USB_SPEED_LOW) { + pipe->methods = &ehci_device_bulk_methods; + } + break; + default: + /* do nothing */ + break; + } + } +done: + return; +} + +static void +ehci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) +{ + /* + * Wait until the hardware has finished any possible use of + * the transfer descriptor(s) and QH + */ + *pus = (188); /* microseconds */ + return; +} + +struct usb2_bus_methods ehci_bus_methods = +{ + .pipe_init = ehci_pipe_init, + .xfer_setup = ehci_xfer_setup, + .xfer_unsetup = ehci_xfer_unsetup, + .do_poll = ehci_do_poll, + .get_dma_delay = ehci_get_dma_delay, +}; diff --git a/sys/dev/usb2/controller/ehci2.h b/sys/dev/usb2/controller/ehci2.h new file mode 100644 index 000000000000..f002d55119ae --- /dev/null +++ b/sys/dev/usb2/controller/ehci2.h @@ -0,0 +1,515 @@ +/* $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. + */ + +#ifndef _EHCI_H_ +#define _EHCI_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_USB_REV_MASK 0xff +#define PCI_USB_REV_PRE_1_0 0x00 +#define PCI_USB_REV_1_0 0x10 +#define PCI_USB_REV_1_1 0x11 +#define PCI_USB_REV_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_BIOS_SEM 0x02 +#define EHCI_LEGSUP_OS_SEM 0x03 +#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) + +/* + * NOTE: the doorbell interrupt is enabled, but the doorbell is never + * used! SiS chipsets require this. + */ +#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_PORT_RESET_COMPLETE 2 /* ms */ + +/* + * Alignment NOTE: structures must be aligned so that the hardware can index + * without performing addition. + */ +#define EHCI_FRAMELIST_ALIGN 0x1000 /* bytes */ +#define EHCI_FRAMELIST_COUNT 1024 /* units */ +#define EHCI_VIRTUAL_FRAMELIST_COUNT 128 /* units */ + +#if ((8*EHCI_VIRTUAL_FRAMELIST_COUNT) < USB_MAX_HS_ISOC_FRAMES_PER_XFER) +#error "maximum number of high-speed isochronous frames is higher than supported!" +#endif + +#if (EHCI_VIRTUAL_FRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER) +#error "maximum number of full-speed isochronous frames is higher than supported!" +#endif + +/* Link types */ +#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) + +/* Structures alignment (bytes) */ +#define EHCI_ITD_ALIGN 128 +#define EHCI_SITD_ALIGN 64 +#define EHCI_QTD_ALIGN 64 +#define EHCI_QH_ALIGN 128 +#define EHCI_FSTN_ALIGN 32 +/* Data buffers are divided into one or more pages */ +#define EHCI_PAGE_SIZE 0x1000 +#if ((USB_PAGE_SIZE < EHCI_PAGE_SIZE) || (EHCI_PAGE_SIZE == 0) || \ + (USB_PAGE_SIZE < EHCI_ITD_ALIGN) || (EHCI_ITD_ALIGN == 0) || \ + (USB_PAGE_SIZE < EHCI_SITD_ALIGN) || (EHCI_SITD_ALIGN == 0) || \ + (USB_PAGE_SIZE < EHCI_QTD_ALIGN) || (EHCI_QTD_ALIGN == 0) || \ + (USB_PAGE_SIZE < EHCI_QH_ALIGN) || (EHCI_QH_ALIGN == 0) || \ + (USB_PAGE_SIZE < EHCI_FSTN_ALIGN) || (EHCI_FSTN_ALIGN == 0)) +#error "Invalid USB page size!" +#endif + + +/* + * Isochronous Transfer Descriptor. This descriptor is used for high speed + * transfers only. + */ +struct ehci_itd { + volatile uint32_t itd_next; + volatile uint32_t itd_status[8]; +#define EHCI_ITD_SET_LEN(x) ((x) << 16) +#define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xFFF) +#define EHCI_ITD_IOC (1 << 15) +#define EHCI_ITD_SET_PG(x) ((x) << 12) +#define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0x7) +#define EHCI_ITD_SET_OFFS(x) (x) +#define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xFFF) +#define EHCI_ITD_ACTIVE (1 << 31) +#define EHCI_ITD_DATABUFERR (1 << 30) +#define EHCI_ITD_BABBLE (1 << 29) +#define EHCI_ITD_XACTERR (1 << 28) + volatile uint32_t itd_bp[7]; + /* itd_bp[0] */ +#define EHCI_ITD_SET_ADDR(x) (x) +#define EHCI_ITD_GET_ADDR(x) (((x) >> 0) & 0x7F) +#define EHCI_ITD_SET_ENDPT(x) ((x) << 8) +#define EHCI_ITD_GET_ENDPT(x) (((x) >> 8) & 0xF) + /* itd_bp[1] */ +#define EHCI_ITD_SET_DIR_IN (1 << 11) +#define EHCI_ITD_SET_DIR_OUT (0 << 11) +#define EHCI_ITD_SET_MPL(x) (x) +#define EHCI_ITD_GET_MPL(x) (((x) >> 0) & 0x7FF) + volatile uint32_t itd_bp_hi[7]; +/* + * Extra information needed: + */ + uint32_t itd_self; + struct ehci_itd *next; + struct ehci_itd *prev; + struct ehci_itd *obj_next; + struct usb2_page_cache *page_cache; +} __aligned(EHCI_ITD_ALIGN); + +typedef struct ehci_itd ehci_itd_t; + +/* + * Split Transaction Isochronous Transfer Descriptor. This descriptor is used + * for full speed transfers only. + */ +struct ehci_sitd { + volatile uint32_t sitd_next; + volatile uint32_t sitd_portaddr; +#define EHCI_SITD_SET_DIR_OUT (0 << 31) +#define EHCI_SITD_SET_DIR_IN (1 << 31) +#define EHCI_SITD_SET_ADDR(x) (x) +#define EHCI_SITD_GET_ADDR(x) ((x) & 0x7F) +#define EHCI_SITD_SET_ENDPT(x) ((x) << 8) +#define EHCI_SITD_GET_ENDPT(x) (((x) >> 8) & 0xF) +#define EHCI_SITD_GET_DIR(x) ((x) >> 31) +#define EHCI_SITD_SET_PORT(x) ((x) << 24) +#define EHCI_SITD_GET_PORT(x) (((x) >> 24) & 0x7F) +#define EHCI_SITD_SET_HUBA(x) ((x) << 16) +#define EHCI_SITD_GET_HUBA(x) (((x) >> 16) & 0x7F) + volatile uint32_t sitd_mask; +#define EHCI_SITD_SET_SMASK(x) (x) +#define EHCI_SITD_SET_CMASK(x) ((x) << 8) + volatile uint32_t sitd_status; +#define EHCI_SITD_COMPLETE_SPLIT (1<<1) +#define EHCI_SITD_START_SPLIT (0<<1) +#define EHCI_SITD_MISSED_MICRO_FRAME (1<<2) +#define EHCI_SITD_XACTERR (1<<3) +#define EHCI_SITD_BABBLE (1<<4) +#define EHCI_SITD_DATABUFERR (1<<5) +#define EHCI_SITD_ERROR (1<<6) +#define EHCI_SITD_ACTIVE (1<<7) +#define EHCI_SITD_IOC (1<<31) +#define EHCI_SITD_SET_LEN(len) ((len)<<16) +#define EHCI_SITD_GET_LEN(x) (((x)>>16) & 0x3FF) + volatile uint32_t sitd_bp[2]; + volatile uint32_t sitd_back; + volatile uint32_t sitd_bp_hi[2]; +/* + * Extra information needed: + */ + uint32_t sitd_self; + struct ehci_sitd *next; + struct ehci_sitd *prev; + struct ehci_sitd *obj_next; + struct usb2_page_cache *page_cache; +} __aligned(EHCI_SITD_ALIGN); + +typedef struct ehci_sitd ehci_sitd_t; + +/* Queue Element Transfer Descriptor */ +struct ehci_qtd { + volatile uint32_t qtd_next; + volatile uint32_t qtd_altnext; + volatile uint32_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 0x74 +#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 +#define EHCI_QTD_NBUFFERS 5 +#define EHCI_QTD_PAYLOAD_MAX ((EHCI_QTD_NBUFFERS-1)*EHCI_PAGE_SIZE) + volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS]; + volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; +/* + * Extra information needed: + */ + struct ehci_qtd *alt_next; + struct ehci_qtd *obj_next; + struct usb2_page_cache *page_cache; + uint32_t qtd_self; + uint16_t len; +} __aligned(EHCI_QTD_ALIGN); + +typedef struct ehci_qtd ehci_qtd_t; + +/* Queue Head Sub Structure */ +struct ehci_qh_sub { + volatile uint32_t qtd_next; + volatile uint32_t qtd_altnext; + volatile uint32_t qtd_status; + volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS]; + volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; +} __aligned(4); + +/* Queue Head */ +struct ehci_qh { + volatile uint32_t qh_link; + volatile uint32_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) + volatile uint32_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) + volatile uint32_t qh_curqtd; + struct ehci_qh_sub qh_qtd; +/* + * Extra information needed: + */ + struct ehci_qh *next; + struct ehci_qh *prev; + struct ehci_qh *obj_next; + struct usb2_page_cache *page_cache; + uint32_t qh_self; +} __aligned(EHCI_QH_ALIGN); + +typedef struct ehci_qh ehci_qh_t; + +/* Periodic Frame Span Traversal Node */ +struct ehci_fstn { + volatile uint32_t fstn_link; + volatile uint32_t fstn_back; +} __aligned(EHCI_FSTN_ALIGN); + +typedef struct ehci_fstn ehci_fstn_t; + +struct ehci_hw_softc { + struct usb2_page_cache pframes_pc; + struct usb2_page_cache async_start_pc; + struct usb2_page_cache intr_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct usb2_page_cache isoc_hs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct usb2_page_cache isoc_fs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT]; + + struct usb2_page pframes_pg; + struct usb2_page async_start_pg; + struct usb2_page intr_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct usb2_page isoc_hs_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct usb2_page isoc_fs_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT]; +}; + +struct ehci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union ehci_hub_desc { + struct usb2_status stat; + struct usb2_port_status ps; + struct usb2_device_descriptor devd; + struct usb2_device_qualifier odevd; + struct usb2_hub_descriptor hubd; + uint8_t temp[128]; +}; + +typedef struct ehci_softc { + struct ehci_hw_softc sc_hw; + struct usb2_bus sc_bus; /* base device */ + struct usb2_config_td sc_config_td; + struct usb2_callout sc_tmo_pcd; + union ehci_hub_desc sc_hub_desc; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + + struct resource *sc_io_res; + struct resource *sc_irq_res; + struct ehci_qh *sc_async_p_last; + struct ehci_qh *sc_intr_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct ehci_sitd *sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; + struct ehci_itd *sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]; + void *sc_intr_hdl; + device_t sc_dev; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + uint32_t sc_eintrs; + uint32_t sc_cmd; /* shadow of cmd register during + * suspend */ + + uint16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT]; + uint16_t sc_id_vendor; /* vendor ID for root hub */ + + uint8_t sc_offs; /* offset to operational registers */ + uint8_t sc_doorbell_disable; /* set on doorbell failure */ + uint8_t sc_noport; + uint8_t sc_addr; /* device address */ + uint8_t sc_conf; /* device configuration */ + uint8_t sc_isreset; + uint8_t sc_hub_idata[8]; + + char sc_vendor[16]; /* vendor string for root hub */ + +} ehci_softc_t; + +#define EREAD1(sc, a) bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (a)) +#define EREAD2(sc, a) bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (a)) +#define EREAD4(sc, a) bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (a)) +#define EWRITE1(sc, a, x) \ + bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x)) +#define EWRITE2(sc, a, x) \ + bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x)) +#define EWRITE4(sc, a, x) \ + bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x)) +#define EOREAD1(sc, a) \ + bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a)) +#define EOREAD2(sc, a) \ + bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a)) +#define EOREAD4(sc, a) \ + bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a)) +#define EOWRITE1(sc, a, x) \ + bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x)) +#define EOWRITE2(sc, a, x) \ + bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x)) +#define EOWRITE4(sc, a, x) \ + bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x)) + +usb2_bus_mem_cb_t ehci_iterate_hw_softc; + +usb2_error_t ehci_init(ehci_softc_t *sc); +void ehci_detach(struct ehci_softc *sc); +void ehci_suspend(struct ehci_softc *sc); +void ehci_resume(struct ehci_softc *sc); +void ehci_shutdown(ehci_softc_t *sc); +void ehci_interrupt(ehci_softc_t *sc); + +#endif /* _EHCI_H_ */ diff --git a/sys/dev/usb2/controller/ehci2_pci.c b/sys/dev/usb2/controller/ehci2_pci.c new file mode 100644 index 000000000000..1fb7dc2cd749 --- /dev/null +++ b/sys/dev/usb2/controller/ehci2_pci.c @@ -0,0 +1,498 @@ +/*- + * 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 +__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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + +#define PCI_EHCI_BASE_REG 0x10 + +static void ehci_pci_takecontroller(device_t self); + +static device_probe_t ehci_pci_probe; +static device_attach_t ehci_pci_attach; +static device_detach_t ehci_pci_detach; +static device_suspend_t ehci_pci_suspend; +static device_resume_t ehci_pci_resume; +static device_shutdown_t ehci_pci_shutdown; + +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_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_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); + + return (0); +} + +static const char * +ehci_pci_match(device_t self) +{ + uint32_t device_id = pci_get_devid(self); + + switch (device_id) { + case 0x268c8086: + return ("Intel 63XXESB USB 2.0 controller"); + + case 0x523910b9: + return "ALi M5239 USB 2.0 controller"; + + case 0x10227463: + return "AMD 8111 USB 2.0 controller"; + + case 0x20951022: + return ("AMD CS5536 (Geode) USB 2.0 controller"); + + case 0x43451002: + return "ATI SB200 USB 2.0 controller"; + case 0x43731002: + return "ATI SB400 USB 2.0 controller"; + + case 0x25ad8086: + return "Intel 6300ESB USB 2.0 controller"; + case 0x24cd8086: + return "Intel 82801DB/L/M (ICH4) USB 2.0 controller"; + case 0x24dd8086: + return "Intel 82801EB/R (ICH5) USB 2.0 controller"; + case 0x265c8086: + return "Intel 82801FB (ICH6) USB 2.0 controller"; + case 0x27cc8086: + return "Intel 82801GB/R (ICH7) USB 2.0 controller"; + + case 0x28368086: + return "Intel 82801H (ICH8) USB 2.0 controller USB2-A"; + case 0x283a8086: + return "Intel 82801H (ICH8) USB 2.0 controller USB2-B"; + case 0x293a8086: + return "Intel 82801I (ICH9) USB 2.0 controller"; + case 0x293c8086: + return "Intel 82801I (ICH9) USB 2.0 controller"; + + case 0x00e01033: + return ("NEC uPD 720100 USB 2.0 controller"); + + case 0x006810de: + return "NVIDIA nForce2 USB 2.0 controller"; + case 0x008810de: + return "NVIDIA nForce2 Ultra 400 USB 2.0 controller"; + case 0x00d810de: + return "NVIDIA nForce3 USB 2.0 controller"; + case 0x00e810de: + return "NVIDIA nForce3 250 USB 2.0 controller"; + case 0x005b10de: + return "NVIDIA nForce4 USB 2.0 controller"; + + case 0x15621131: + return "Philips ISP156x USB 2.0 controller"; + + case 0x31041106: + return ("VIA VT6202 USB 2.0 controller"); + + default: + break; + } + + if ((pci_get_class(self) == PCIC_SERIALBUS) + && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) + && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) { + return ("EHCI (generic) USB 2.0 controller"); + } + 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 (0); + } else { + return (ENXIO); + } +} + +static int +ehci_pci_attach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + int rid; + + if (sc == NULL) { + device_printf(self, "Could not allocate sc\n"); + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { + return ENOMEM; + } + sc->sc_dev = self; + + pci_enable_busmaster(self); + + switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { + case PCI_USB_REV_PRE_1_0: + case PCI_USB_REV_1_0: + case PCI_USB_REV_1_1: + /* + * NOTE: some EHCI USB controllers have the wrong USB + * revision number. It appears those controllers are + * fully compliant so we just ignore this value in + * some common cases. + */ + device_printf(self, "pre-2.0 USB revision (ignored)\n"); + /* fallthrough */ + case PCI_USB_REV_2_0: + sc->sc_bus.usbrev = USB_REV_2_0; + break; + default: + sc->sc_bus.usbrev = USB_REV_UNKNOWN; + break; + } + + rid = PCI_CBMEM; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + 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_PHILIPS: + sprintf(sc->sc_vendor, "Philips"); + 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 = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.mtx, + NULL, 0, 4); + if (err) { + device_printf(self, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); +#endif + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + ehci_pci_takecontroller(self); + err = ehci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed err=%d\n", err); + goto error; + } + return (0); + +error: + ehci_pci_detach(self); + return (ENXIO); +} + +static int +ehci_pci_detach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + device_t bdev; + + usb2_config_td_drain(&sc->sc_config_td); + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(self); + + pci_disable_busmaster(self); + + /* + * disable interrupts that might have been switched on in ehci_init + */ + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + } + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static void +ehci_pci_takecontroller(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + uint32_t cparams; + uint32_t eec; + uint16_t to; + uint8_t eecp; + uint8_t bios_sem; + + 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 == 0) { + continue; + } + device_printf(sc->sc_bus.bdev, "waiting for BIOS " + "to give up control\n"); + pci_write_config(self, eecp + + EHCI_LEGSUP_OS_SEM, 1, 1); + to = 500; + while (1) { + bios_sem = pci_read_config(self, eecp + + EHCI_LEGSUP_BIOS_SEM, 1); + if (bios_sem == 0) + break; + + if (--to == 0) { + device_printf(sc->sc_bus.bdev, + "timed out waiting for BIOS\n"); + break; + } + usb2_pause_mtx(NULL, 10); /* wait 10ms */ + } + } + return; +} + +static driver_t ehci_driver = +{ + .name = "ehci", + .methods = (device_method_t[]){ + /* 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} + }, + .size = sizeof(struct ehci_softc), +}; + +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, usb2_controller, 1, 1, 1); +MODULE_DEPEND(ehci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/musb2_otg.c b/sys/dev/usb2/controller/musb2_otg.c new file mode 100644 index 000000000000..b036d5c6123a --- /dev/null +++ b/sys/dev/usb2/controller/musb2_otg.c @@ -0,0 +1,2945 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +/* + * Thanks to Mentor Graphics for providing a reference driver for this + * USB chip at their homepage. + */ + +/* + * This file contains the driver for the Mentor Graphics Inventra USB + * 2.0 High Speed Dual-Role controller. + * + * NOTE: The current implementation only supports Device Side Mode! + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR musbotgdebug +#define usb2_config_td_cc musbotg_config_copy +#define usb2_config_td_softc musbotg_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MUSBOTG_INTR_ENDPT 1 + +#define MUSBOTG_BUS2SC(bus) \ + ((struct musbotg_softc *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((struct musbotg_softc *)0)->sc_bus)))) + +#define MUSBOTG_PC2SC(pc) \ + MUSBOTG_BUS2SC((pc)->tag_parent->info->bus) + +#if USB_DEBUG +static int musbotgdebug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, musbotg, CTLFLAG_RW, 0, "USB musbotg"); +SYSCTL_INT(_hw_usb2_musbotg, OID_AUTO, debug, CTLFLAG_RW, + &musbotgdebug, 0, "Debug level"); +#endif + +/* prototypes */ + +struct usb2_bus_methods musbotg_bus_methods; +struct usb2_pipe_methods musbotg_device_bulk_methods; +struct usb2_pipe_methods musbotg_device_ctrl_methods; +struct usb2_pipe_methods musbotg_device_intr_methods; +struct usb2_pipe_methods musbotg_device_isoc_methods; +struct usb2_pipe_methods musbotg_root_ctrl_methods; +struct usb2_pipe_methods musbotg_root_intr_methods; + +static musbotg_cmd_t musbotg_setup_rx; +static musbotg_cmd_t musbotg_setup_data_rx; +static musbotg_cmd_t musbotg_setup_data_tx; +static musbotg_cmd_t musbotg_setup_status; +static musbotg_cmd_t musbotg_data_rx; +static musbotg_cmd_t musbotg_data_tx; +static void musbotg_device_done(struct usb2_xfer *xfer, usb2_error_t error); +static void musbotg_do_poll(struct usb2_bus *bus); +static void musbotg_root_ctrl_poll(struct musbotg_softc *sc); +static void musbotg_standard_done(struct usb2_xfer *xfer); +static void musbotg_interrupt_poll(struct musbotg_softc *sc); + +static usb2_sw_transfer_func_t musbotg_root_intr_done; +static usb2_sw_transfer_func_t musbotg_root_ctrl_done; +static usb2_config_td_command_t musbotg_root_ctrl_task; + +/* + * Here is a configuration that the chip supports. + */ +static const struct usb2_hw_ep_profile musbotg_ep_profile[1] = { + + [0] = { + .max_in_frame_size = 64,/* fixed */ + .max_out_frame_size = 64, /* fixed */ + .is_simplex = 1, + .support_control = 1, + } +}; + +static void +musbotg_get_hw_ep_profile(struct usb2_device *udev, + const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) +{ + struct musbotg_softc *sc; + + sc = MUSBOTG_BUS2SC(udev->bus); + + if (ep_addr == 0) { + /* control endpoint */ + *ppf = musbotg_ep_profile; + } else if (ep_addr <= sc->sc_ep_max) { + /* other endpoints */ + *ppf = sc->sc_hw_ep_profile + ep_addr; + } else { + *ppf = NULL; + } + return; +} + +static void +musbotg_clocks_on(struct musbotg_softc *sc) +{ + if (sc->sc_flags.clocks_off && + sc->sc_flags.port_powered) { + + DPRINTFN(4, "\n"); + + if (sc->sc_clocks_on) { + (sc->sc_clocks_on) (sc->sc_clocks_arg); + } + sc->sc_flags.clocks_off = 0; + + /* XXX enable Transceiver */ + } + return; +} + +static void +musbotg_clocks_off(struct musbotg_softc *sc) +{ + if (!sc->sc_flags.clocks_off) { + + DPRINTFN(4, "\n"); + + /* XXX disable Transceiver */ + + if (sc->sc_clocks_off) { + (sc->sc_clocks_off) (sc->sc_clocks_arg); + } + sc->sc_flags.clocks_off = 1; + } + return; +} + +static void +musbotg_pull_common(struct musbotg_softc *sc, uint8_t on) +{ + uint8_t temp; + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + if (on) + temp |= MUSB2_MASK_SOFTC; + else + temp &= ~MUSB2_MASK_SOFTC; + + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + return; +} + +static void +musbotg_pull_up(struct musbotg_softc *sc) +{ + /* pullup D+, if possible */ + + if (!sc->sc_flags.d_pulled_up && + sc->sc_flags.port_powered) { + sc->sc_flags.d_pulled_up = 1; + musbotg_pull_common(sc, 1); + } + return; +} + +static void +musbotg_pull_down(struct musbotg_softc *sc) +{ + /* pulldown D+, if possible */ + + if (sc->sc_flags.d_pulled_up) { + sc->sc_flags.d_pulled_up = 0; + musbotg_pull_common(sc, 0); + } + return; +} + +static void +musbotg_wakeup_peer(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + uint8_t temp; + uint8_t use_polling; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp |= MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + + /* wait 8 milliseconds */ + if (use_polling) { + /* polling */ + DELAY(8000); + } else { + /* Wait for reset to complete. */ + usb2_pause_mtx(&sc->sc_bus.mtx, 8); + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + return; +} + +static void +musbotg_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) +{ + DPRINTFN(4, "is_on=%u\n", is_on); + return; +} + +static void +musbotg_set_address(struct musbotg_softc *sc, uint8_t addr) +{ + DPRINTFN(4, "addr=%d\n", addr); + addr &= 0x7F; + MUSB2_WRITE_1(sc, MUSB2_REG_FADDR, addr); + return; +} + +static uint8_t +musbotg_setup_rx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + struct usb2_device_request req; + uint16_t count; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* + * NOTE: If DATAEND is set we should not call the + * callback, hence the status stage is not complete. + */ + if (csr & MUSB2_MASK_CSR0L_DATAEND) { + /* wait for interrupt */ + goto not_complete; + } + if (csr & MUSB2_MASK_CSR0L_SENTSTALL) { + /* clear SENTSTALL */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + /* get latest status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + /* update EP0 state */ + sc->sc_ep0_busy = 0; + } + if (csr & MUSB2_MASK_CSR0L_SETUPEND) { + /* clear SETUPEND */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_SETUPEND_CLR); + /* get latest status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + /* update EP0 state */ + sc->sc_ep0_busy = 0; + } + if (sc->sc_ep0_busy) { + /* abort any ongoing transfer */ + if (!td->did_stall) { + DPRINTFN(4, "stalling\n"); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_SENDSTALL); + td->did_stall = 1; + } + goto not_complete; + } + if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { + goto not_complete; + } + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + /* verify data length */ + if (count != td->remainder) { + DPRINTFN(0, "Invalid SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + if (count != sizeof(req)) { + DPRINTFN(0, "Unsupported SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req)); + + /* copy data into real buffer */ + usb2_copy_in(td->pc, 0, &req, sizeof(req)); + + td->offset = sizeof(req); + td->remainder = 0; + + /* set pending command */ + sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR; + + /* we need set stall or dataend after this */ + sc->sc_ep0_busy = 1; + + /* sneak peek the set address */ + if ((req.bmRequestType == UT_WRITE_DEVICE) && + (req.bRequest == UR_SET_ADDRESS)) { + sc->sc_dv_addr = req.wValue[0] & 0x7F; + } else { + sc->sc_dv_addr = 0xFF; + } + return (0); /* complete */ + +not_complete: + return (1); /* not complete */ +} + +/* Control endpoint only data handling functions (RX/TX/SYNC) */ + +static uint8_t +musbotg_setup_data_rx(struct musbotg_td *td) +{ + struct usb2_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr; + uint8_t got_short; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* check if a command is pending */ + if (sc->sc_ep0_cmd) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); + sc->sc_ep0_cmd = 0; + } + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + got_short = 0; + + if (csr & (MUSB2_MASK_CSR0L_SETUPEND | + MUSB2_MASK_CSR0L_SENTSTALL)) { + if (td->remainder == 0) { + /* + * We are actually complete and have + * received the next SETUP + */ + DPRINTFN(4, "faking complete\n"); + return (0); /* complete */ + } + /* + * USB Host Aborted the transfer. + */ + td->error = 1; + return (0); /* complete */ + } + if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { + return (1); /* not complete */ + } + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + /* verify the packet byte count */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + while (count > 0) { + uint32_t temp; + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + (void *)(&sc->sc_bounce_buf[count / 4]), temp); + } + usb2_copy_in(td->pc, td->offset, + sc->sc_bounce_buf, count); + + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR; + return (0); + } + /* else need to receive a zero length packet */ + } + /* write command - need more data */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_RXPKTRDY_CLR); + return (1); /* not complete */ +} + +static uint8_t +musbotg_setup_data_tx(struct musbotg_td *td) +{ + struct usb2_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* check if a command is pending */ + if (sc->sc_ep0_cmd) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); + sc->sc_ep0_cmd = 0; + } + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & (MUSB2_MASK_CSR0L_SETUPEND | + MUSB2_MASK_CSR0L_SENTSTALL)) { + /* + * The current transfer was aborted + * by the USB Host + */ + td->error = 1; + return (0); /* complete */ + } + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) { + return (1); /* not complete */ + } + count = td->max_frame_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + while (count > 0) { + uint32_t temp; + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + usb2_copy_out(td->pc, td->offset, + sc->sc_bounce_buf, count); + + temp = count & ~3; + + if (temp) { + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* transmit data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY; + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_TXPKTRDY); + + return (1); /* not complete */ +} + +static uint8_t +musbotg_setup_status(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + if (sc->sc_ep0_busy) { + sc->sc_ep0_busy = 0; + sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); + sc->sc_ep0_cmd = 0; + } + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & MUSB2_MASK_CSR0L_DATAEND) { + /* wait for interrupt */ + return (1); /* not complete */ + } + if (sc->sc_dv_addr != 0xFF) { + /* write function address */ + musbotg_set_address(sc, sc->sc_dv_addr); + } + return (0); /* complete */ +} + +static uint8_t +musbotg_data_rx(struct musbotg_td *td) +{ + struct usb2_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr; + uint8_t to; + uint8_t got_short; + + to = 8; /* don't loop forever! */ + got_short = 0; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); + +repeat: + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* clear overrun */ + if (csr & MUSB2_MASK_CSRL_RXOVERRUN) { + /* make sure we don't clear "RXPKTRDY" */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXPKTRDY); + } + /* check status */ + if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) { + return (1); /* not complete */ + } + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + DPRINTFN(4, "count=0x%04x\n", count); + + /* + * Check for short or invalid packet: + */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + while (count > 0) { + uint32_t temp; + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + usb2_copy_in(td->pc, td->offset, + sc->sc_bounce_buf, count); + + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + return (0); + } + /* else need to receive a zero length packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +musbotg_data_tx(struct musbotg_td *td) +{ + struct usb2_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr; + uint8_t to; + + to = 8; /* don't loop forever! */ + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); + +repeat: + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & (MUSB2_MASK_CSRL_TXINCOMP | + MUSB2_MASK_CSRL_TXUNDERRUN)) { + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + } + if (csr & MUSB2_MASK_CSRL_TXPKTRDY) { + return (1); /* not complete */ + } + /* check for short packet */ + count = td->max_frame_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + while (count > 0) { + uint32_t temp; + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + usb2_copy_out(td->pc, td->offset, + sc->sc_bounce_buf, count); + + temp = count & ~3; + + if (temp) { + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), + sc->sc_bounce_buf, temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* transmit data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXPKTRDY); + + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +musbotg_xfer_do_fifo(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc; + struct musbotg_td *td; + + DPRINTFN(8, "\n"); + + td = xfer->td_transfer_cache; + while (1) { + if ((td->func) (td)) { + /* operation in progress */ + break; + } + if (((void *)td) == xfer->td_transfer_last) { + goto done; + } + if (td->error) { + goto done; + } else if (td->remainder > 0) { + /* + * We had a short transfer. If there is no alternate + * next, stop processing ! + */ + if (!td->alt_next) { + goto done; + } + } + /* + * Fetch the next transfer descriptor and transfer + * some flags to the next transfer descriptor + */ + td = td->obj_next; + xfer->td_transfer_cache = td; + } + return (1); /* not complete */ + +done: + sc = xfer->usb2_sc; + + /* compute all actual lengths */ + + musbotg_standard_done(xfer); + + return (0); /* complete */ +} + +static void +musbotg_interrupt_poll(struct musbotg_softc *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!musbotg_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + + return; +} + +static void +musbotg_vbus_interrupt(struct usb2_bus *bus, uint8_t is_on) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus); + + DPRINTFN(4, "vbus = %u\n", is_on); + + mtx_lock(&sc->sc_bus.mtx); + if (is_on) { + if (!sc->sc_flags.status_vbus) { + sc->sc_flags.status_vbus = 1; + + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &musbotg_root_intr_done); + } + } else { + if (sc->sc_flags.status_vbus) { + sc->sc_flags.status_vbus = 0; + sc->sc_flags.status_bus_reset = 0; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &musbotg_root_intr_done); + } + } + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +void +musbotg_interrupt(struct musbotg_softc *sc) +{ + uint16_t rx_status; + uint16_t tx_status; + uint8_t usb_status; + uint8_t temp; + uint8_t to = 2; + + mtx_lock(&sc->sc_bus.mtx); + +repeat: + + /* read all interrupt registers */ + usb_status = MUSB2_READ_1(sc, MUSB2_REG_INTUSB); + + /* read all FIFO interrupts */ + rx_status = MUSB2_READ_2(sc, MUSB2_REG_INTRX); + tx_status = MUSB2_READ_2(sc, MUSB2_REG_INTTX); + + /* check for any bus state change interrupts */ + + if (usb_status & (MUSB2_MASK_IRESET | + MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP)) { + + DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status); + + if (usb_status & MUSB2_MASK_IRESET) { + + /* set correct state */ + sc->sc_flags.status_bus_reset = 1; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + /* determine line speed */ + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + if (temp & MUSB2_MASK_HSMODE) + sc->sc_flags.status_high_speed = 1; + else + sc->sc_flags.status_high_speed = 0; + + /* + * After reset all interrupts are on and we need to + * turn them off! + */ + temp = MUSB2_MASK_IRESET; + /* disable resume interrupt */ + temp &= ~MUSB2_MASK_IRESUME; + /* enable suspend interrupt */ + temp |= MUSB2_MASK_ISUSP; + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); + /* disable TX and RX interrupts */ + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); + MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); + } + /* + * If RXRSM and RXSUSP is set at the same time we interpret + * that like RESUME. Resume is set when there is at least 3 + * milliseconds of inactivity on the USB BUS. + */ + if (usb_status & MUSB2_MASK_IRESUME) { + if (sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 1; + + temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE); + /* disable resume interrupt */ + temp &= ~MUSB2_MASK_IRESUME; + /* enable suspend interrupt */ + temp |= MUSB2_MASK_ISUSP; + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); + } + } else if (usb_status & MUSB2_MASK_ISUSP) { + if (!sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 1; + sc->sc_flags.change_suspend = 1; + + temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE); + /* disable suspend interrupt */ + temp &= ~MUSB2_MASK_ISUSP; + /* enable resume interrupt */ + temp |= MUSB2_MASK_IRESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); + } + } + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &musbotg_root_intr_done); + } + /* check for any endpoint interrupts */ + + if (rx_status || tx_status) { + DPRINTFN(4, "real endpoint interrupt " + "rx=0x%04x, tx=0x%04x\n", rx_status, tx_status); + } + /* poll one time regardless of FIFO status */ + + musbotg_interrupt_poll(sc); + + if (--to) + goto repeat; + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +musbotg_setup_standard_chain_sub(struct musbotg_std_temp *temp) +{ + struct musbotg_td *td; + + /* get current Transfer Descriptor */ + td = temp->td_next; + temp->td = td; + + /* prepare for next TD */ + temp->td_next = td->obj_next; + + /* fill out the Transfer Descriptor */ + td->func = temp->func; + td->pc = temp->pc; + td->offset = temp->offset; + td->remainder = temp->len; + td->error = 0; + td->did_stall = 0; + td->short_pkt = temp->short_pkt; + td->alt_next = temp->setup_alt_next; + return; +} + +static void +musbotg_setup_standard_chain(struct usb2_xfer *xfer) +{ + struct musbotg_std_temp temp; + struct musbotg_softc *sc; + struct musbotg_td *td; + uint32_t x; + uint8_t ep_no; + + DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.max_frame_size = xfer->max_frame_size; + + td = xfer->td_start[0]; + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + /* setup temp */ + + temp.td = NULL; + temp.td_next = xfer->td_start[0]; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.offset = 0; + + sc = xfer->usb2_sc; + ep_no = (xfer->endpoint & UE_ADDR); + + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + temp.func = &musbotg_setup_rx; + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.short_pkt = temp.len ? 1 : 0; + + musbotg_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + if (x != xfer->nframes) { + if (xfer->endpoint & UE_DIR_IN) { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_setup_data_tx; + else + temp.func = &musbotg_data_tx; + } else { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_setup_data_rx; + else + temp.func = &musbotg_data_rx; + } + + /* setup "pc" pointer */ + temp.pc = xfer->frbuffers + x; + } + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.short_pkt = 0; + + } else { + + /* regular data transfer */ + + temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + musbotg_setup_standard_chain_sub(&temp); + + if (xfer->flags_int.isochronous_xfr) { + temp.offset += temp.len; + } else { + /* get next Page Cache pointer */ + temp.pc = xfer->frbuffers + x; + } + } + + /* always setup a valid "pc" pointer for status and sync */ + temp.pc = xfer->frbuffers + 0; + + /* check if we should append a status stage */ + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + /* + * Send a DATA1 message and invert the current + * endpoint direction. + */ + temp.func = &musbotg_setup_status; + temp.len = 0; + temp.short_pkt = 0; + + musbotg_setup_standard_chain_sub(&temp); + } + /* must have at least one frame! */ + td = temp.td; + xfer->td_transfer_last = td; + return; +} + +static void +musbotg_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + struct musbotg_softc *sc = xfer->usb2_sc; + + DPRINTFN(1, "xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + musbotg_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +musbotg_ep_int_set(struct usb2_xfer *xfer, uint8_t on) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + uint16_t temp; + uint8_t ep_no = xfer->endpoint & UE_ADDR; + + /* + * Only enable the endpoint interrupt when we are + * actually waiting for data, hence we are dealing + * with level triggered interrupts ! + */ + if (ep_no == 0) { + temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); + if (on) + temp |= MUSB2_MASK_EPINT(0); + else + temp &= ~MUSB2_MASK_EPINT(0); + + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); + } else { + if (USB_GET_DATA_ISREAD(xfer)) { + temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE); + if (on) + temp |= MUSB2_MASK_EPINT(ep_no); + else + temp &= ~MUSB2_MASK_EPINT(ep_no); + MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp); + + } else { + temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); + if (on) + temp |= MUSB2_MASK_EPINT(ep_no); + else + temp &= ~MUSB2_MASK_EPINT(ep_no); + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); + } + } + return; +} + +static void +musbotg_start_standard_chain(struct usb2_xfer *xfer) +{ + DPRINTFN(8, "\n"); + + /* poll one time */ + if (musbotg_xfer_do_fifo(xfer)) { + + musbotg_ep_int_set(xfer, 1); + + DPRINTFN(14, "enabled interrupts on endpoint\n"); + + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &musbotg_timeout, xfer->timeout); + } + } + return; +} + +static void +musbotg_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + + DPRINTFN(8, "\n"); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + musbotg_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); + + /* set port bit */ + sc->sc_hub_idata[0] = 0x02; /* we only have one port */ + +done: + return; +} + +static usb2_error_t +musbotg_standard_done_sub(struct usb2_xfer *xfer) +{ + struct musbotg_td *td; + uint32_t len; + uint8_t error; + + DPRINTFN(8, "\n"); + + td = xfer->td_transfer_cache; + + do { + len = td->remainder; + + if (xfer->aframes != xfer->nframes) { + /* + * Verify the length and subtract + * the remainder from "frlengths[]": + */ + if (len > xfer->frlengths[xfer->aframes]) { + td->error = 1; + } else { + xfer->frlengths[xfer->aframes] -= len; + } + } + /* Check for transfer error */ + if (td->error) { + /* the transfer is finished */ + error = 1; + td = NULL; + break; + } + /* Check for short transfer */ + if (len > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + td = td->obj_next; + } else { + td = NULL; + } + } else { + /* the transfer is finished */ + td = NULL; + } + error = 0; + break; + } + td = td->obj_next; + + /* this USB frame is complete */ + error = 0; + break; + + } while (0); + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + return (error ? + USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); +} + +static void +musbotg_standard_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(12, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + err = musbotg_standard_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = musbotg_standard_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = musbotg_standard_done_sub(xfer); + } +done: + musbotg_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * musbotg_device_done + * + * NOTE: this function can be called more than one time on the + * same USB transfer! + *------------------------------------------------------------------------*/ +static void +musbotg_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + + musbotg_ep_int_set(xfer, 0); + + DPRINTFN(14, "disabled interrupts on endpoint\n"); + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +static void +musbotg_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, + struct usb2_pipe *pipe) +{ + struct musbotg_softc *sc; + uint8_t ep_no; + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + DPRINTFN(4, "pipe=%p\n", pipe); + + if (xfer) { + /* cancel any ongoing transfers */ + musbotg_device_done(xfer, USB_ERR_STALLED); + } + /* set FORCESTALL */ + sc = MUSBOTG_BUS2SC(udev->bus); + + ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no); + + if (pipe->edesc->bEndpointAddress & UE_DIR_IN) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXSENDSTALL); + } else { + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXSENDSTALL); + } + return; +} + +static void +musbotg_clear_stall_sub(struct musbotg_softc *sc, uint16_t wMaxPacket, + uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) +{ + uint16_t mps; + uint16_t temp; + uint8_t csr; + + if (ep_type == UE_CONTROL) { + /* clearing stall is not needed */ + return; + } + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no); + + /* compute max frame size */ + mps = wMaxPacket & 0x7FF; + switch ((wMaxPacket >> 11) & 3) { + case 1: + mps *= 2; + break; + case 2: + mps *= 3; + break; + default: + break; + } + + if (ep_dir == UE_DIR_IN) { + + temp = 0; + + /* Configure endpoint */ + switch (ep_type) { + case UE_INTERRUPT: + MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, + MUSB2_MASK_CSRH_TXMODE | temp); + break; + case UE_ISOCHRONOUS: + MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, + MUSB2_MASK_CSRH_TXMODE | + MUSB2_MASK_CSRH_TXISO | temp); + break; + case UE_BULK: + MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, + MUSB2_MASK_CSRH_TXMODE | temp); + break; + default: + break; + } + + /* Need to flush twice in case of double bufring */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXFFLUSH); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXFFLUSH); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + /* reset data toggle */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXDT_CLR); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + /* set double/single buffering */ + temp = MUSB2_READ_2(sc, MUSB2_REG_TXDBDIS); + if (mps <= (sc->sc_hw_ep_profile[ep_no]. + max_in_frame_size / 2)) { + /* double buffer */ + temp &= ~(1 << ep_no); + } else { + /* single buffer */ + temp |= (1 << ep_no); + } + MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, temp); + + /* clear sent stall */ + if (csr & MUSB2_MASK_CSRL_TXSENTSTALL) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } else { + + temp = 0; + + /* Configure endpoint */ + switch (ep_type) { + case UE_INTERRUPT: + MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, + MUSB2_MASK_CSRH_RXNYET | temp); + break; + case UE_ISOCHRONOUS: + MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, + MUSB2_MASK_CSRH_RXNYET | + MUSB2_MASK_CSRH_RXISO | temp); + break; + case UE_BULK: + MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, temp); + break; + default: + break; + } + + /* Need to flush twice in case of double bufring */ + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + if (csr & MUSB2_MASK_CSRL_RXPKTRDY) { + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXFFLUSH); + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + if (csr & MUSB2_MASK_CSRL_RXPKTRDY) { + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXFFLUSH); + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + } + } + /* reset data toggle */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXDT_CLR); + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + + /* set double/single buffering */ + temp = MUSB2_READ_2(sc, MUSB2_REG_RXDBDIS); + if (mps <= (sc->sc_hw_ep_profile[ep_no]. + max_out_frame_size / 2)) { + /* double buffer */ + temp &= ~(1 << ep_no); + } else { + /* single buffer */ + temp |= (1 << ep_no); + } + MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, temp); + + /* clear sent stall */ + if (csr & MUSB2_MASK_CSRL_RXSENTSTALL) { + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); + } + } + return; +} + +static void +musbotg_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + struct musbotg_softc *sc; + struct usb2_endpoint_descriptor *ed; + + DPRINTFN(4, "pipe=%p\n", pipe); + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + /* check mode */ + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + /* get softc */ + sc = MUSBOTG_BUS2SC(udev->bus); + + /* get endpoint descriptor */ + ed = pipe->edesc; + + /* reset endpoint */ + musbotg_clear_stall_sub(sc, + UGETW(ed->wMaxPacketSize), + (ed->bEndpointAddress & UE_ADDR), + (ed->bmAttributes & UE_XFERTYPE), + (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); + return; +} + +usb2_error_t +musbotg_init(struct musbotg_softc *sc) +{ + struct usb2_hw_ep_profile *pf; + uint8_t nrx; + uint8_t ntx; + uint8_t temp; + uint8_t fsize; + uint8_t frx; + uint8_t ftx; + + DPRINTFN(1, "start\n"); + + /* set up the bus structure */ + sc->sc_bus.usbrev = USB_REV_2_0; + sc->sc_bus.methods = &musbotg_bus_methods; + + mtx_lock(&sc->sc_bus.mtx); + + /* turn on clocks */ + + if (sc->sc_clocks_on) { + (sc->sc_clocks_on) (sc->sc_clocks_arg); + } + /* wait a little for things to stabilise */ + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + + /* disable all interrupts */ + + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0); + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); + MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); + + /* disable pullup */ + + musbotg_pull_common(sc, 0); + + /* wait a little bit (10ms) */ + usb2_pause_mtx(&sc->sc_bus.mtx, 10); + + /* disable double packet buffering */ + MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF); + MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF); + + /* enable HighSpeed and ISO Update flags */ + + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, + MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD); + + /* clear Session bit, if set */ + + temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); + temp &= ~MUSB2_MASK_SESS; + MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp); + + DPRINTF("DEVCTL=0x%02x\n", temp); + + /* disable testmode */ + + MUSB2_WRITE_1(sc, MUSB2_REG_TESTMODE, 0); + + /* set default value */ + + MUSB2_WRITE_1(sc, MUSB2_REG_MISC, 0); + + /* select endpoint index 0 */ + + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out number of endpoints */ + + nrx = + (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) / 16); + + ntx = + (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) % 16); + + /* these numbers exclude the control endpoint */ + + DPRINTFN(2, "RX/TX endpoints: %u/%u\n", nrx, ntx); + + sc->sc_ep_max = (nrx > ntx) ? nrx : ntx; + if (sc->sc_ep_max == 0) { + DPRINTFN(2, "ERROR: Looks like the clocks are off!\n"); + } + /* read out configuration data */ + + sc->sc_conf_data = MUSB2_READ_1(sc, MUSB2_REG_CONFDATA); + + DPRINTFN(2, "Config Data: 0x%02x\n", + sc->sc_conf_data); + + DPRINTFN(2, "HW version: 0x%04x\n", + MUSB2_READ_1(sc, MUSB2_REG_HWVERS)); + + /* initialise endpoint profiles */ + + for (temp = 1; temp <= sc->sc_ep_max; temp++) { + pf = sc->sc_hw_ep_profile + temp; + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, temp); + + fsize = MUSB2_READ_1(sc, MUSB2_REG_FSIZE); + frx = (fsize & MUSB2_MASK_RX_FSIZE) / 16;; + ftx = (fsize & MUSB2_MASK_TX_FSIZE); + + DPRINTF("Endpoint %u FIFO size: IN=%u, OUT=%u\n", + temp, pf->max_in_frame_size, + pf->max_out_frame_size); + + if (frx && ftx && (temp <= nrx) && (temp <= ntx)) { + pf->max_in_frame_size = 1 << ftx; + pf->max_out_frame_size = 1 << frx; + pf->is_simplex = 0; /* duplex */ + pf->support_multi_buffer = 1; + pf->support_bulk = 1; + pf->support_interrupt = 1; + pf->support_isochronous = 1; + pf->support_in = 1; + pf->support_out = 1; + } else if (frx && (temp <= nrx)) { + pf->max_out_frame_size = 1 << frx; + pf->is_simplex = 1; /* simplex */ + pf->support_multi_buffer = 1; + pf->support_bulk = 1; + pf->support_interrupt = 1; + pf->support_isochronous = 1; + pf->support_out = 1; + } else if (ftx && (temp <= ntx)) { + pf->max_in_frame_size = 1 << ftx; + pf->is_simplex = 1; /* simplex */ + pf->support_multi_buffer = 1; + pf->support_bulk = 1; + pf->support_interrupt = 1; + pf->support_isochronous = 1; + pf->support_in = 1; + } + } + + /* turn on default interrupts */ + + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, + MUSB2_MASK_IRESET); + + musbotg_clocks_off(sc); + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch any lost interrupts */ + + musbotg_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +musbotg_uninit(struct musbotg_softc *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + + /* disable all interrupts */ + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0); + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); + MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); + + sc->sc_flags.port_powered = 0; + sc->sc_flags.status_vbus = 0; + sc->sc_flags.status_bus_reset = 0; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + musbotg_pull_down(sc); + musbotg_clocks_off(sc); + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +void +musbotg_suspend(struct musbotg_softc *sc) +{ + return; +} + +void +musbotg_resume(struct musbotg_softc *sc) +{ + return; +} + +static void +musbotg_do_poll(struct usb2_bus *bus) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + musbotg_interrupt_poll(sc); + musbotg_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/*------------------------------------------------------------------------* + * musbotg bulk support + *------------------------------------------------------------------------*/ +static void +musbotg_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_bulk_close(struct usb2_xfer *xfer) +{ + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +musbotg_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_bulk_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + musbotg_setup_standard_chain(xfer); + musbotg_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods musbotg_device_bulk_methods = +{ + .open = musbotg_device_bulk_open, + .close = musbotg_device_bulk_close, + .enter = musbotg_device_bulk_enter, + .start = musbotg_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * musbotg control support + *------------------------------------------------------------------------*/ +static void +musbotg_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_ctrl_close(struct usb2_xfer *xfer) +{ + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +musbotg_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_ctrl_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + musbotg_setup_standard_chain(xfer); + musbotg_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods musbotg_device_ctrl_methods = +{ + .open = musbotg_device_ctrl_open, + .close = musbotg_device_ctrl_close, + .enter = musbotg_device_ctrl_enter, + .start = musbotg_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * musbotg interrupt support + *------------------------------------------------------------------------*/ +static void +musbotg_device_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_intr_close(struct usb2_xfer *xfer) +{ + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +musbotg_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_intr_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + musbotg_setup_standard_chain(xfer); + musbotg_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods musbotg_device_intr_methods = +{ + .open = musbotg_device_intr_open, + .close = musbotg_device_intr_close, + .enter = musbotg_device_intr_enter, + .start = musbotg_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * musbotg full speed isochronous support + *------------------------------------------------------------------------*/ +static void +musbotg_device_isoc_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_device_isoc_close(struct usb2_xfer *xfer) +{ + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +musbotg_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + uint32_t temp; + uint32_t nframes; + uint32_t fs_frames; + + DPRINTFN(5, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + /* get the current frame index */ + + nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME); + + /* + * check if the frame index is within the window where the frames + * will be inserted + */ + temp = (nframes - xfer->pipe->isoc_next) & MUSB2_MASK_FRAME; + + if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { + fs_frames = (xfer->nframes + 7) / 8; + } else { + fs_frames = xfer->nframes; + } + + if ((xfer->pipe->is_synced == 0) || + (temp < fs_frames)) { + /* + * If there is data underflow or the pipe queue is + * empty we schedule the transfer a few frames ahead + * of the current frame position. Else two isochronous + * transfers might overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME; + xfer->pipe->is_synced = 1; + DPRINTFN(2, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + temp = (xfer->pipe->isoc_next - nframes) & MUSB2_MASK_FRAME; + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + + fs_frames; + + /* compute frame number for next insertion */ + xfer->pipe->isoc_next += fs_frames; + + /* setup TDs */ + musbotg_setup_standard_chain(xfer); + return; +} + +static void +musbotg_device_isoc_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + musbotg_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods musbotg_device_isoc_methods = +{ + .open = musbotg_device_isoc_open, + .close = musbotg_device_isoc_close, + .enter = musbotg_device_isoc_enter, + .start = musbotg_device_isoc_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * musbotg root control support + *------------------------------------------------------------------------* + * simulate a hardware HUB by handling + * all the necessary requests + *------------------------------------------------------------------------*/ +static void +musbotg_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_root_ctrl_close(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* + * USB descriptors for the virtual Root HUB: + */ + +static const struct usb2_device_descriptor musbotg_devd = { + .bLength = sizeof(struct usb2_device_descriptor), + .bDescriptorType = UDESC_DEVICE, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_HSHUBSTT, + .bMaxPacketSize = 64, + .bcdDevice = {0x00, 0x01}, + .iManufacturer = 1, + .iProduct = 2, + .bNumConfigurations = 1, +}; + +static const struct usb2_device_qualifier musbotg_odevd = { + .bLength = sizeof(struct usb2_device_qualifier), + .bDescriptorType = UDESC_DEVICE_QUALIFIER, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_FSHUB, + .bMaxPacketSize0 = 0, + .bNumConfigurations = 0, +}; + +static const struct musbotg_config_desc musbotg_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(musbotg_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0, + }, + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_HSHUBSTT, + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = (UE_DIR_IN | MUSBOTG_INTR_ENDPT), + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, + .bInterval = 255, + }, +}; + +static const struct usb2_hub_descriptor_min musbotg_hubd = { + .bDescLength = sizeof(musbotg_hubd), + .bDescriptorType = UDESC_HUB, + .bNbrPorts = 1, + .wHubCharacteristics[0] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, + .wHubCharacteristics[1] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, + .bPwrOn2PwrGood = 50, + .bHubContrCurrent = 0, + .DeviceRemovable = {0}, /* port is removable */ +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_VENDOR \ + 'M', 0, 'e', 0, 'n', 0, 't', 0, 'o', 0, 'r', 0, ' ', 0, \ + 'G', 0, 'r', 0, 'a', 0, 'p', 0, 'h', 0, 'i', 0, 'c', 0, 's', 0 + +#define STRING_PRODUCT \ + 'O', 0, 'T', 0, 'G', 0, ' ', 0, 'R', 0, \ + 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ + 'U', 0, 'B', 0, + +USB_MAKE_STRING_DESC(STRING_LANG, musbotg_langtab); +USB_MAKE_STRING_DESC(STRING_VENDOR, musbotg_vendor); +USB_MAKE_STRING_DESC(STRING_PRODUCT, musbotg_product); + +static void +musbotg_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_root_ctrl_start(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command( + &sc->sc_config_td, NULL, &musbotg_root_ctrl_task, 0, 0); + + return; +} + +static void +musbotg_root_ctrl_task(struct musbotg_softc *sc, + struct musbotg_config_copy *cc, uint16_t refcount) +{ + musbotg_root_ctrl_poll(sc); + return; +} + +static void +musbotg_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + uint16_t value; + uint16_t index; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + musbotg_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + /* demultiplex the control request */ + + switch (std->req.bmRequestType) { + case UT_READ_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_descriptor; + case UR_GET_CONFIG: + goto tr_handle_get_config; + case UR_GET_STATUS: + goto tr_handle_get_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_DEVICE: + switch (std->req.bRequest) { + case UR_SET_ADDRESS: + goto tr_handle_set_address; + case UR_SET_CONFIG: + goto tr_handle_set_config; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_DESCRIPTOR: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_WRITE_ENDPOINT: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_clear_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_clear_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SET_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_set_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_set_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SYNCH_FRAME: + goto tr_valid; /* nop */ + default: + goto tr_stalled; + } + break; + + case UT_READ_ENDPOINT: + switch (std->req.bRequest) { + case UR_GET_STATUS: + goto tr_handle_get_ep_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_INTERFACE: + switch (std->req.bRequest) { + case UR_SET_INTERFACE: + goto tr_handle_set_interface; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_READ_INTERFACE: + switch (std->req.bRequest) { + case UR_GET_INTERFACE: + goto tr_handle_get_interface; + case UR_GET_STATUS: + goto tr_handle_get_iface_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_INTERFACE: + case UT_WRITE_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_READ_CLASS_INTERFACE: + case UT_READ_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_WRITE_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_valid; + case UR_SET_DESCRIPTOR: + case UR_SET_FEATURE: + break; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_handle_clear_port_feature; + case UR_SET_FEATURE: + goto tr_handle_set_port_feature; + case UR_CLEAR_TT_BUFFER: + case UR_RESET_TT: + case UR_STOP_TT: + goto tr_valid; + + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_GET_TT_STATE: + goto tr_handle_get_tt_state; + case UR_GET_STATUS: + goto tr_handle_get_port_status; + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_class_descriptor; + case UR_GET_STATUS: + goto tr_handle_get_class_status; + + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + goto tr_valid; + +tr_handle_get_descriptor: + switch (value >> 8) { + case UDESC_DEVICE: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(musbotg_devd); + std->ptr = USB_ADD_BYTES(&musbotg_devd, 0); + goto tr_valid; + case UDESC_CONFIG: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(musbotg_confd); + std->ptr = USB_ADD_BYTES(&musbotg_confd, 0); + goto tr_valid; + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + std->len = sizeof(musbotg_langtab); + std->ptr = USB_ADD_BYTES(&musbotg_langtab, 0); + goto tr_valid; + + case 1: /* Vendor */ + std->len = sizeof(musbotg_vendor); + std->ptr = USB_ADD_BYTES(&musbotg_vendor, 0); + goto tr_valid; + + case 2: /* Product */ + std->len = sizeof(musbotg_product); + std->ptr = USB_ADD_BYTES(&musbotg_product, 0); + goto tr_valid; + default: + break; + } + break; + default: + goto tr_stalled; + } + goto tr_stalled; + +tr_handle_get_config: + std->len = 1; + sc->sc_hub_temp.wValue[0] = sc->sc_conf; + goto tr_valid; + +tr_handle_get_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); + goto tr_valid; + +tr_handle_set_address: + if (value & 0xFF00) { + goto tr_stalled; + } + sc->sc_rt_addr = value; + goto tr_valid; + +tr_handle_set_config: + if (value >= 2) { + goto tr_stalled; + } + sc->sc_conf = value; + goto tr_valid; + +tr_handle_get_interface: + std->len = 1; + sc->sc_hub_temp.wValue[0] = 0; + goto tr_valid; + +tr_handle_get_tt_state: +tr_handle_get_class_status: +tr_handle_get_iface_status: +tr_handle_get_ep_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, 0); + goto tr_valid; + +tr_handle_set_halt: +tr_handle_set_interface: +tr_handle_set_wakeup: +tr_handle_clear_wakeup: +tr_handle_clear_halt: + goto tr_valid; + +tr_handle_clear_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(8, "UR_CLEAR_PORT_FEATURE on port %d\n", index); + + switch (value) { + case UHF_PORT_SUSPEND: + musbotg_wakeup_peer(xfer); + break; + + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 0; + break; + + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 0; + musbotg_pull_down(sc); + musbotg_clocks_off(sc); + break; + case UHF_C_PORT_CONNECTION: + sc->sc_flags.change_connect = 0; + break; + case UHF_C_PORT_SUSPEND: + sc->sc_flags.change_suspend = 0; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_set_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(8, "UR_SET_PORT_FEATURE\n"); + + switch (value) { + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 1; + break; + case UHF_PORT_SUSPEND: + case UHF_PORT_RESET: + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 1; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_get_port_status: + + DPRINTFN(8, "UR_GET_PORT_STATUS\n"); + + if (index != 1) { + goto tr_stalled; + } + if (sc->sc_flags.status_vbus) { + musbotg_clocks_on(sc); + musbotg_pull_up(sc); + } else { + musbotg_pull_down(sc); + musbotg_clocks_off(sc); + } + + /* Select Device Side Mode */ + value = UPS_PORT_MODE_DEVICE; + + if (sc->sc_flags.status_high_speed) { + value |= UPS_HIGH_SPEED; + } + if (sc->sc_flags.port_powered) { + value |= UPS_PORT_POWER; + } + if (sc->sc_flags.port_enabled) { + value |= UPS_PORT_ENABLED; + } + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + value |= UPS_CURRENT_CONNECT_STATUS; + } + if (sc->sc_flags.status_suspend) { + value |= UPS_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortStatus, value); + + value = 0; + + if (sc->sc_flags.change_connect) { + value |= UPS_C_CONNECT_STATUS; + + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + /* reset EP0 state */ + sc->sc_ep0_busy = 0; + sc->sc_ep0_cmd = 0; + } + } + if (sc->sc_flags.change_suspend) { + value |= UPS_C_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortChange, value); + std->len = sizeof(sc->sc_hub_temp.ps); + goto tr_valid; + +tr_handle_get_class_descriptor: + if (value & 0xFF) { + goto tr_stalled; + } + std->ptr = USB_ADD_BYTES(&musbotg_hubd, 0); + std->len = sizeof(musbotg_hubd); + goto tr_valid; + +tr_stalled: + std->err = USB_ERR_STALLED; +tr_valid: +done: + return; +} + +static void +musbotg_root_ctrl_poll(struct musbotg_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &musbotg_root_ctrl_done); + return; +} + +struct usb2_pipe_methods musbotg_root_ctrl_methods = +{ + .open = musbotg_root_ctrl_open, + .close = musbotg_root_ctrl_close, + .enter = musbotg_root_ctrl_enter, + .start = musbotg_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * musbotg root interrupt support + *------------------------------------------------------------------------*/ +static void +musbotg_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_root_intr_close(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + musbotg_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +musbotg_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_root_intr_start(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + return; +} + +struct usb2_pipe_methods musbotg_root_intr_methods = +{ + .open = musbotg_root_intr_open, + .close = musbotg_root_intr_close, + .enter = musbotg_root_intr_enter, + .start = musbotg_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +musbotg_xfer_setup(struct usb2_setup_params *parm) +{ + const struct usb2_hw_ep_profile *pf; + struct musbotg_softc *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t n; + uint8_t ep_no; + + sc = MUSBOTG_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + /* + * NOTE: This driver does not use any of the parameters that + * are computed from the following values. Just set some + * reasonable dummies: + */ + parm->hc_max_packet_size = 0x400; + parm->hc_max_frame_size = 0x400; + + if ((parm->methods == &musbotg_device_isoc_methods) || + (parm->methods == &musbotg_device_intr_methods)) + parm->hc_max_packet_count = 3; + else + parm->hc_max_packet_count = 1; + + usb2_transfer_setup_sub(parm); + + /* + * compute maximum number of TDs + */ + if (parm->methods == &musbotg_device_ctrl_methods) { + + ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ; + + } else if (parm->methods == &musbotg_device_bulk_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &musbotg_device_intr_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &musbotg_device_isoc_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else { + + ntd = 0; + } + + /* + * check if "usb2_transfer_setup_sub" set an error + */ + if (parm->err) { + return; + } + /* + * allocate transfer descriptors + */ + last_obj = NULL; + + /* + * get profile stuff + */ + if (ntd) { + + ep_no = xfer->endpoint & UE_ADDR; + musbotg_get_hw_ep_profile(parm->udev, &pf, ep_no); + + if (pf == NULL) { + /* should not happen */ + parm->err = USB_ERR_INVAL; + return; + } + } else { + ep_no = 0; + pf = NULL; + } + + /* align data */ + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); + + for (n = 0; n != ntd; n++) { + + struct musbotg_td *td; + + if (parm->buf) { + + td = USB_ADD_BYTES(parm->buf, parm->size[0]); + + /* init TD */ + td->max_frame_size = xfer->max_frame_size; + td->ep_no = ep_no; + td->obj_next = last_obj; + + last_obj = td; + } + parm->size[0] += sizeof(*td); + } + + xfer->td_start[0] = last_obj; + return; +} + +static void +musbotg_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +musbotg_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_rt_addr); + + if (udev->device_index == sc->sc_rt_addr) { + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + switch (edesc->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &musbotg_root_ctrl_methods; + break; + case UE_DIR_IN | MUSBOTG_INTR_ENDPT: + pipe->methods = &musbotg_root_intr_methods; + break; + default: + /* do nothing */ + break; + } + } else { + + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + if ((udev->speed != USB_SPEED_FULL) && + (udev->speed != USB_SPEED_HIGH)) { + /* not supported */ + return; + } + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &musbotg_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &musbotg_device_intr_methods; + break; + case UE_ISOCHRONOUS: + pipe->methods = &musbotg_device_isoc_methods; + break; + case UE_BULK: + pipe->methods = &musbotg_device_bulk_methods; + break; + default: + /* do nothing */ + break; + } + } + return; +} + +struct usb2_bus_methods musbotg_bus_methods = +{ + .pipe_init = &musbotg_pipe_init, + .xfer_setup = &musbotg_xfer_setup, + .xfer_unsetup = &musbotg_xfer_unsetup, + .do_poll = &musbotg_do_poll, + .get_hw_ep_profile = &musbotg_get_hw_ep_profile, + .set_stall = &musbotg_set_stall, + .clear_stall = &musbotg_clear_stall, + .vbus_interrupt = &musbotg_vbus_interrupt, + .rem_wakeup_set = &musbotg_rem_wakeup_set, +}; diff --git a/sys/dev/usb2/controller/musb2_otg.h b/sys/dev/usb2/controller/musb2_otg.h new file mode 100644 index 000000000000..68193e2ea085 --- /dev/null +++ b/sys/dev/usb2/controller/musb2_otg.h @@ -0,0 +1,403 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 header file defines the registers of the Mentor Graphics + * USB OnTheGo Inventra chip. + */ + +#ifndef _MUSB2_OTG_H_ +#define _MUSB2_OTG_H_ + +/* Common registers */ + +#define MUSB2_REG_FADDR 0x0000 /* function address register */ +#define MUSB2_MASK_FADDR 0x7F + +#define MUSB2_REG_POWER 0x0001 /* power register */ +#define MUSB2_MASK_SUSPM_ENA 0x01 +#define MUSB2_MASK_SUSPMODE 0x02 +#define MUSB2_MASK_RESUME 0x04 +#define MUSB2_MASK_RESET 0x08 +#define MUSB2_MASK_HSMODE 0x10 +#define MUSB2_MASK_HSENAB 0x20 +#define MUSB2_MASK_SOFTC 0x40 +#define MUSB2_MASK_ISOUPD 0x80 + +/* Endpoint interrupt handling */ + +#define MUSB2_REG_INTTX 0x0002 /* transmit interrupt register */ +#define MUSB2_REG_INTRX 0x0004 /* receive interrupt register */ +#define MUSB2_REG_INTTXE 0x0006 /* transmit interrupt enable register */ +#define MUSB2_REG_INTRXE 0x0008 /* receive interrupt enable register */ +#define MUSB2_MASK_EPINT(epn) (1 << (epn)) /* epn = [0..15] */ + +/* Common interrupt handling */ + +#define MUSB2_REG_INTUSB 0x000A /* USB interrupt register */ +#define MUSB2_MASK_ISUSP 0x01 +#define MUSB2_MASK_IRESUME 0x02 +#define MUSB2_MASK_IRESET 0x04 +#define MUSB2_MASK_IBABBLE 0x04 +#define MUSB2_MASK_ISOF 0x08 +#define MUSB2_MASK_ICONN 0x10 +#define MUSB2_MASK_IDISC 0x20 +#define MUSB2_MASK_ISESSRQ 0x40 +#define MUSB2_MASK_IVBUSERR 0x80 + +#define MUSB2_REG_INTUSBE 0x000B /* USB interrupt enable register */ +#define MUSB2_REG_FRAME 0x000C /* USB frame register */ +#define MUSB2_MASK_FRAME 0x3FF /* 0..1023 */ + +#define MUSB2_REG_EPINDEX 0x000E /* endpoint index register */ +#define MUSB2_MASK_EPINDEX 0x0F + +#define MUSB2_REG_TESTMODE 0x000F /* test mode register */ +#define MUSB2_MASK_TSE0_NAK 0x01 +#define MUSB2_MASK_TJ 0x02 +#define MUSB2_MASK_TK 0x04 +#define MUSB2_MASK_TPACKET 0x08 +#define MUSB2_MASK_TFORCE_HS 0x10 +#define MUSB2_MASK_TFORCE_LS 0x20 +#define MUSB2_MASK_TFIFO_ACC 0x40 +#define MUSB2_MASK_TFORCE_HC 0x80 + +#define MUSB2_REG_INDEXED_CSR 0x0010 /* EP control status register offset */ + +#define MUSB2_REG_TXMAXP (0x0000 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_REG_RXMAXP (0x0004 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_PKTSIZE 0x03FF /* in bytes, should be even */ +#define MUSB2_MASK_PKTMULT 0xFC00 /* HS packet multiplier: 0..2 */ + +#define MUSB2_REG_TXCSRL (0x0002 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_CSRL_TXPKTRDY 0x01 +#define MUSB2_MASK_CSRL_TXFIFONEMPTY 0x02 +#define MUSB2_MASK_CSRL_TXUNDERRUN 0x04 /* Device Mode */ +#define MUSB2_MASK_CSRL_TXERROR 0x04 /* Host Mode */ +#define MUSB2_MASK_CSRL_TXFFLUSH 0x08 +#define MUSB2_MASK_CSRL_TXSENDSTALL 0x10/* Device Mode */ +#define MUSB2_MASK_CSRL_TXSETUPPKT 0x10 /* Host Mode */ +#define MUSB2_MASK_CSRL_TXSENTSTALL 0x20/* Device Mode */ +#define MUSB2_MASK_CSRL_TXSTALLED 0x20 /* Host Mode */ +#define MUSB2_MASK_CSRL_TXDT_CLR 0x40 +#define MUSB2_MASK_CSRL_TXINCOMP 0x80 + +/* Device Side Mode */ +#define MUSB2_MASK_CSR0L_RXPKTRDY 0x01 +#define MUSB2_MASK_CSR0L_TXPKTRDY 0x02 +#define MUSB2_MASK_CSR0L_SENTSTALL 0x04 +#define MUSB2_MASK_CSR0L_DATAEND 0x08 +#define MUSB2_MASK_CSR0L_SETUPEND 0x10 +#define MUSB2_MASK_CSR0L_SENDSTALL 0x20 +#define MUSB2_MASK_CSR0L_RXPKTRDY_CLR 0x40 +#define MUSB2_MASK_CSR0L_SETUPEND_CLR 0x80 + +/* Host Side Mode */ +#define MUSB2_MASK_CSR0L_RXSTALL 0x04 +#define MUSB2_MASK_CSR0L_SETUPPKT 0x08 +#define MUSB2_MASK_CSR0L_ERROR 0x10 +#define MUSB2_MASK_CSR0L_REQPKT 0x20 +#define MUSB2_MASK_CSR0L_STATUSPKT 0x40 +#define MUSB2_MASK_CSR0L_NAKTIMO 0x80 + +#define MUSB2_REG_TXCSRH (0x0003 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_CSRH_TXDT_VAL 0x01 /* Host Mode */ +#define MUSB2_MASK_CSRH_TXDT_WR 0x02 /* Host Mode */ +#define MUSB2_MASK_CSRH_TXDMAREQMODE 0x04 +#define MUSB2_MASK_CSRH_TXDT_SWITCH 0x08 +#define MUSB2_MASK_CSRH_TXDMAREQENA 0x10 +#define MUSB2_MASK_CSRH_RXMODE 0x00 +#define MUSB2_MASK_CSRH_TXMODE 0x20 +#define MUSB2_MASK_CSRH_TXISO 0x40 /* Device Mode */ +#define MUSB2_MASK_CSRH_TXAUTOSET 0x80 + +#define MUSB2_MASK_CSR0H_FFLUSH 0x01 /* Device Side flush FIFO */ +#define MUSB2_MASK_CSR0H_DT 0x02 /* Host Side data toggle */ +#define MUSB2_MASK_CSR0H_DT_SET 0x04 /* Host Side */ +#define MUSB2_MASK_CSR0H_PING_DIS 0x08 /* Host Side */ + +#define MUSB2_REG_RXCSRL (0x0006 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_CSRL_RXPKTRDY 0x01 +#define MUSB2_MASK_CSRL_RXFIFOFULL 0x02 +#define MUSB2_MASK_CSRL_RXOVERRUN 0x04 +#define MUSB2_MASK_CSRL_RXDATAERR 0x08 +#define MUSB2_MASK_CSRL_RXFFLUSH 0x10 +#define MUSB2_MASK_CSRL_RXSENDSTALL 0x20/* Device Mode */ +#define MUSB2_MASK_CSRL_RXREQPKT 0x20 /* Host Mode */ +#define MUSB2_MASK_CSRL_RXSENTSTALL 0x40/* Device Mode */ +#define MUSB2_MASK_CSRL_RXSTALL 0x40 /* Host Mode */ +#define MUSB2_MASK_CSRL_RXDT_CLR 0x80 + +#define MUSB2_REG_RXCSRH (0x0007 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_CSRH_RXINCOMP 0x01 +#define MUSB2_MASK_CSRH_RXDT_VAL 0x02 /* Host Mode */ +#define MUSB2_MASK_CSRH_RXDT_SET 0x04 /* Host Mode */ +#define MUSB2_MASK_CSRH_RXDMAREQMODE 0x08 +#define MUSB2_MASK_CSRH_RXNYET 0x10 +#define MUSB2_MASK_CSRH_RXDMAREQENA 0x20 +#define MUSB2_MASK_CSRH_RXISO 0x40 /* Device Mode */ +#define MUSB2_MASK_CSRH_RXAUTOREQ 0x40 /* Host Mode */ +#define MUSB2_MASK_CSRH_RXAUTOCLEAR 0x80 + +#define MUSB2_REG_RXCOUNT (0x0008 + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_RXCOUNT 0xFFFF + +#define MUSB2_REG_TXTI (0x000A + MUSB2_REG_INDEXED_CSR) +#define MUSB2_REG_RXTI (0x000C + MUSB2_REG_INDEXED_CSR) + +/* Host Mode */ +#define MUSB2_MASK_TI_SPEED 0xC0 +#define MUSB2_MASK_TI_SPEED_LO 0xC0 +#define MUSB2_MASK_TI_SPEED_FS 0x80 +#define MUSB2_MASK_TI_SPEED_HS 0x40 +#define MUSB2_MASK_TI_PROTO_CTRL 0x00 +#define MUSB2_MASK_TI_PROTO_ISOC 0x10 +#define MUSB2_MASK_TI_PROTO_BULK 0x20 +#define MUSB2_MASK_TI_PROTO_INTR 0x30 +#define MUSB2_MASK_TI_EP_NUM 0x0F + +#define MUSB2_REG_TXNAKLIMIT (0x000B /* EPN=0 */ + MUSB2_REG_INDEXED_CSR) +#define MUSB2_REG_RXNAKLIMIT (0x000D /* EPN=0 */ + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_NAKLIMIT 0xFF + +#define MUSB2_REG_FSIZE (0x000F + MUSB2_REG_INDEXED_CSR) +#define MUSB2_MASK_RX_FSIZE 0xF0 /* 3..13, 2**n bytes */ +#define MUSB2_MASK_TX_FSIZE 0x0F /* 3..13, 2**n bytes */ + +#define MUSB2_REG_EPFIFO(n) (0x0020 + (4*(n))) + +#define MUSB2_REG_CONFDATA 0x000F /* EPN=0 */ +#define MUSB2_MASK_CD_UTMI_DW 0x01 +#define MUSB2_MASK_CD_SOFTCONE 0x02 +#define MUSB2_MASK_CD_DYNFIFOSZ 0x04 +#define MUSB2_MASK_CD_HBTXE 0x08 +#define MUSB2_MASK_CD_HBRXE 0x10 +#define MUSB2_MASK_CD_BIGEND 0x20 +#define MUSB2_MASK_CD_MPTXE 0x40 +#define MUSB2_MASK_CD_MPRXE 0x80 + +/* Various registers */ + +#define MUSB2_REG_DEVCTL 0x0060 +#define MUSB2_MASK_SESS 0x01 +#define MUSB2_MASK_HOSTREQ 0x02 +#define MUSB2_MASK_HOSTMD 0x04 +#define MUSB2_MASK_VBUS0 0x08 +#define MUSB2_MASK_VBUS1 0x10 +#define MUSB2_MASK_LSDEV 0x20 +#define MUSB2_MASK_FSDEV 0x40 +#define MUSB2_MASK_BDEV 0x80 + +#define MUSB2_REG_MISC 0x0061 +#define MUSB2_MASK_RXEDMA 0x01 +#define MUSB2_MASK_TXEDMA 0x02 + +#define MUSB2_REG_TXFIFOSZ 0x0062 +#define MUSB2_REG_RXFIFOSZ 0x0063 +#define MUSB2_MASK_FIFODB 0x10 /* set if double buffering, r/w */ +#define MUSB2_MASK_FIFOSZ 0x0F +#define MUSB2_VAL_FIFOSZ_8 0 +#define MUSB2_VAL_FIFOSZ_16 1 +#define MUSB2_VAL_FIFOSZ_32 2 +#define MUSB2_VAL_FIFOSZ_64 3 +#define MUSB2_VAL_FIFOSZ_128 4 +#define MUSB2_VAL_FIFOSZ_256 5 +#define MUSB2_VAL_FIFOSZ_512 6 +#define MUSB2_VAL_FIFOSZ_1024 7 +#define MUSB2_VAL_FIFOSZ_2048 8 +#define MUSB2_VAL_FIFOSZ_4096 9 + +#define MUSB2_REG_TXFIFOADD 0x0064 +#define MUSB2_REG_RXFIFOADD 0x0066 +#define MUSB2_MASK_FIFOADD 0xFFF /* unit is 8-bytes */ + +#define MUSB2_REG_VSTATUS 0x0068 +#define MUSB2_REG_VCONTROL 0x0068 +#define MUSB2_REG_HWVERS 0x006C +#define MUSB2_REG_ULPI_BASE 0x0070 + +#define MUSB2_REG_EPINFO 0x0078 +#define MUSB2_MASK_NRXEP 0xF0 +#define MUSB2_MASK_NTXEP 0x0F + +#define MUSB2_REG_RAMINFO 0x0079 +#define MUSB2_REG_LINKINFO 0x007A + +#define MUSB2_REG_VPLEN 0x007B +#define MUSB2_MASK_VPLEN 0xFF + +#define MUSB2_REG_HS_EOF1 0x007C +#define MUSB2_REG_FS_EOF1 0x007D +#define MUSB2_REG_LS_EOF1 0x007E +#define MUSB2_REG_SOFT_RST 0x007F +#define MUSB2_MASK_SRST 0x01 +#define MUSB2_MASK_SRSTX 0x02 + +#define MUSB2_REG_RQPKTCOUNT(n) (0x0300 + (4*(n)) +#define MUSB2_REG_RXDBDIS 0x0340 +#define MUSB2_REG_TXDBDIS 0x0342 +#define MUSB2_MASK_DB(n) (1 << (n)) /* disable double buffer, n = [0..15] */ + +#define MUSB2_REG_CHIRPTO 0x0344 +#define MUSB2_REG_HSRESUM 0x0346 + +/* Host Mode only registers */ + +#define MUSB2_REG_TXFADDR(n) (0x0080 + (8*(n))) +#define MUSB2_REG_TXHADDR(n) (0x0082 + (8*(n))) +#define MUSB2_REG_TXHUBPORT(n) (0x0083 + (8*(n))) +#define MUSB2_REG_RXFADDR(n) (0x0084 + (8*(n))) +#define MUSB2_REG_RXHADDR(n) (0x0086 + (8*(n))) +#define MUSB2_REG_RXHPORT(n) (0x0087 + (8*(n))) + +#define MUSB2_EP_MAX 16 /* maximum number of endpoints */ + +#define MUSB2_READ_2(sc, reg) \ + bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) + +#define MUSB2_WRITE_2(sc, reg, data) \ + bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) + +#define MUSB2_READ_1(sc, reg) \ + bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) + +#define MUSB2_WRITE_1(sc, reg, data) \ + bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) + +struct musbotg_td; +struct musbotg_softc; + +typedef uint8_t (musbotg_cmd_t)(struct musbotg_td *td); + +struct musbotg_dma { + struct musbotg_softc *sc; + uint32_t dma_chan; + uint8_t busy:1; + uint8_t complete:1; + uint8_t error:1; +}; + +struct musbotg_td { + struct musbotg_td *obj_next; + musbotg_cmd_t *func; + struct usb2_page_cache *pc; + uint32_t offset; + uint32_t remainder; + uint16_t max_frame_size; /* packet_size * mult */ + uint8_t ep_no; + uint8_t error:1; + uint8_t alt_next:1; + uint8_t short_pkt:1; + uint8_t support_multi_buffer:1; + uint8_t did_stall:1; + uint8_t dma_enabled:1; +}; + +struct musbotg_std_temp { + musbotg_cmd_t *func; + struct usb2_page_cache *pc; + struct musbotg_td *td; + struct musbotg_td *td_next; + uint32_t len; + uint32_t offset; + uint16_t max_frame_size; + uint8_t short_pkt; + /* + * short_pkt = 0: transfer should be short terminated + * short_pkt = 1: transfer should not be short terminated + */ + uint8_t setup_alt_next; +}; + +struct musbotg_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union musbotg_hub_temp { + uWord wValue; + struct usb2_port_status ps; +}; + +struct musbotg_flags { + uint8_t change_connect:1; + uint8_t change_suspend:1; + uint8_t status_suspend:1; /* set if suspended */ + uint8_t status_vbus:1; /* set if present */ + uint8_t status_bus_reset:1; /* set if reset complete */ + uint8_t status_high_speed:1; /* set if High Speed is selected */ + uint8_t remote_wakeup:1; + uint8_t self_powered:1; + uint8_t clocks_off:1; + uint8_t port_powered:1; + uint8_t port_enabled:1; + uint8_t d_pulled_up:1; +}; + +struct musbotg_softc { + struct usb2_bus sc_bus; + union musbotg_hub_temp sc_hub_temp; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + struct usb2_config_td sc_config_td; + struct usb2_hw_ep_profile sc_hw_ep_profile[16]; + struct resource *sc_io_res; + struct resource *sc_irq_res; + void *sc_intr_hdl; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + void (*sc_clocks_on) (void *arg); + void (*sc_clocks_off) (void *arg); + void *sc_clocks_arg; + + uint32_t sc_bounce_buf[(1024 * 3) / 4]; /* bounce buffer */ + + uint8_t sc_ep_max; /* maximum number of RX and TX + * endpoints supported */ + uint8_t sc_rt_addr; /* root HUB address */ + uint8_t sc_dv_addr; /* device address */ + uint8_t sc_conf; /* root HUB config */ + uint8_t sc_ep0_busy; /* set if ep0 is busy */ + uint8_t sc_ep0_cmd; /* pending commands */ + uint8_t sc_conf_data; /* copy of hardware register */ + + uint8_t sc_hub_idata[1]; + + struct musbotg_flags sc_flags; +}; + +/* prototypes */ + +usb2_error_t musbotg_init(struct musbotg_softc *sc); +void musbotg_uninit(struct musbotg_softc *sc); +void musbotg_suspend(struct musbotg_softc *sc); +void musbotg_resume(struct musbotg_softc *sc); +void musbotg_interrupt(struct musbotg_softc *sc); + +#endif /* _MUSB2_OTG_H_ */ diff --git a/sys/dev/usb2/controller/musb2_otg_atmelarm.c b/sys/dev/usb2/controller/musb2_otg_atmelarm.c new file mode 100644 index 000000000000..58ee13496448 --- /dev/null +++ b/sys/dev/usb2/controller/musb2_otg_atmelarm.c @@ -0,0 +1,256 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static device_probe_t musbotg_probe; +static device_attach_t musbotg_attach; +static device_detach_t musbotg_detach; +static device_shutdown_t musbotg_shutdown; + +struct musbotg_super_softc { + struct musbotg_softc sc_otg; /* must be first */ +}; + +static void +musbotg_vbus_interrupt(struct musbotg_super_softc *sc) +{ + uint8_t vbus_val = 1; /* fake VBUS on - TODO */ + + /* just forward it */ + + (sc->sc_otg.sc_bus.methods->vbus_interrupt) + (&sc->sc_otg.sc_bus, vbus_val); + return; +} + +static void +musbotg_clocks_on(void *arg) +{ +#if 0 + struct musbotg_super_softc *sc = arg; + +#endif + + return; +} + +static void +musbotg_clocks_off(void *arg) +{ +#if 0 + struct musbotg_super_softc *sc = arg; + +#endif + + return; +} + +static int +musbotg_probe(device_t dev) +{ + device_set_desc(dev, "MUSB OTG integrated USB controller"); + return (0); +} + +static int +musbotg_attach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + int err; + int rid; + + if (sc == NULL) { + return (ENXIO); + } + /* setup MUSB OTG USB controller interface softc */ + + sc->sc_otg.sc_clocks_on = &musbotg_clocks_on; + sc->sc_otg.sc_clocks_off = &musbotg_clocks_off; + sc->sc_otg.sc_clocks_arg = sc; + + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_otg.sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + return (ENOMEM); + } + rid = 0; + sc->sc_otg.sc_io_res = + bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + + if (!(sc->sc_otg.sc_io_res)) { + err = ENOMEM; + goto error; + } + sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); + sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); + sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); + + rid = 0; + sc->sc_otg.sc_irq_res = + bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (!(sc->sc_otg.sc_irq_res)) { + goto error; + } + sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_otg.sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); + + err = usb2_config_td_setup(&sc->sc_otg.sc_config_td, sc, + &sc->sc_otg.sc_bus.mtx, NULL, 0, 4); + if (err) { + device_printf(dev, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); +#endif + if (err) { + sc->sc_otg.sc_intr_hdl = NULL; + goto error; + } + err = musbotg_init(&sc->sc_otg); + if (!err) { + err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); + } + if (err) { + goto error; + } else { + /* poll VBUS one time */ + musbotg_vbus_interrupt(sc); + } + return (0); + +error: + musbotg_detach(dev); + return (ENXIO); +} + +static int +musbotg_detach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_otg.sc_bus.bdev) { + bdev = sc->sc_otg.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { + /* + * only call musbotg_uninit() after musbotg_init() + */ + musbotg_uninit(&sc->sc_otg); + + err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, + sc->sc_otg.sc_intr_hdl); + sc->sc_otg.sc_intr_hdl = NULL; + } + /* free IRQ channel, if any */ + if (sc->sc_otg.sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->sc_otg.sc_irq_res); + sc->sc_otg.sc_irq_res = NULL; + } + /* free memory resource, if any */ + if (sc->sc_otg.sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, + sc->sc_otg.sc_io_res); + sc->sc_otg.sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_otg.sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); + + return (0); +} + +static int +musbotg_shutdown(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_shutdown(dev); + if (err) + return (err); + + musbotg_uninit(&sc->sc_otg); + + return (0); +} + +static device_method_t musbotg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, musbotg_probe), + DEVMETHOD(device_attach, musbotg_attach), + DEVMETHOD(device_detach, musbotg_detach), + DEVMETHOD(device_shutdown, musbotg_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t musbotg_driver = { + "musbotg", + musbotg_methods, + sizeof(struct musbotg_super_softc), +}; + +static devclass_t musbotg_devclass; + +DRIVER_MODULE(musbotg, atmelarm, musbotg_driver, musbotg_devclass, 0, 0); +MODULE_DEPEND(musbotg, usb2_controller, 1, 1, 1); +MODULE_DEPEND(musbotg, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/ohci2.c b/sys/dev/usb2/controller/ohci2.c new file mode 100644 index 000000000000..54133ba828ed --- /dev/null +++ b/sys/dev/usb2/controller/ohci2.c @@ -0,0 +1,2802 @@ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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 +__FBSDID("$FreeBSD$"); + +/* + * 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 +#include +#include +#include + +#define USB_DEBUG_VAR ohcidebug +#define usb2_config_td_cc ohci_config_copy +#define usb2_config_td_softc ohci_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OHCI_BUS2SC(bus) ((ohci_softc_t *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((ohci_softc_t *)0)->sc_bus)))) + +#if USB_DEBUG +static int ohcidebug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci"); +SYSCTL_INT(_hw_usb2_ohci, OID_AUTO, debug, CTLFLAG_RW, + &ohcidebug, 0, "ohci debug level"); +static void ohci_dumpregs(ohci_softc_t *); +static void ohci_dump_tds(ohci_td_t *); +static uint8_t ohci_dump_td(ohci_td_t *); +static void ohci_dump_ed(ohci_ed_t *); +static uint8_t ohci_dump_itd(ohci_itd_t *); +static void ohci_dump_itds(ohci_itd_t *); + +#endif + +#define OBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ + BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) +#define OWRITE1(sc, r, x) \ + do { OBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) +#define OWRITE2(sc, r, x) \ + do { OBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) +#define OWRITE4(sc, r, x) \ + do { OBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) +#define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) +#define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) +#define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) + +#define OHCI_INTR_ENDPT 1 + +extern struct usb2_bus_methods ohci_bus_methods; +extern struct usb2_pipe_methods ohci_device_bulk_methods; +extern struct usb2_pipe_methods ohci_device_ctrl_methods; +extern struct usb2_pipe_methods ohci_device_intr_methods; +extern struct usb2_pipe_methods ohci_device_isoc_methods; +extern struct usb2_pipe_methods ohci_root_ctrl_methods; +extern struct usb2_pipe_methods ohci_root_intr_methods; + +static usb2_config_td_command_t ohci_root_ctrl_task; +static void ohci_root_ctrl_poll(struct ohci_softc *sc); +static void ohci_do_poll(struct usb2_bus *bus); +static void ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error); + +static usb2_sw_transfer_func_t ohci_root_intr_done; +static usb2_sw_transfer_func_t ohci_root_ctrl_done; +static void ohci_timeout(void *arg); +static uint8_t ohci_check_transfer(struct usb2_xfer *xfer); + +struct ohci_std_temp { + struct usb2_page_cache *pc; + ohci_td_t *td; + ohci_td_t *td_next; + uint32_t average; + uint32_t td_flags; + uint32_t len; + uint16_t max_frame_size; + uint8_t shortpkt; + uint8_t setup_alt_next; + uint8_t short_frames_ok; +}; + +static struct ohci_hcca * +ohci_get_hcca(ohci_softc_t *sc) +{ + usb2_pc_cpu_invalidate(&sc->sc_hw.hcca_pc); + return (sc->sc_hcca_p); +} + +void +ohci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + uint32_t i; + + cb(bus, &sc->sc_hw.hcca_pc, &sc->sc_hw.hcca_pg, + sizeof(ohci_hcca_t), OHCI_HCCA_ALIGN); + + cb(bus, &sc->sc_hw.ctrl_start_pc, &sc->sc_hw.ctrl_start_pg, + sizeof(ohci_ed_t), OHCI_ED_ALIGN); + + cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, + sizeof(ohci_ed_t), OHCI_ED_ALIGN); + + cb(bus, &sc->sc_hw.isoc_start_pc, &sc->sc_hw.isoc_start_pg, + sizeof(ohci_ed_t), OHCI_ED_ALIGN); + + for (i = 0; i != OHCI_NO_EDS; i++) { + cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, + sizeof(ohci_ed_t), OHCI_ED_ALIGN); + } + return; +} + +static usb2_error_t +ohci_controller_init(ohci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint32_t i; + uint32_t ctl; + uint32_t ival; + uint32_t hcr; + uint32_t fm; + uint32_t per; + uint32_t desca; + + /* Determine in what context we are running. */ + ctl = OREAD4(sc, OHCI_CONTROL); + if (ctl & OHCI_IR) { + /* SMM active, request change */ + DPRINTF("SMM active, request owner change\n"); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_OCR); + for (i = 0; (i < 100) && (ctl & OHCI_IR); i++) { + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + ctl = OREAD4(sc, OHCI_CONTROL); + } + if (ctl & OHCI_IR) { + device_printf(sc->sc_bus.bdev, + "SMM does not respond, resetting\n"); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + goto reset; + } + } else { + DPRINTF("cold started\n"); +reset: + /* controller was cold started */ + usb2_pause_mtx(&sc->sc_bus.mtx, + 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); + + usb2_pause_mtx(&sc->sc_bus.mtx, + 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) { + device_printf(sc->sc_bus.bdev, "reset timeout\n"); + return (USB_ERR_IOERROR); + } +#if 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 */ + usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); + OWRITE4(sc, OHCI_HCCA, buf_res.physaddr); + + usb2_get_page(&sc->sc_hw.ctrl_start_pc, 0, &buf_res); + OWRITE4(sc, OHCI_CONTROL_HEAD_ED, buf_res.physaddr); + + usb2_get_page(&sc->sc_hw.bulk_start_pc, 0, &buf_res); + OWRITE4(sc, OHCI_BULK_HEAD_ED, buf_res.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 */ + usb2_pause_mtx(&sc->sc_bus.mtx, + 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++) { + usb2_pause_mtx(&sc->sc_bus.mtx, + OHCI_READ_DESC_DELAY); + sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A)); + } + +#if USB_DEBUG + if (ohcidebug > 5) { + ohci_dumpregs(sc); + } +#endif + return (USB_ERR_NORMAL_COMPLETION); +} + +static struct ohci_ed * +ohci_init_ed(struct usb2_page_cache *pc) +{ + struct usb2_page_search buf_res; + struct ohci_ed *ed; + + usb2_get_page(pc, 0, &buf_res); + + ed = buf_res.buffer; + + ed->ed_self = htole32(buf_res.physaddr); + ed->ed_flags = htole32(OHCI_ED_SKIP); + ed->page_cache = pc; + + return (ed); +} + +usb2_error_t +ohci_init(ohci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint16_t i; + uint16_t bit; + uint16_t x; + uint16_t y; + + mtx_lock(&sc->sc_bus.mtx); + + DPRINTF("start\n"); + + sc->sc_eintrs = OHCI_NORMAL_INTRS; + + /* + * Setup all ED's + */ + + sc->sc_ctrl_p_last = + ohci_init_ed(&sc->sc_hw.ctrl_start_pc); + + sc->sc_bulk_p_last = + ohci_init_ed(&sc->sc_hw.bulk_start_pc); + + sc->sc_isoc_p_last = + ohci_init_ed(&sc->sc_hw.isoc_start_pc); + + for (i = 0; i != OHCI_NO_EDS; i++) { + sc->sc_intr_p_last[i] = + ohci_init_ed(sc->sc_hw.intr_start_pc + i); + } + + /* + * the QHs are arranged to give poll intervals that are + * powers of 2 times 1ms + */ + bit = OHCI_NO_EDS / 2; + while (bit) { + x = bit; + while (x & bit) { + ohci_ed_t *ed_x; + ohci_ed_t *ed_y; + + y = (x ^ bit) | (bit / 2); + + /* + * the next QH has half the poll interval + */ + ed_x = sc->sc_intr_p_last[x]; + ed_y = sc->sc_intr_p_last[y]; + + ed_x->next = NULL; + ed_x->ed_next = ed_y->ed_self; + + x++; + } + bit >>= 1; + } + + if (1) { + + ohci_ed_t *ed_int; + ohci_ed_t *ed_isc; + + ed_int = sc->sc_intr_p_last[0]; + ed_isc = sc->sc_isoc_p_last; + + /* the last (1ms) QH */ + ed_int->next = ed_isc; + ed_int->ed_next = ed_isc->ed_self; + } + usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); + + sc->sc_hcca_p = buf_res.buffer; + + /* + * 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_p->hcca_interrupt_table[i] = + sc->sc_intr_p_last[i | (OHCI_NO_EDS / 2)]->ed_self; + } + /* flush all cache into memory */ + + usb2_bus_mem_flush_all(&sc->sc_bus, &ohci_iterate_hw_softc); + + /* set up the bus struct */ + sc->sc_bus.methods = &ohci_bus_methods; + + usb2_callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.mtx, + CALLOUT_RETURNUNLOCKED); + +#if USB_DEBUG + if (ohcidebug > 15) { + for (i = 0; i != OHCI_NO_EDS; i++) { + printf("ed#%d ", i); + ohci_dump_ed(sc->sc_intr_p_last[i]); + } + printf("iso "); + ohci_dump_ed(sc->sc_isoc_p_last); + } +#endif + + sc->sc_bus.usbrev = USB_REV_1_0; + + if (ohci_controller_init(sc)) { + mtx_unlock(&sc->sc_bus.mtx); + return (USB_ERR_INVAL); + } else { + mtx_unlock(&sc->sc_bus.mtx); + /* catch any lost interrupts */ + ohci_do_poll(&sc->sc_bus); + return (USB_ERR_NORMAL_COMPLETION); + } +} + +/* + * shut down the controller when the system is going down + */ +void +ohci_detach(struct ohci_softc *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + + usb2_callout_stop(&sc->sc_tmo_rhsc); + + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + + /* XXX let stray task complete */ + usb2_pause_mtx(&sc->sc_bus.mtx, 50); + + mtx_unlock(&sc->sc_bus.mtx); + + usb2_callout_drain(&sc->sc_tmo_rhsc); + + return; +} + +/* NOTE: suspend/resume is called from + * interrupt context and cannot sleep! + */ +void +ohci_suspend(ohci_softc_t *sc) +{ + uint32_t ctl; + + mtx_lock(&sc->sc_bus.mtx); + +#if USB_DEBUG + DPRINTF("\n"); + if (ohcidebug > 2) { + ohci_dumpregs(sc); + } +#endif + + 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); + + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_RESUME_WAIT); + + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +void +ohci_resume(ohci_softc_t *sc) +{ + uint32_t ctl; + + mtx_lock(&sc->sc_bus.mtx); + +#if USB_DEBUG + DPRINTF("\n"); + if (ohcidebug > 2) { + ohci_dumpregs(sc); + } +#endif + /* some broken BIOSes never initialize the 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); + usb2_pause_mtx(&sc->sc_bus.mtx, USB_RESUME_DELAY); + ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; + OWRITE4(sc, OHCI_CONTROL, ctl); + usb2_pause_mtx(&sc->sc_bus.mtx, USB_RESUME_RECOVERY); + sc->sc_control = sc->sc_intre = 0; + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch any lost interrupts */ + ohci_do_poll(&sc->sc_bus); + + return; +} + +#if USB_DEBUG +static void +ohci_dumpregs(ohci_softc_t *sc) +{ + struct ohci_hcca *hcca; + + 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))); + + hcca = ohci_get_hcca(sc); + + DPRINTF(" HCCA: frame_number=0x%04x done_head=0x%08x\n", + le32toh(hcca->hcca_frame_number), + le32toh(hcca->hcca_done_head)); + return; +} +static void +ohci_dump_tds(ohci_td_t *std) +{ + for (; std; std = std->obj_next) { + if (ohci_dump_td(std)) { + break; + } + } + return; +} + +static uint8_t +ohci_dump_td(ohci_td_t *std) +{ + uint32_t td_flags; + uint8_t temp; + + usb2_pc_cpu_invalidate(std->page_cache); + + td_flags = le32toh(std->td_flags); + temp = (std->td_next == 0); + + printf("TD(%p) at 0x%08x: %s%s%s%s%s delay=%d ec=%d " + "cc=%d\ncbp=0x%08x next=0x%08x be=0x%08x\n", + std, le32toh(std->td_self), + (td_flags & OHCI_TD_R) ? "-R" : "", + (td_flags & OHCI_TD_OUT) ? "-OUT" : "", + (td_flags & OHCI_TD_IN) ? "-IN" : "", + ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_1) ? "-TOG1" : "", + ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_0) ? "-TOG0" : "", + OHCI_TD_GET_DI(td_flags), + OHCI_TD_GET_EC(td_flags), + OHCI_TD_GET_CC(td_flags), + le32toh(std->td_cbp), + le32toh(std->td_next), + le32toh(std->td_be)); + + return (temp); +} + +static uint8_t +ohci_dump_itd(ohci_itd_t *sitd) +{ + uint32_t itd_flags; + uint16_t i; + uint8_t temp; + + usb2_pc_cpu_invalidate(sitd->page_cache); + + itd_flags = le32toh(sitd->itd_flags); + temp = (sitd->itd_next == 0); + + printf("ITD(%p) at 0x%08x: sf=%d di=%d fc=%d cc=%d\n" + "bp0=0x%08x next=0x%08x be=0x%08x\n", + sitd, le32toh(sitd->itd_self), + OHCI_ITD_GET_SF(itd_flags), + OHCI_ITD_GET_DI(itd_flags), + OHCI_ITD_GET_FC(itd_flags), + OHCI_ITD_GET_CC(itd_flags), + le32toh(sitd->itd_bp0), + le32toh(sitd->itd_next), + le32toh(sitd->itd_be)); + for (i = 0; i < OHCI_ITD_NOFFSET; i++) { + printf("offs[%d]=0x%04x ", i, + (uint32_t)le16toh(sitd->itd_offset[i])); + } + printf("\n"); + + return (temp); +} + +static void +ohci_dump_itds(ohci_itd_t *sitd) +{ + for (; sitd; sitd = sitd->obj_next) { + if (ohci_dump_itd(sitd)) { + break; + } + } + return; +} + +static void +ohci_dump_ed(ohci_ed_t *sed) +{ + uint32_t ed_flags; + uint32_t ed_headp; + + usb2_pc_cpu_invalidate(sed->page_cache); + + ed_flags = le32toh(sed->ed_flags); + ed_headp = le32toh(sed->ed_headp); + + printf("ED(%p) at 0x%08x: addr=%d endpt=%d maxp=%d flags=%s%s%s%s%s\n" + "tailp=0x%08x headflags=%s%s headp=0x%08x nexted=0x%08x\n", + sed, le32toh(sed->ed_self), + OHCI_ED_GET_FA(ed_flags), + OHCI_ED_GET_EN(ed_flags), + OHCI_ED_GET_MAXP(ed_flags), + (ed_flags & OHCI_ED_DIR_OUT) ? "-OUT" : "", + (ed_flags & OHCI_ED_DIR_IN) ? "-IN" : "", + (ed_flags & OHCI_ED_SPEED) ? "-LOWSPEED" : "", + (ed_flags & OHCI_ED_SKIP) ? "-SKIP" : "", + (ed_flags & OHCI_ED_FORMAT_ISO) ? "-ISO" : "", + le32toh(sed->ed_tailp), + (ed_headp & OHCI_HALTED) ? "-HALTED" : "", + (ed_headp & OHCI_TOGGLECARRY) ? "-CARRY" : "", + le32toh(sed->ed_headp), + le32toh(sed->ed_next)); + return; +} + +#endif + +static void +ohci_transfer_intr_enqueue(struct usb2_xfer *xfer) +{ + /* check for early completion */ + if (ohci_check_transfer(xfer)) { + return; + } + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout); + } + return; +} + +#define OHCI_APPEND_QH(sed,td_self,last) (last) = _ohci_append_qh(sed,td_self,last) +static ohci_ed_t * +_ohci_append_qh(ohci_ed_t *sed, uint32_t td_self, ohci_ed_t *last) +{ + DPRINTFN(11, "%p to %p\n", sed, last); + + /* (sc->sc_bus.mtx) must be locked */ + + sed->next = last->next; + sed->ed_next = last->ed_next; + sed->ed_tailp = 0; + sed->ed_headp = td_self; + + sed->prev = last; + + usb2_pc_cpu_flush(sed->page_cache); + + /* + * the last->next->prev is never followed: sed->next->prev = sed; + */ + + last->next = sed; + last->ed_next = sed->ed_self; + + usb2_pc_cpu_flush(last->page_cache); + + return (sed); +} + +#define OHCI_REMOVE_QH(sed,last) (last) = _ohci_remove_qh(sed,last) +static ohci_ed_t * +_ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last) +{ + DPRINTFN(11, "%p from %p\n", sed, last); + + /* (sc->sc_bus.mtx) must be locked */ + + /* only remove if not removed from a queue */ + if (sed->prev) { + + sed->prev->next = sed->next; + sed->prev->ed_next = sed->ed_next; + + usb2_pc_cpu_flush(sed->prev->page_cache); + + if (sed->next) { + sed->next->prev = sed->prev; + usb2_pc_cpu_flush(sed->next->page_cache); + } + /* + * terminate transfer in case the transferred packet was + * short so that the ED still points at the last used TD + */ + sed->ed_flags |= htole32(OHCI_ED_SKIP); + sed->ed_headp = sed->ed_tailp; + + last = ((last == sed) ? sed->prev : last); + + sed->prev = 0; + + usb2_pc_cpu_flush(sed->page_cache); + } + return (last); +} + +static void +ohci_isoc_done(struct usb2_xfer *xfer) +{ + uint8_t nframes; + uint32_t *plen = xfer->frlengths; + volatile uint16_t *olen; + uint16_t len = 0; + ohci_itd_t *td = xfer->td_transfer_first; + + while (1) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } +#if USB_DEBUG + if (ohcidebug > 5) { + DPRINTF("isoc TD\n"); + ohci_dump_itd(td); + } +#endif + usb2_pc_cpu_invalidate(td->page_cache); + + nframes = td->frames; + olen = &td->itd_offset[0]; + + if (nframes > 8) { + nframes = 8; + } + while (nframes--) { + len = le16toh(*olen); + + if ((len >> 12) == OHCI_CC_NOT_ACCESSED) { + len = 0; + } else { + len &= ((1 << 12) - 1); + } + + if (len > *plen) { + len = 0;/* invalid length */ + } + *plen = len; + plen++; + olen++; + } + + if (((void *)td) == xfer->td_transfer_last) { + break; + } + td = td->obj_next; + } + + xfer->aframes = xfer->nframes; + ohci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); + return; +} + +#if USB_DEBUG +static const char *const + 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 + +static usb2_error_t +ohci_non_isoc_done_sub(struct usb2_xfer *xfer) +{ + ohci_td_t *td; + ohci_td_t *td_alt_next; + uint32_t temp; + uint32_t phy_start; + uint32_t phy_end; + uint32_t td_flags; + uint16_t cc; + + td = xfer->td_transfer_cache; + td_alt_next = td->alt_next; + td_flags = 0; + + while (1) { + + usb2_pc_cpu_invalidate(td->page_cache); + phy_start = le32toh(td->td_cbp); + td_flags = le32toh(td->td_flags); + cc = OHCI_TD_GET_CC(td_flags); + + if (phy_start) { + /* + * short transfer - compute the number of remaining + * bytes in the hardware buffer: + */ + phy_end = le32toh(td->td_be); + temp = (OHCI_PAGE(phy_start ^ phy_end) ? + (OHCI_PAGE_SIZE + 1) : 0x0001); + temp += OHCI_PAGE_OFFSET(phy_end); + temp -= OHCI_PAGE_OFFSET(phy_start); + + if (temp > td->len) { + /* guard against corruption */ + cc = OHCI_CC_STALL; + } else if (xfer->aframes != xfer->nframes) { + /* + * subtract remaining length from + * "frlengths[]" + */ + xfer->frlengths[xfer->aframes] -= temp; + } + } + /* Check for last transfer */ + if (((void *)td) == xfer->td_transfer_last) { + td = NULL; + break; + } + /* Check transfer status */ + if (cc) { + /* the transfer is finished */ + td = NULL; + break; + } + /* Check for short transfer */ + if (phy_start) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + td = td->alt_next; + } else { + /* the transfer is finished */ + td = NULL; + } + break; + } + td = td->obj_next; + + if (td->alt_next != td_alt_next) { + /* this USB frame is complete */ + break; + } + } + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + DPRINTFN(16, "error cc=%d (%s)\n", + cc, ohci_cc_strs[cc]); + + return ((cc == 0) ? USB_ERR_NORMAL_COMPLETION : + (cc == OHCI_CC_STALL) ? USB_ERR_STALLED : USB_ERR_IOERROR); +} + +static void +ohci_non_isoc_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + +#if USB_DEBUG + if (ohcidebug > 10) { + ohci_dump_tds(xfer->td_transfer_first); + } +#endif + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + err = ohci_non_isoc_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = ohci_non_isoc_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = ohci_non_isoc_done_sub(xfer); + } +done: + ohci_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * ohci_check_transfer_sub + *------------------------------------------------------------------------*/ +static void +ohci_check_transfer_sub(struct usb2_xfer *xfer) +{ + ohci_td_t *td; + ohci_ed_t *ed; + uint32_t phy_start; + uint32_t td_flags; + uint32_t td_next; + uint16_t cc; + + td = xfer->td_transfer_cache; + + while (1) { + + usb2_pc_cpu_invalidate(td->page_cache); + phy_start = le32toh(td->td_cbp); + td_flags = le32toh(td->td_flags); + td_next = le32toh(td->td_next); + + /* Check for last transfer */ + if (((void *)td) == xfer->td_transfer_last) { + /* the transfer is finished */ + td = NULL; + break; + } + /* Check transfer status */ + cc = OHCI_TD_GET_CC(td_flags); + if (cc) { + /* the transfer is finished */ + td = NULL; + break; + } + /* + * Check if we reached the last packet + * or if there is a short packet: + */ + + if (((td_next & (~0xF)) == OHCI_TD_NEXT_END) || phy_start) { + /* follow alt next */ + td = td->alt_next; + break; + } + td = td->obj_next; + } + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + if (td) { + + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + ed->ed_headp = td->td_self; + usb2_pc_cpu_flush(ed->page_cache); + + DPRINTFN(13, "xfer=%p following alt next\n", xfer); + } + return; +} + +/*------------------------------------------------------------------------* + * ohci_check_transfer + * + * Return values: + * 0: USB transfer is not finished + * Else: USB transfer is finished + *------------------------------------------------------------------------*/ +static uint8_t +ohci_check_transfer(struct usb2_xfer *xfer) +{ + ohci_ed_t *ed; + uint32_t ed_flags; + uint32_t ed_headp; + uint32_t ed_tailp; + + DPRINTFN(13, "xfer=%p checking transfer\n", xfer); + + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + usb2_pc_cpu_invalidate(ed->page_cache); + ed_flags = le32toh(ed->ed_flags); + ed_headp = le32toh(ed->ed_headp); + ed_tailp = le32toh(ed->ed_tailp); + + if ((ed_flags & OHCI_ED_SKIP) || + (ed_headp & OHCI_HALTED) || + (((ed_headp ^ ed_tailp) & (~0xF)) == 0)) { + if (xfer->pipe->methods == &ohci_device_isoc_methods) { + /* isochronous transfer */ + ohci_isoc_done(xfer); + } else { + if (xfer->flags_int.short_frames_ok) { + ohci_check_transfer_sub(xfer); + if (xfer->td_transfer_cache) { + /* not finished yet */ + return (0); + } + } + /* store data-toggle */ + if (ed_headp & OHCI_TOGGLECARRY) { + xfer->pipe->toggle_next = 1; + } else { + xfer->pipe->toggle_next = 0; + } + + /* non-isochronous transfer */ + ohci_non_isoc_done(xfer); + } + return (1); + } + DPRINTFN(13, "xfer=%p is still active\n", xfer); + return (0); +} + +static void +ohci_rhsc_enable(ohci_softc_t *sc) +{ + DPRINTFN(5, "\n"); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + sc->sc_eintrs |= OHCI_RHSC; + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); + + /* acknowledge any RHSC interrupt */ + OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC); + + usb2_sw_transfer(&sc->sc_root_intr, + &ohci_root_intr_done); + + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +static void +ohci_interrupt_poll(ohci_softc_t *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + /* + * check if transfer is transferred + */ + if (ohci_check_transfer(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + return; +} + +/*------------------------------------------------------------------------* + * ohci_interrupt - OHCI interrupt handler + * + * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, + * hence the interrupt handler will be setup before "sc->sc_bus.bdev" + * is present ! + *------------------------------------------------------------------------*/ +void +ohci_interrupt(ohci_softc_t *sc) +{ + struct ohci_hcca *hcca; + uint32_t status; + uint32_t done; + + mtx_lock(&sc->sc_bus.mtx); + + hcca = ohci_get_hcca(sc); + + DPRINTFN(16, "real interrupt\n"); + +#if USB_DEBUG + if (ohcidebug > 15) { + ohci_dumpregs(sc); + } +#endif + + done = le32toh(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) { + status = 0; + + if (done & ~OHCI_DONE_INTRS) { + status |= OHCI_WDH; + } + if (done & OHCI_DONE_INTRS) { + status |= OREAD4(sc, OHCI_INTERRUPT_STATUS); + } + hcca->hcca_done_head = 0; + + usb2_pc_cpu_flush(&sc->sc_hw.hcca_pc); + } else { + status = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH; + } + + status &= ~OHCI_MIE; + if (status == 0) { + /* + * nothing to be done (PCI shared + * interrupt) + */ + goto done; + } + OWRITE4(sc, OHCI_INTERRUPT_STATUS, status); /* Acknowledge */ + + status &= sc->sc_eintrs; + if (status == 0) { + goto done; + } + if (status & (OHCI_SO | OHCI_RD | OHCI_UE | OHCI_RHSC)) { +#if 0 + if (status & OHCI_SO) { + /* XXX do what */ + } +#endif + if (status & OHCI_RD) { + printf("%s: resume detect\n", __FUNCTION__); + /* XXX process resume detect */ + } + if (status & OHCI_UE) { + printf("%s: unrecoverable error, " + "controller halted\n", __FUNCTION__); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + /* XXX what else */ + } + if (status & OHCI_RHSC) { + /* + * Disable RHSC interrupt for now, because it will be + * on until the port has been reset. + */ + sc->sc_eintrs &= ~OHCI_RHSC; + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); + + usb2_sw_transfer(&sc->sc_root_intr, + &ohci_root_intr_done); + + /* do not allow RHSC interrupts > 1 per second */ + usb2_callout_reset(&sc->sc_tmo_rhsc, hz, + (void *)&ohci_rhsc_enable, sc); + } + } + status &= ~(OHCI_RHSC | OHCI_WDH | OHCI_SO); + if (status != 0) { + /* Block unprocessed interrupts. XXX */ + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, status); + sc->sc_eintrs &= ~status; + printf("%s: blocking intrs 0x%x\n", + __FUNCTION__, status); + } + /* poll all the USB transfers */ + ohci_interrupt_poll(sc); + +done: + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/* + * called when a request does not complete + */ +static void +ohci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + ohci_softc_t *sc = xfer->usb2_sc; + + DPRINTF("xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + ohci_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +ohci_do_poll(struct usb2_bus *bus) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + ohci_interrupt_poll(sc); + ohci_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +static void +ohci_setup_standard_chain_sub(struct ohci_std_temp *temp) +{ + struct usb2_page_search buf_res; + ohci_td_t *td; + ohci_td_t *td_next; + ohci_td_t *td_alt_next; + uint32_t buf_offset; + uint32_t average; + uint32_t len_old; + uint8_t shortpkt_old; + uint8_t precompute; + + td_alt_next = NULL; + buf_offset = 0; + shortpkt_old = temp->shortpkt; + len_old = temp->len; + precompute = 1; + + /* software is used to detect short incoming transfers */ + + if ((temp->td_flags & htole32(OHCI_TD_DP_MASK)) == htole32(OHCI_TD_IN)) { + temp->td_flags |= htole32(OHCI_TD_R); + } else { + temp->td_flags &= ~htole32(OHCI_TD_R); + } + +restart: + + td = temp->td; + td_next = temp->td_next; + + while (1) { + + if (temp->len == 0) { + + if (temp->shortpkt) { + break; + } + /* send a Zero Length Packet, ZLP, last */ + + temp->shortpkt = 1; + average = 0; + + } else { + + average = temp->average; + + if (temp->len < average) { + if (temp->len % temp->max_frame_size) { + temp->shortpkt = 1; + } + average = temp->len; + } + } + + if (td_next == NULL) { + panic("%s: out of OHCI transfer descriptors!", __FUNCTION__); + } + /* get next TD */ + + td = td_next; + td_next = td->obj_next; + + /* check if we are pre-computing */ + + if (precompute) { + + /* update remaining length */ + + temp->len -= average; + + continue; + } + /* fill out current TD */ + td->td_flags = temp->td_flags; + + /* the next TD uses TOGGLE_CARRY */ + temp->td_flags &= ~htole32(OHCI_TD_TOGGLE_MASK); + + if (average == 0) { + + td->td_cbp = 0; + td->td_be = ~0; + td->len = 0; + + } else { + + usb2_get_page(temp->pc, buf_offset, &buf_res); + td->td_cbp = htole32(buf_res.physaddr); + buf_offset += (average - 1); + + usb2_get_page(temp->pc, buf_offset, &buf_res); + td->td_be = htole32(buf_res.physaddr); + buf_offset++; + + td->len = average; + + /* update remaining length */ + + temp->len -= average; + } + + if ((td_next == td_alt_next) && temp->setup_alt_next) { + /* we need to receive these frames one by one ! */ + td->td_flags &= htole32(~OHCI_TD_INTR_MASK); + td->td_flags |= htole32(OHCI_TD_SET_DI(1)); + td->td_next = htole32(OHCI_TD_NEXT_END); + } else { + if (td_next) { + /* link the current TD with the next one */ + td->td_next = td_next->td_self; + } + } + + td->alt_next = td_alt_next; + + usb2_pc_cpu_flush(td->page_cache); + } + + if (precompute) { + precompute = 0; + + /* setup alt next pointer, if any */ + if (temp->short_frames_ok) { + if (temp->setup_alt_next) { + td_alt_next = td_next; + } + } else { + /* we use this field internally */ + td_alt_next = td_next; + } + + /* restore */ + temp->shortpkt = shortpkt_old; + temp->len = len_old; + goto restart; + } + temp->td = td; + temp->td_next = td_next; + + return; +} + +static void +ohci_setup_standard_chain(struct usb2_xfer *xfer, ohci_ed_t **ed_last) +{ + struct ohci_std_temp temp; + struct usb2_pipe_methods *methods; + ohci_ed_t *ed; + ohci_td_t *td; + uint32_t ed_flags; + uint32_t x; + + DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.average = xfer->max_usb2_frame_size; + temp.max_frame_size = xfer->max_frame_size; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + temp.td = NULL; + temp.td_next = td; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.short_frames_ok = xfer->flags_int.short_frames_ok; + + methods = xfer->pipe->methods; + + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC | + OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); + + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.shortpkt = temp.len ? 1 : 0; + + ohci_setup_standard_chain_sub(&temp); + + /* + * XXX assume that the setup message is + * contained within one USB packet: + */ + xfer->pipe->toggle_next = 1; + } + x = 1; + } else { + x = 0; + } + temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); + + /* set data toggle */ + + if (xfer->pipe->toggle_next) { + temp.td_flags |= htole32(OHCI_TD_TOGGLE_1); + } else { + temp.td_flags |= htole32(OHCI_TD_TOGGLE_0); + } + + /* set endpoint direction */ + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + temp.td_flags |= htole32(OHCI_TD_IN); + } else { + temp.td_flags |= htole32(OHCI_TD_OUT); + } + + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + temp.pc = xfer->frbuffers + x; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.shortpkt = 0; + + } else { + + /* regular data transfer */ + + temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + ohci_setup_standard_chain_sub(&temp); + } + + /* check if we should append a status stage */ + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + /* + * Send a DATA1 message and invert the current endpoint + * direction. + */ + + /* set endpoint direction and data toggle */ + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + temp.td_flags = htole32(OHCI_TD_OUT | + OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); + } else { + temp.td_flags = htole32(OHCI_TD_IN | + OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); + } + + temp.len = 0; + temp.pc = NULL; + temp.shortpkt = 0; + + ohci_setup_standard_chain_sub(&temp); + } + td = temp.td; + + td->td_next = htole32(OHCI_TD_NEXT_END); + td->td_flags &= ~htole32(OHCI_TD_INTR_MASK); + td->td_flags |= htole32(OHCI_TD_SET_DI(1)); + + usb2_pc_cpu_flush(td->page_cache); + + /* must have at least one frame! */ + + xfer->td_transfer_last = td; + +#if USB_DEBUG + if (ohcidebug > 8) { + DPRINTF("nexttog=%d; data before transfer:\n", + xfer->pipe->toggle_next); + ohci_dump_tds(xfer->td_transfer_first); + } +#endif + + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + ed_flags = (OHCI_ED_SET_FA(xfer->address) | + OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) | + OHCI_ED_SET_MAXP(xfer->max_frame_size)); + + ed_flags |= (OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD); + + if (xfer->udev->speed == USB_SPEED_LOW) { + ed_flags |= OHCI_ED_SPEED; + } + ed->ed_flags = htole32(ed_flags); + + usb2_pc_cpu_flush(ed->page_cache); + + td = xfer->td_transfer_first; + + OHCI_APPEND_QH(ed, td->td_self, *ed_last); + + if (methods == &ohci_device_bulk_methods) { + ohci_softc_t *sc = xfer->usb2_sc; + + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + } + if (methods == &ohci_device_ctrl_methods) { + ohci_softc_t *sc = xfer->usb2_sc; + + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + } + return; +} + +static void +ohci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ohci_softc_t *sc = xfer->usb2_sc; + uint32_t hstatus; + uint16_t i; + uint16_t m; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + ohci_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); + + /* clear any old interrupt data */ + bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); + + hstatus = OREAD4(sc, OHCI_RH_STATUS); + DPRINTF("sc=%p xfer=%p hstatus=0x%08x\n", + sc, xfer, hstatus); + + /* set bits */ + m = (sc->sc_noport + 1); + if (m > (8 * sizeof(sc->sc_hub_idata))) { + m = (8 * sizeof(sc->sc_hub_idata)); + } + for (i = 1; i < m; i++) { + /* pick out CHANGE bits from the status register */ + if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) { + sc->sc_hub_idata[i / 8] |= 1 << (i % 8); + DPRINTF("port %d changed\n", i); + } + } +done: + return; +} + +/* NOTE: "done" can be run two times in a row, + * from close and from interrupt + */ +static void +ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct usb2_pipe_methods *methods = xfer->pipe->methods; + ohci_softc_t *sc = xfer->usb2_sc; + ohci_ed_t *ed; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + if (ed) { + usb2_pc_cpu_invalidate(ed->page_cache); + } + if (methods == &ohci_device_bulk_methods) { + OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); + } + if (methods == &ohci_device_ctrl_methods) { + OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); + } + if (methods == &ohci_device_intr_methods) { + OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); + } + if (methods == &ohci_device_isoc_methods) { + OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last); + } + xfer->td_transfer_first = NULL; + xfer->td_transfer_last = NULL; + + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +/*------------------------------------------------------------------------* + * ohci bulk support + *------------------------------------------------------------------------*/ +static void +ohci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_bulk_close(struct usb2_xfer *xfer) +{ + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ohci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_bulk_start(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ohci_setup_standard_chain(xfer, &sc->sc_bulk_p_last); + + /* put transfer on interrupt queue */ + ohci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ohci_device_bulk_methods = +{ + .open = ohci_device_bulk_open, + .close = ohci_device_bulk_close, + .enter = ohci_device_bulk_enter, + .start = ohci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ohci control support + *------------------------------------------------------------------------*/ +static void +ohci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_ctrl_close(struct usb2_xfer *xfer) +{ + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ohci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_ctrl_start(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ohci_setup_standard_chain(xfer, &sc->sc_ctrl_p_last); + + /* put transfer on interrupt queue */ + ohci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ohci_device_ctrl_methods = +{ + .open = ohci_device_ctrl_open, + .close = ohci_device_ctrl_close, + .enter = ohci_device_ctrl_enter, + .start = ohci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ohci interrupt support + *------------------------------------------------------------------------*/ +static void +ohci_device_intr_open(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + uint16_t best; + uint16_t bit; + uint16_t x; + + best = 0; + bit = OHCI_NO_EDS / 2; + while (bit) { + if (xfer->interval >= bit) { + x = bit; + best = bit; + while (x & bit) { + if (sc->sc_intr_stat[x] < + sc->sc_intr_stat[best]) { + best = x; + } + x++; + } + break; + } + bit >>= 1; + } + + sc->sc_intr_stat[best]++; + xfer->qh_pos = best; + + DPRINTFN(3, "best=%d interval=%d\n", + best, xfer->interval); + return; +} + +static void +ohci_device_intr_close(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + sc->sc_intr_stat[xfer->qh_pos]--; + + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ohci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_intr_start(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + /* setup TD's and QH */ + ohci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); + + /* put transfer on interrupt queue */ + ohci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ohci_device_intr_methods = +{ + .open = ohci_device_intr_open, + .close = ohci_device_intr_close, + .enter = ohci_device_intr_enter, + .start = ohci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ohci isochronous support + *------------------------------------------------------------------------*/ +static void +ohci_device_isoc_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_device_isoc_close(struct usb2_xfer *xfer) +{ + /**/ + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ohci_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ohci_softc_t *sc = xfer->usb2_sc; + struct ohci_hcca *hcca; + uint32_t buf_offset; + uint32_t nframes; + uint32_t ed_flags; + uint32_t *plen; + uint16_t itd_offset[OHCI_ITD_NOFFSET]; + uint16_t length; + uint8_t ncur; + ohci_itd_t *td; + ohci_itd_t *td_last = NULL; + ohci_ed_t *ed; + + hcca = ohci_get_hcca(sc); + + nframes = le32toh(hcca->hcca_frame_number); + + DPRINTFN(6, "xfer=%p isoc_next=%u nframes=%u hcca_fn=%u\n", + xfer, xfer->pipe->isoc_next, xfer->nframes, nframes); + + if ((xfer->pipe->is_synced == 0) || + (((nframes - xfer->pipe->isoc_next) & 0xFFFF) < xfer->nframes) || + (((xfer->pipe->isoc_next - nframes) & 0xFFFF) >= 128)) { + /* + * If there is data underflow or the pipe queue is empty we + * schedule the transfer a few frames ahead of the current + * frame position. Else two isochronous transfers might + * overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & 0xFFFF; + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + buf_offset = ((xfer->pipe->isoc_next - nframes) & 0xFFFF); + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + (usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + + xfer->nframes); + + /* get the real number of frames */ + + nframes = xfer->nframes; + + buf_offset = 0; + + plen = xfer->frlengths; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + + xfer->td_transfer_first = td; + + ncur = 0; + length = 0; + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + itd_offset[ncur] = length; + buf_offset += *plen; + length += *plen; + plen++; + ncur++; + + if ( /* check if the ITD is full */ + (ncur == OHCI_ITD_NOFFSET) || + /* check if we have put more than 4K into the ITD */ + (length & 0xF000) || + /* check if it is the last frame */ + (nframes == 0)) { + + /* fill current ITD */ + td->itd_flags = htole32( + OHCI_ITD_NOCC | + OHCI_ITD_SET_SF(xfer->pipe->isoc_next) | + OHCI_ITD_NOINTR | + OHCI_ITD_SET_FC(ncur)); + + td->frames = ncur; + xfer->pipe->isoc_next += ncur; + + if (length == 0) { + /* all zero */ + td->itd_bp0 = 0; + td->itd_be = ~0; + + while (ncur--) { + td->itd_offset[ncur] = + htole16(OHCI_ITD_MK_OFFS(0)); + } + } else { + usb2_get_page(xfer->frbuffers, buf_offset - length, &buf_res); + length = OHCI_PAGE_MASK(buf_res.physaddr); + buf_res.physaddr = + OHCI_PAGE(buf_res.physaddr); + td->itd_bp0 = htole32(buf_res.physaddr); + usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); + td->itd_be = htole32(buf_res.physaddr); + + while (ncur--) { + itd_offset[ncur] += length; + itd_offset[ncur] = + OHCI_ITD_MK_OFFS(itd_offset[ncur]); + td->itd_offset[ncur] = + htole16(itd_offset[ncur]); + } + } + ncur = 0; + length = 0; + td_last = td; + td = td->obj_next; + + if (td) { + /* link the last TD with the next one */ + td_last->itd_next = td->itd_self; + } + usb2_pc_cpu_flush(td_last->page_cache); + } + } + + /* update the last TD */ + td_last->itd_flags &= ~htole32(OHCI_ITD_NOINTR); + td_last->itd_flags |= htole32(OHCI_ITD_SET_DI(0)); + td_last->itd_next = 0; + + usb2_pc_cpu_flush(td_last->page_cache); + + xfer->td_transfer_last = td_last; + +#if USB_DEBUG + if (ohcidebug > 8) { + DPRINTF("data before transfer:\n"); + ohci_dump_itds(xfer->td_transfer_first); + } +#endif + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) + ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO); + else + ed_flags = (OHCI_ED_DIR_OUT | OHCI_ED_FORMAT_ISO); + + ed_flags |= (OHCI_ED_SET_FA(xfer->address) | + OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) | + OHCI_ED_SET_MAXP(xfer->max_frame_size)); + + if (xfer->udev->speed == USB_SPEED_LOW) { + ed_flags |= OHCI_ED_SPEED; + } + ed->ed_flags = htole32(ed_flags); + + usb2_pc_cpu_flush(ed->page_cache); + + td = xfer->td_transfer_first; + + OHCI_APPEND_QH(ed, td->itd_self, sc->sc_isoc_p_last); + return; +} + +static void +ohci_device_isoc_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ohci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods ohci_device_isoc_methods = +{ + .open = ohci_device_isoc_open, + .close = ohci_device_isoc_close, + .enter = ohci_device_isoc_enter, + .start = ohci_device_isoc_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * ohci root control support + *------------------------------------------------------------------------* + * simulate a hardware hub by handling + * all the necessary requests + *------------------------------------------------------------------------*/ + +static void +ohci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_root_ctrl_close(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* data structures and routines + * to emulate the root hub: + */ +static const +struct usb2_device_descriptor ohci_devd = +{ + sizeof(struct usb2_device_descriptor), + 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 const +struct ohci_config_desc ohci_confd = +{ + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(ohci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0, /* max power */ + }, + + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_FSHUB, + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = UE_DIR_IN | OHCI_INTR_ENDPT, + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 32,/* max packet (255 ports) */ + .bInterval = 255, + }, +}; + +static const +struct usb2_hub_descriptor ohci_hubd = +{ + 0, /* dynamic length */ + UDESC_HUB, + 0, + {0, 0}, + 0, + 0, + {0}, +}; + +static void +ohci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_root_ctrl_start(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &ohci_root_ctrl_task, 0, 0); + + return; +} + +static void +ohci_root_ctrl_task(struct ohci_softc *sc, + struct ohci_config_copy *cc, uint16_t refcount) +{ + ohci_root_ctrl_poll(sc); + return; +} + +static void +ohci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ohci_softc_t *sc = xfer->usb2_sc; + char *ptr; + uint32_t port; + uint32_t v; + uint16_t value; + uint16_t index; + uint8_t l; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + ohci_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = sc->sc_hub_desc.temp; + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " + "wValue=0x%04x wIndex=0x%04x\n", + std->req.bmRequestType, std->req.bRequest, + UGETW(std->req.wLength), value, index); + +#define C(x,y) ((x) | ((y) << 8)) + switch (C(std->req.bRequest, std->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): + std->len = 1; + sc->sc_hub_desc.temp[0] = sc->sc_conf; + break; + case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): + switch (value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(ohci_devd); + sc->sc_hub_desc.devd = ohci_devd; + break; + + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(ohci_confd); + std->ptr = USB_ADD_BYTES(&ohci_confd, 0); + break; + + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + ptr = "\001"; + break; + + case 1: /* Vendor */ + ptr = sc->sc_vendor; + break; + + case 2: /* Product */ + ptr = "OHCI root HUB"; + break; + + default: + ptr = ""; + break; + } + + std->len = usb2_make_str_desc + (sc->sc_hub_desc.temp, + sizeof(sc->sc_hub_desc.temp), + ptr); + break; + + default: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + case C(UR_GET_INTERFACE, UT_READ_INTERFACE): + std->len = 1; + sc->sc_hub_desc.temp[0] = 0; + break; + case C(UR_GET_STATUS, UT_READ_DEVICE): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); + break; + case C(UR_GET_STATUS, UT_READ_INTERFACE): + case C(UR_GET_STATUS, UT_READ_ENDPOINT): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, 0); + break; + case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): + if (value >= USB_MAX_DEVICES) { + std->err = USB_ERR_IOERROR; + goto done; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if ((value != 0) && (value != 1)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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): + std->err = USB_ERR_IOERROR; + goto done; + 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(9, "UR_CLEAR_PORT_FEATURE " + "port=%d feature=%d\n", + index, value); + if ((index < 1) || + (index > sc->sc_noport)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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: + std->err = USB_ERR_IOERROR; + goto done; + } + 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_enable(sc); + mtx_lock(&sc->sc_bus.mtx); + } + break; + default: + break; + } + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); + + sc->sc_hub_desc.hubd = ohci_hubd; + sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; + USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, + (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : + v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) + /* XXX overcurrent */ + ); + sc->sc_hub_desc.hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); + v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); + + for (l = 0; l < sc->sc_noport; l++) { + if (v & 1) { + sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] |= (1 << (l % 8)); + } + v >>= 1; + } + sc->sc_hub_desc.hubd.bDescLength = + 8 + ((sc->sc_noport + 7) / 8); + std->len = sc->sc_hub_desc.hubd.bDescLength; + break; + + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + std->len = 16; + bzero(sc->sc_hub_desc.temp, 16); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + DPRINTFN(9, "get port status i=%d\n", + index); + if ((index < 1) || + (index > sc->sc_noport)) { + std->err = USB_ERR_IOERROR; + goto done; + } + v = OREAD4(sc, OHCI_RH_PORT_STATUS(index)); + DPRINTFN(9, "port status=0x%04x\n", v); + USETW(sc->sc_hub_desc.ps.wPortStatus, v); + USETW(sc->sc_hub_desc.ps.wPortChange, v >> 16); + std->len = sizeof(sc->sc_hub_desc.ps); + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + std->err = USB_ERR_IOERROR; + goto done; + 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)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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(6, "reset port %d\n", index); + OWRITE4(sc, port, UPS_RESET); + for (v = 0;; v++) { + if (v < 12) { + if (use_polling) { + /* polling */ + DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_PORT_ROOT_RESET_DELAY); + } + + if ((OREAD4(sc, port) & UPS_RESET) == 0) { + break; + } + } else { + std->err = USB_ERR_TIMEOUT; + goto done; + } + } + DPRINTFN(9, "ohci port %d reset, status = 0x%04x\n", + index, OREAD4(sc, port)); + break; + case UHF_PORT_POWER: + DPRINTFN(3, "set port power %d\n", index); + OWRITE4(sc, port, UPS_PORT_POWER); + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } +done: + return; +} + +static void +ohci_root_ctrl_poll(struct ohci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &ohci_root_ctrl_done); + return; +} + +struct usb2_pipe_methods ohci_root_ctrl_methods = +{ + .open = ohci_root_ctrl_open, + .close = ohci_root_ctrl_close, + .enter = ohci_root_ctrl_enter, + .start = ohci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * ohci root interrupt support + *------------------------------------------------------------------------*/ +static void +ohci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_root_intr_close(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + ohci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +ohci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_root_intr_start(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + return; +} + +struct usb2_pipe_methods ohci_root_intr_methods = +{ + .open = ohci_root_intr_open, + .close = ohci_root_intr_close, + .enter = ohci_root_intr_enter, + .start = ohci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +ohci_xfer_setup(struct usb2_setup_params *parm) +{ + struct usb2_page_search page_info; + struct usb2_page_cache *pc; + ohci_softc_t *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t nitd; + uint32_t nqh; + uint32_t n; + + sc = OHCI_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + parm->hc_max_packet_size = 0x500; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = OHCI_PAGE_SIZE; + + /* + * calculate ntd and nqh + */ + if (parm->methods == &ohci_device_ctrl_methods) { + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nitd = 0; + ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + nqh = 1; + + } else if (parm->methods == &ohci_device_bulk_methods) { + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nitd = 0; + ntd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + nqh = 1; + + } else if (parm->methods == &ohci_device_intr_methods) { + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nitd = 0; + ntd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_usb2_frame_size)); + nqh = 1; + + } else if (parm->methods == &ohci_device_isoc_methods) { + xfer->flags_int.bdma_enable = 1; + + usb2_transfer_setup_sub(parm); + + nitd = ((xfer->max_data_length / OHCI_PAGE_SIZE) + + ((xfer->nframes + OHCI_ITD_NOFFSET - 1) / OHCI_ITD_NOFFSET) + + 1 /* EXTRA */ ); + ntd = 0; + nqh = 1; + + } else { + + usb2_transfer_setup_sub(parm); + + nitd = 0; + ntd = 0; + nqh = 0; + } + +alloc_dma_set: + + if (parm->err) { + return; + } + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ohci_td_t), + OHCI_TD_ALIGN, ntd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != ntd; n++) { + ohci_td_t *td; + + usb2_get_page(pc + n, 0, &page_info); + + td = page_info.buffer; + + /* init TD */ + td->td_self = htole32(page_info.physaddr); + td->obj_next = last_obj; + td->page_cache = pc + n; + + last_obj = td; + + usb2_pc_cpu_flush(pc + n); + } + } + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ohci_itd_t), + OHCI_ITD_ALIGN, nitd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nitd; n++) { + ohci_itd_t *itd; + + usb2_get_page(pc + n, 0, &page_info); + + itd = page_info.buffer; + + /* init TD */ + itd->itd_self = htole32(page_info.physaddr); + itd->obj_next = last_obj; + itd->page_cache = pc + n; + + last_obj = itd; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; + + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(ohci_ed_t), + OHCI_ED_ALIGN, nqh)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nqh; n++) { + ohci_ed_t *ed; + + usb2_get_page(pc + n, 0, &page_info); + + ed = page_info.buffer; + + /* init QH */ + ed->ed_self = htole32(page_info.physaddr); + ed->obj_next = last_obj; + ed->page_cache = pc + n; + + last_obj = ed; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; + + if (!xfer->flags_int.curr_dma_set) { + xfer->flags_int.curr_dma_set = 1; + goto alloc_dma_set; + } + return; +} + +static void +ohci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_addr); + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + if (udev->device_index == sc->sc_addr) { + switch (edesc->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: + /* do nothing */ + break; + } + } else { + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &ohci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &ohci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + if (udev->speed == USB_SPEED_FULL) { + pipe->methods = &ohci_device_isoc_methods; + } + break; + case UE_BULK: + if (udev->speed != USB_SPEED_LOW) { + pipe->methods = &ohci_device_bulk_methods; + } + break; + default: + /* do nothing */ + break; + } + } + return; +} + +static void +ohci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +ohci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) +{ + /* + * Wait until hardware has finished any possible use of the + * transfer descriptor(s) and QH + */ + *pus = (1125); /* microseconds */ + return; +} + +struct usb2_bus_methods ohci_bus_methods = +{ + .pipe_init = ohci_pipe_init, + .xfer_setup = ohci_xfer_setup, + .xfer_unsetup = ohci_xfer_unsetup, + .do_poll = ohci_do_poll, + .get_dma_delay = ohci_get_dma_delay, +}; diff --git a/sys/dev/usb2/controller/ohci2.h b/sys/dev/usb2/controller/ohci2.h new file mode 100644 index 000000000000..fefde6cf54ca --- /dev/null +++ b/sys/dev/usb2/controller/ohci2.h @@ -0,0 +1,364 @@ +/* $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 _OHCI_H_ +#define _OHCI_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 /* HostControllerFunctionalStat + * e */ +#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) + +#define OHCI_NO_INTRS 32 +#define OHCI_HCCA_SIZE 256 + +/* Structures alignment (bytes) */ +#define OHCI_HCCA_ALIGN 256 +#define OHCI_ED_ALIGN 16 +#define OHCI_TD_ALIGN 16 +#define OHCI_ITD_ALIGN 32 + +#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) + +#if ((USB_PAGE_SIZE < OHCI_ED_ALIGN) || (OHCI_ED_ALIGN == 0) || \ + (USB_PAGE_SIZE < OHCI_TD_ALIGN) || (OHCI_TD_ALIGN == 0) || \ + (USB_PAGE_SIZE < OHCI_ITD_ALIGN) || (OHCI_ITD_ALIGN == 0) || \ + (USB_PAGE_SIZE < OHCI_PAGE_SIZE) || (OHCI_PAGE_SIZE == 0)) +#error "Invalid USB page size!" +#endif + +#define OHCI_VIRTUAL_FRAMELIST_COUNT 128/* dummy */ + +#if (OHCI_VIRTUAL_FRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER) +#error "maximum number of full-speed isochronous frames is higher than supported!" +#endif + +struct ohci_hcca { + volatile uint32_t hcca_interrupt_table[OHCI_NO_INTRS]; + volatile uint32_t hcca_frame_number; + volatile uint32_t hcca_done_head; +#define OHCI_DONE_INTRS 1 +} __aligned(OHCI_HCCA_ALIGN); + +typedef struct ohci_hcca ohci_hcca_t; + +struct ohci_ed { + volatile uint32_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) + volatile uint32_t ed_tailp; + volatile uint32_t ed_headp; +#define OHCI_HALTED 0x00000001 +#define OHCI_TOGGLECARRY 0x00000002 +#define OHCI_HEADMASK 0xfffffffc + volatile uint32_t ed_next; +/* + * Extra information needed: + */ + struct ohci_ed *next; + struct ohci_ed *prev; + struct ohci_ed *obj_next; + struct usb2_page_cache *page_cache; + uint32_t ed_self; +} __aligned(OHCI_ED_ALIGN); + +typedef struct ohci_ed ohci_ed_t; + +struct ohci_td { + volatile uint32_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_SET_CC(x) ((x) << 28) +#define OHCI_TD_NOCC 0xf0000000 + volatile uint32_t td_cbp; /* Current Buffer Pointer */ + volatile uint32_t td_next; /* Next TD */ +#define OHCI_TD_NEXT_END 0 + volatile uint32_t td_be; /* Buffer End */ +/* + * Extra information needed: + */ + struct ohci_td *obj_next; + struct ohci_td *alt_next; + struct usb2_page_cache *page_cache; + uint32_t td_self; + uint16_t len; +} __aligned(OHCI_TD_ALIGN); + +typedef struct ohci_td ohci_td_t; + +struct ohci_itd { + volatile uint32_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 +#define OHCI_ITD_NOFFSET 8 + volatile uint32_t itd_bp0; /* Buffer Page 0 */ + volatile uint32_t itd_next; /* Next ITD */ + volatile uint32_t itd_be; /* Buffer End */ + volatile uint16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets and + * Status */ +#define OHCI_ITD_PAGE_SELECT 0x00001000 +#define OHCI_ITD_MK_OFFS(len) (0xe000 | ((len) & 0x1fff)) +#define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */ +#define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */ +/* + * Extra information needed: + */ + struct ohci_itd *obj_next; + struct usb2_page_cache *page_cache; + uint32_t itd_self; + uint8_t frames; +} __aligned(OHCI_ITD_ALIGN); + +typedef struct ohci_itd ohci_itd_t; + +#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 + +#define OHCI_NO_EDS (2*OHCI_NO_INTRS) + +struct ohci_hw_softc { + struct usb2_page_cache hcca_pc; + struct usb2_page_cache ctrl_start_pc; + struct usb2_page_cache bulk_start_pc; + struct usb2_page_cache isoc_start_pc; + struct usb2_page_cache intr_start_pc[OHCI_NO_EDS]; + + struct usb2_page hcca_pg; + struct usb2_page ctrl_start_pg; + struct usb2_page bulk_start_pg; + struct usb2_page isoc_start_pg; + struct usb2_page intr_start_pg[OHCI_NO_EDS]; +}; + +struct ohci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union ohci_hub_desc { + struct usb2_status stat; + struct usb2_port_status ps; + struct usb2_device_descriptor devd; + struct usb2_hub_descriptor hubd; + uint8_t temp[128]; +}; + +typedef struct ohci_softc { + struct ohci_hw_softc sc_hw; + struct usb2_bus sc_bus; /* base device */ + struct usb2_config_td sc_config_td; + struct usb2_callout sc_tmo_rhsc; + union ohci_hub_desc sc_hub_desc; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + + struct resource *sc_io_res; + struct resource *sc_irq_res; + struct ohci_hcca *sc_hcca_p; + struct ohci_ed *sc_ctrl_p_last; + struct ohci_ed *sc_bulk_p_last; + struct ohci_ed *sc_isoc_p_last; + struct ohci_ed *sc_intr_p_last[OHCI_NO_EDS]; + void *sc_intr_hdl; + device_t sc_dev; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + uint32_t sc_eintrs; /* enabled interrupts */ + uint32_t sc_control; /* Preserved during suspend/standby */ + uint32_t sc_intre; + + uint16_t sc_intr_stat[OHCI_NO_EDS]; + uint16_t sc_id_vendor; + + uint8_t sc_noport; + uint8_t sc_addr; /* device address */ + uint8_t sc_conf; /* device configuration */ + uint8_t sc_hub_idata[32]; + + char sc_vendor[16]; + +} ohci_softc_t; + +usb2_bus_mem_cb_t ohci_iterate_hw_softc; + +usb2_error_t ohci_init(ohci_softc_t *sc); +void ohci_detach(struct ohci_softc *sc); +void ohci_suspend(ohci_softc_t *sc); +void ohci_resume(ohci_softc_t *sc); +void ohci_interrupt(ohci_softc_t *sc); + +#endif /* _OHCI_H_ */ diff --git a/sys/dev/usb2/controller/ohci2_atmelarm.c b/sys/dev/usb2/controller/ohci2_atmelarm.c new file mode 100644 index 000000000000..83325bef7b15 --- /dev/null +++ b/sys/dev/usb2/controller/ohci2_atmelarm.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2006 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 ``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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define MEM_RID 0 + +static device_probe_t ohci_atmelarm_probe; +static device_attach_t ohci_atmelarm_attach; +static device_detach_t ohci_atmelarm_detach; + +struct at91_ohci_softc { + struct ohci_softc sc_ohci; /* must be first */ + struct at91_pmc_clock *iclk; + struct at91_pmc_clock *fclk; +}; + +static int +ohci_atmelarm_probe(device_t dev) +{ + device_set_desc(dev, "AT91 integrated OHCI controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +ohci_atmelarm_attach(device_t dev) +{ + struct at91_ohci_softc *sc = device_get_softc(dev); + int err; + int rid; + + if (sc == NULL) { + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, + USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { + return ENOMEM; + } + sc->iclk = at91_pmc_clock_ref("ohci_clk"); + sc->fclk = at91_pmc_clock_ref("uhpck"); + + sc->sc_ohci.sc_dev = dev; + + rid = MEM_RID; + sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + + if (!(sc->sc_ohci.sc_io_res)) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); + + rid = 0; + sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!(sc->sc_ohci.sc_irq_res)) { + goto error; + } + sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_ohci.sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); + + strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor)); + + err = usb2_config_td_setup(&sc->sc_ohci.sc_config_td, sc, + &sc->sc_ohci.sc_bus.mtx, NULL, 0, 4); + if (err) { + device_printf(dev, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); +#endif + if (err) { + sc->sc_ohci.sc_intr_hdl = NULL; + goto error; + } + /* + * turn on the clocks from the AT91's point of view. Keep the unit in reset. + */ + at91_pmc_clock_enable(sc->iclk); + at91_pmc_clock_enable(sc->fclk); + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, + OHCI_CONTROL, 0); + + err = ohci_init(&sc->sc_ohci); + if (!err) { + err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); + } + if (err) { + goto error; + } + return (0); + +error: + ohci_atmelarm_detach(dev); + return (ENXIO); +} + +static int +ohci_atmelarm_detach(device_t dev) +{ + struct at91_ohci_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_ohci.sc_bus.bdev) { + bdev = sc->sc_ohci.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + /* + * Put the controller into reset, then disable clocks and do + * the MI tear down. We have to disable the clocks/hardware + * after we do the rest of the teardown. We also disable the + * clocks in the opposite order we acquire them, but that + * doesn't seem to be absolutely necessary. We free up the + * clocks after we disable them, so the system could, in + * theory, reuse them. + */ + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, + OHCI_CONTROL, 0); + + at91_pmc_clock_disable(sc->fclk); + at91_pmc_clock_disable(sc->iclk); + at91_pmc_clock_deref(sc->fclk); + at91_pmc_clock_deref(sc->iclk); + + if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { + /* + * only call ohci_detach() after ohci_init() + */ + ohci_detach(&sc->sc_ohci); + + err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); + sc->sc_ohci.sc_intr_hdl = NULL; + } + if (sc->sc_ohci.sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); + sc->sc_ohci.sc_irq_res = NULL; + } + if (sc->sc_ohci.sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, + sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_ohci.sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); + + return (0); +} + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ohci_atmelarm_probe), + DEVMETHOD(device_attach, ohci_atmelarm_attach), + DEVMETHOD(device_detach, ohci_atmelarm_detach), + 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(struct at91_ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, atmelarm, ohci_driver, ohci_devclass, 0, 0); +MODULE_DEPEND(ohci, usb2_controller, 1, 1, 1); +MODULE_DEPEND(ohci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/ohci2_pci.c b/sys/dev/usb2/controller/ohci2_pci.c new file mode 100644 index 000000000000..60ae0b6f4f59 --- /dev/null +++ b/sys/dev/usb2/controller/ohci2_pci.c @@ -0,0 +1,392 @@ +/*- + * 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 +__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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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_BASE_REG 0x10 + +static device_probe_t ohci_pci_probe; +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_suspend(sc); + return (0); +} + +static int +ohci_pci_resume(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + uint32_t reg, int_line; + + if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) { + device_printf(self, "chip is in D%d mode " + "-- setting to D0\n", pci_get_powerstate(self)); + reg = pci_read_config(self, PCI_CBMEM, 4); + int_line = pci_read_config(self, PCIR_INTLINE, 4); + pci_set_powerstate(self, PCI_POWERSTATE_D0); + pci_write_config(self, PCI_CBMEM, reg, 4); + pci_write_config(self, PCIR_INTLINE, int_line, 4); + } + ohci_resume(sc); + + bus_generic_resume(self); + return (0); +} + +static const char * +ohci_pci_match(device_t self) +{ + uint32_t device_id = pci_get_devid(self); + + switch (device_id) { + case 0x523710b9: + return ("AcerLabs M5237 (Aladdin-V) USB controller"); + + case 0x740c1022: + return ("AMD-756 USB Controller"); + + case 0x74141022: + return ("AMD-766 USB Controller"); + + case 0x43741002: + return "ATI SB400 USB Controller"; + case 0x43751002: + return "ATI SB400 USB Controller"; + + case 0x06701095: + return ("CMD Tech 670 (USB0670) USB controller"); + + case 0x06731095: + return ("CMD Tech 673 (USB0673) USB controller"); + + case 0xc8611045: + return ("OPTi 82C861 (FireLink) USB controller"); + + case 0x00351033: + return ("NEC uPD 9210 USB controller"); + + case 0x00d710de: + return ("nVidia nForce3 USB Controller"); + + case 0x70011039: + return ("SiS 5571 USB controller"); + + case 0x1103108e: + return "Sun PCIO-2 USB controller"; + + case 0x0019106b: + return ("Apple KeyLargo USB controller"); + + default: + break; + } + if ((pci_get_class(self) == PCIC_SERIALBUS) && + (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && + (pci_get_progif(self) == PCI_INTERFACE_OHCI)) { + return ("OHCI (generic) USB controller"); + } + return (NULL); +} + +static int +ohci_pci_probe(device_t self) +{ + const char *desc = ohci_pci_match(self); + + if (desc) { + device_set_desc(self, desc); + return (0); + } else { + return (ENXIO); + } +} + +static int +ohci_pci_attach(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + int rid; + int err; + + if (sc == NULL) { + device_printf(self, "Could not allocate sc\n"); + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), + &ohci_iterate_hw_softc)) { + return ENOMEM; + } + sc->sc_dev = self; + + pci_enable_busmaster(self); + + rid = PCI_CBMEM; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + 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; + case PCI_OHCI_VENDORID_SUN: + sprintf(sc->sc_vendor, "SUN"); + 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 = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.mtx, + NULL, 0, 4); + if (err) { + device_printf(self, "could not setup config thread!\n"); + goto error; + } + /* sc->sc_bus.usbrev; set by ohci_init() */ + +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl); +#endif + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + err = ohci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed\n"); + goto error; + } + return (0); + +error: + ohci_pci_detach(self); + return (ENXIO); +} + +static int +ohci_pci_detach(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + device_t bdev; + + usb2_config_td_drain(&sc->sc_config_td); + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(self); + + pci_disable_busmaster(self); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ohci_detach() after ohci_init() + */ + ohci_detach(sc); + + int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) { + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + } + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); + + return (0); +} + +static driver_t ohci_driver = +{ + .name = "ohci", + .methods = (device_method_t[]){ + /* 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} + }, + .size = sizeof(struct ohci_softc), +}; + +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, usb2_controller, 1, 1, 1); +MODULE_DEPEND(ohci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/uhci2.c b/sys/dev/usb2/controller/uhci2.c new file mode 100644 index 000000000000..9a938d9cbdb1 --- /dev/null +++ b/sys/dev/usb2/controller/uhci2.c @@ -0,0 +1,3256 @@ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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 +__FBSDID("$FreeBSD$"); + +/* + * 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 +#include +#include +#include + +#define USB_DEBUG_VAR uhcidebug +#define usb2_config_td_cc uhci_config_copy +#define usb2_config_td_softc uhci_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define alt_next next +#define UHCI_BUS2SC(bus) ((uhci_softc_t *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((uhci_softc_t *)0)->sc_bus)))) + +#if USB_DEBUG +static int uhcidebug = 0; +static int uhcinoloop = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci"); +SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, debug, CTLFLAG_RW, + &uhcidebug, 0, "uhci debug level"); +SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, loop, CTLFLAG_RW, + &uhcinoloop, 0, "uhci noloop"); +static void uhci_dumpregs(uhci_softc_t *sc); +static void uhci_dump_tds(uhci_td_t *td); + +#endif + +#define UBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ + BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) +#define UWRITE1(sc, r, x) \ + do { UBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ + } while (/*CONSTCOND*/0) +#define UWRITE2(sc, r, x) \ + do { UBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ + } while (/*CONSTCOND*/0) +#define UWRITE4(sc, r, x) \ + do { UBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ + } while (/*CONSTCOND*/0) +#define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) +#define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) +#define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (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_INTR_ENDPT 1 + +struct uhci_mem_layout { + + struct usb2_page_search buf_res; + struct usb2_page_search fix_res; + + struct usb2_page_cache *buf_pc; + struct usb2_page_cache *fix_pc; + + uint32_t buf_offset; + + uint16_t max_frame_size; +}; + +struct uhci_std_temp { + + struct uhci_mem_layout ml; + uhci_td_t *td; + uhci_td_t *td_next; + uint32_t average; + uint32_t td_status; + uint32_t td_token; + uint32_t len; + uint16_t max_frame_size; + uint8_t shortpkt; + uint8_t setup_alt_next; + uint8_t short_frames_ok; +}; + +extern struct usb2_bus_methods uhci_bus_methods; +extern struct usb2_pipe_methods uhci_device_bulk_methods; +extern struct usb2_pipe_methods uhci_device_ctrl_methods; +extern struct usb2_pipe_methods uhci_device_intr_methods; +extern struct usb2_pipe_methods uhci_device_isoc_methods; +extern struct usb2_pipe_methods uhci_root_ctrl_methods; +extern struct usb2_pipe_methods uhci_root_intr_methods; + +static usb2_config_td_command_t uhci_root_ctrl_task; +static void uhci_root_ctrl_poll(struct uhci_softc *sc); +static void uhci_do_poll(struct usb2_bus *bus); +static void uhci_device_done(struct usb2_xfer *xfer, usb2_error_t error); +static void uhci_transfer_intr_enqueue(struct usb2_xfer *xfer); +static void uhci_root_intr_check(void *arg); +static void uhci_timeout(void *arg); +static uint8_t uhci_check_transfer(struct usb2_xfer *xfer); + +void +uhci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) +{ + struct uhci_softc *sc = UHCI_BUS2SC(bus); + uint32_t i; + + cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, + sizeof(uint32_t) * UHCI_FRAMELIST_COUNT, UHCI_FRAMELIST_ALIGN); + + cb(bus, &sc->sc_hw.ls_ctl_start_pc, &sc->sc_hw.ls_ctl_start_pg, + sizeof(uhci_qh_t), UHCI_QH_ALIGN); + + cb(bus, &sc->sc_hw.fs_ctl_start_pc, &sc->sc_hw.fs_ctl_start_pg, + sizeof(uhci_qh_t), UHCI_QH_ALIGN); + + cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, + sizeof(uhci_qh_t), UHCI_QH_ALIGN); + + cb(bus, &sc->sc_hw.last_qh_pc, &sc->sc_hw.last_qh_pg, + sizeof(uhci_qh_t), UHCI_QH_ALIGN); + + cb(bus, &sc->sc_hw.last_td_pc, &sc->sc_hw.last_td_pg, + sizeof(uhci_td_t), UHCI_TD_ALIGN); + + for (i = 0; i != UHCI_VFRAMELIST_COUNT; i++) { + cb(bus, sc->sc_hw.isoc_start_pc + i, + sc->sc_hw.isoc_start_pg + i, + sizeof(uhci_td_t), UHCI_TD_ALIGN); + } + + for (i = 0; i != UHCI_IFRAMELIST_COUNT; i++) { + cb(bus, sc->sc_hw.intr_start_pc + i, + sc->sc_hw.intr_start_pg + i, + sizeof(uhci_qh_t), UHCI_QH_ALIGN); + } + return; +} + +static void +uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usb2_xfer *xfer) +{ + ml->buf_pc = xfer->frbuffers + 0; + ml->fix_pc = xfer->buf_fixup; + + ml->buf_offset = 0; + + ml->max_frame_size = xfer->max_frame_size; + + return; +} + +static void +uhci_mem_layout_fixup(struct uhci_mem_layout *ml, struct uhci_td *td) +{ + usb2_get_page(ml->buf_pc, ml->buf_offset, &ml->buf_res); + + if (ml->buf_res.length < td->len) { + + /* need to do a fixup */ + + usb2_get_page(ml->fix_pc, 0, &ml->fix_res); + + td->td_buffer = htole32(ml->fix_res.physaddr); + + /* + * The UHCI driver cannot handle + * page crossings, so a fixup is + * needed: + * + * +----+----+ - - - + * | YYY|Y | + * +----+----+ - - - + * \ \ + * \ \ + * +----+ + * |YYYY| (fixup) + * +----+ + */ + + if ((td->td_token & htole32(UHCI_TD_PID)) == + htole32(UHCI_TD_PID_IN)) { + td->fix_pc = ml->fix_pc; + usb2_pc_cpu_invalidate(ml->fix_pc); + + } else { + td->fix_pc = NULL; + + /* copy data to fixup location */ + + usb2_copy_out(ml->buf_pc, ml->buf_offset, + ml->fix_res.buffer, td->len); + + usb2_pc_cpu_flush(ml->fix_pc); + } + + /* prepare next fixup */ + + ml->fix_pc++; + + } else { + + td->td_buffer = htole32(ml->buf_res.physaddr); + td->fix_pc = NULL; + } + + /* prepare next data location */ + + ml->buf_offset += td->len; + + return; +} + +void +uhci_reset(uhci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint16_t n; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + DPRINTF("resetting the HC\n"); + + /* disable interrupts */ + + UWRITE2(sc, UHCI_INTR, 0); + + /* global reset */ + + UHCICMD(sc, UHCI_CMD_GRESET); + + /* wait */ + + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_BUS_RESET_DELAY); + + /* terminate all transfers */ + + UHCICMD(sc, UHCI_CMD_HCRESET); + + /* the reset bit goes low when the controller is done */ + + n = UHCI_RESET_TIMEOUT; + while (n--) { + /* wait one millisecond */ + + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + + if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET)) { + goto done_1; + } + } + + device_printf(sc->sc_bus.bdev, + "controller did not reset\n"); + +done_1: + + n = 10; + while (n--) { + /* wait one millisecond */ + + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + + /* check if HC is stopped */ + if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) { + goto done_2; + } + } + + device_printf(sc->sc_bus.bdev, + "controller did not stop\n"); + +done_2: + + /* reload the configuration */ + usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); + UWRITE4(sc, UHCI_FLBASEADDR, buf_res.physaddr); + UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum); + UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof); + return; +} + +static void +uhci_start(uhci_softc_t *sc) +{ + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + DPRINTFN(2, "enabling\n"); + + /* enable interrupts */ + + UWRITE2(sc, UHCI_INTR, + (UHCI_INTR_TOCRCIE | + UHCI_INTR_RIE | + UHCI_INTR_IOCE | + UHCI_INTR_SPIE)); + + /* + * assume 64 byte packets at frame end and start HC controller + */ + + UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); + + uint8_t n = 10; + + while (n--) { + /* wait one millisecond */ + + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + + /* check that controller has started */ + + if (!(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH)) { + goto done; + } + } + + device_printf(sc->sc_bus.bdev, + "cannot start HC controller\n"); + +done: + return; +} + +static struct uhci_qh * +uhci_init_qh(struct usb2_page_cache *pc) +{ + struct usb2_page_search buf_res; + struct uhci_qh *qh; + + usb2_get_page(pc, 0, &buf_res); + + qh = buf_res.buffer; + + qh->qh_self = + htole32(buf_res.physaddr) | + htole32(UHCI_PTR_QH); + + qh->page_cache = pc; + + return (qh); +} + +static struct uhci_td * +uhci_init_td(struct usb2_page_cache *pc) +{ + struct usb2_page_search buf_res; + struct uhci_td *td; + + usb2_get_page(pc, 0, &buf_res); + + td = buf_res.buffer; + + td->td_self = + htole32(buf_res.physaddr) | + htole32(UHCI_PTR_TD); + + td->page_cache = pc; + + return (td); +} + +usb2_error_t +uhci_init(uhci_softc_t *sc) +{ + uint16_t bit; + uint16_t x; + uint16_t y; + + mtx_lock(&sc->sc_bus.mtx); + + DPRINTF("start\n"); + +#if USB_DEBUG + if (uhcidebug > 2) { + uhci_dumpregs(sc); + } +#endif + + sc->sc_saved_sof = 0x40; /* default value */ + sc->sc_saved_frnum = 0; /* default frame number */ + + /* + * Setup QH's + */ + sc->sc_ls_ctl_p_last = + uhci_init_qh(&sc->sc_hw.ls_ctl_start_pc); + + sc->sc_fs_ctl_p_last = + uhci_init_qh(&sc->sc_hw.fs_ctl_start_pc); + + sc->sc_bulk_p_last = + uhci_init_qh(&sc->sc_hw.bulk_start_pc); +#if 0 + sc->sc_reclaim_qh_p = + sc->sc_fs_ctl_p_last; +#else + /* setup reclaim looping point */ + sc->sc_reclaim_qh_p = + sc->sc_bulk_p_last; +#endif + + sc->sc_last_qh_p = + uhci_init_qh(&sc->sc_hw.last_qh_pc); + + sc->sc_last_td_p = + uhci_init_td(&sc->sc_hw.last_td_pc); + + for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { + sc->sc_isoc_p_last[x] = + uhci_init_td(sc->sc_hw.isoc_start_pc + x); + } + + for (x = 0; x != UHCI_IFRAMELIST_COUNT; x++) { + sc->sc_intr_p_last[x] = + uhci_init_qh(sc->sc_hw.intr_start_pc + x); + } + + /* + * the QHs are arranged to give poll intervals that are + * powers of 2 times 1ms + */ + bit = UHCI_IFRAMELIST_COUNT / 2; + while (bit) { + x = bit; + while (x & bit) { + uhci_qh_t *qh_x; + uhci_qh_t *qh_y; + + y = (x ^ bit) | (bit / 2); + + /* + * the next QH has half the poll interval + */ + qh_x = sc->sc_intr_p_last[x]; + qh_y = sc->sc_intr_p_last[y]; + + qh_x->h_next = NULL; + qh_x->qh_h_next = qh_y->qh_self; + qh_x->e_next = NULL; + qh_x->qh_e_next = htole32(UHCI_PTR_T); + x++; + } + bit >>= 1; + } + + if (1) { + uhci_qh_t *qh_ls; + uhci_qh_t *qh_intr; + + qh_ls = sc->sc_ls_ctl_p_last; + qh_intr = sc->sc_intr_p_last[0]; + + /* start QH for interrupt traffic */ + qh_intr->h_next = qh_ls; + qh_intr->qh_h_next = qh_ls->qh_self; + qh_intr->e_next = 0; + qh_intr->qh_e_next = htole32(UHCI_PTR_T); + } + for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { + + uhci_td_t *td_x; + uhci_qh_t *qh_intr; + + td_x = sc->sc_isoc_p_last[x]; + qh_intr = sc->sc_intr_p_last[x | (UHCI_IFRAMELIST_COUNT / 2)]; + + /* start TD for isochronous traffic */ + td_x->next = NULL; + td_x->td_next = qh_intr->qh_self; + td_x->td_status = htole32(UHCI_TD_IOS); + td_x->td_token = htole32(0); + td_x->td_buffer = htole32(0); + } + + if (1) { + uhci_qh_t *qh_ls; + uhci_qh_t *qh_fs; + + qh_ls = sc->sc_ls_ctl_p_last; + qh_fs = sc->sc_fs_ctl_p_last; + + /* start QH where low speed control traffic will be queued */ + qh_ls->h_next = qh_fs; + qh_ls->qh_h_next = qh_fs->qh_self; + qh_ls->e_next = 0; + qh_ls->qh_e_next = htole32(UHCI_PTR_T); + } + if (1) { + uhci_qh_t *qh_ctl; + uhci_qh_t *qh_blk; + uhci_qh_t *qh_lst; + uhci_td_t *td_lst; + + qh_ctl = sc->sc_fs_ctl_p_last; + qh_blk = sc->sc_bulk_p_last; + + /* start QH where full speed control traffic will be queued */ + qh_ctl->h_next = qh_blk; + qh_ctl->qh_h_next = qh_blk->qh_self; + qh_ctl->e_next = 0; + qh_ctl->qh_e_next = htole32(UHCI_PTR_T); + + qh_lst = sc->sc_last_qh_p; + + /* start QH where bulk traffic will be queued */ + qh_blk->h_next = qh_lst; + qh_blk->qh_h_next = qh_lst->qh_self; + qh_blk->e_next = 0; + qh_blk->qh_e_next = htole32(UHCI_PTR_T); + + td_lst = sc->sc_last_td_p; + + /* end QH which is used for looping the QHs */ + qh_lst->h_next = 0; + qh_lst->qh_h_next = htole32(UHCI_PTR_T); /* end of QH chain */ + qh_lst->e_next = td_lst; + qh_lst->qh_e_next = td_lst->td_self; + + /* + * end TD which hangs from the last QH, to avoid a bug in the PIIX + * that makes it run berserk otherwise + */ + td_lst->next = 0; + td_lst->td_next = htole32(UHCI_PTR_T); + td_lst->td_status = htole32(0); /* inactive */ + td_lst->td_token = htole32(0); + td_lst->td_buffer = htole32(0); + } + if (1) { + struct usb2_page_search buf_res; + uint32_t *pframes; + + usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); + + pframes = buf_res.buffer; + + + /* + * Setup UHCI framelist + * + * Execution order: + * + * pframes -> full speed isochronous -> interrupt QH's -> low + * speed control -> full speed control -> bulk transfers + * + */ + + for (x = 0; x != UHCI_FRAMELIST_COUNT; x++) { + pframes[x] = + sc->sc_isoc_p_last[x % UHCI_VFRAMELIST_COUNT]->td_self; + } + } + /* flush all cache into memory */ + + usb2_bus_mem_flush_all(&sc->sc_bus, &uhci_iterate_hw_softc); + + /* set up the bus struct */ + sc->sc_bus.methods = &uhci_bus_methods; + + /* reset the controller */ + uhci_reset(sc); + + /* start the controller */ + uhci_start(sc); + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch lost interrupts */ + uhci_do_poll(&sc->sc_bus); + + return (0); +} + +/* NOTE: suspend/resume is called from + * interrupt context and cannot sleep! + */ + +void +uhci_suspend(uhci_softc_t *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + +#if USB_DEBUG + if (uhcidebug > 2) { + uhci_dumpregs(sc); + } +#endif + /* save some state if BIOS doesn't */ + + sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM); + sc->sc_saved_sof = UREAD1(sc, UHCI_SOF); + + /* stop the controller */ + + uhci_reset(sc); + + /* enter global suspend */ + + UHCICMD(sc, UHCI_CMD_EGSM); + + usb2_pause_mtx(&sc->sc_bus.mtx, USB_RESUME_WAIT); + + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +void +uhci_resume(uhci_softc_t *sc) +{ + mtx_lock(&sc->sc_bus.mtx); + + /* reset the controller */ + + uhci_reset(sc); + + /* force global resume */ + + UHCICMD(sc, UHCI_CMD_FGR); + + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_RESUME_DELAY); + + /* and start traffic again */ + + uhci_start(sc); + +#if USB_DEBUG + if (uhcidebug > 2) { + uhci_dumpregs(sc); + } +#endif + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch lost interrupts */ + uhci_do_poll(&sc->sc_bus); + + return; +} + +#if USB_DEBUG +static void +uhci_dumpregs(uhci_softc_t *sc) +{ + DPRINTFN(0, "%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)); + return; +} + +static uint8_t +uhci_dump_td(uhci_td_t *p) +{ + uint32_t td_next; + uint32_t td_status; + uint32_t td_token; + uint8_t temp; + + usb2_pc_cpu_invalidate(p->page_cache); + + td_next = le32toh(p->td_next); + td_status = le32toh(p->td_status); + td_token = le32toh(p->td_token); + + /* + * Check whether the link pointer in this TD marks the link pointer + * as end of queue: + */ + temp = ((td_next & UHCI_PTR_T) || (td_next == 0)); + + printf("TD(%p) at 0x%08x = link=0x%08x status=0x%08x " + "token=0x%08x buffer=0x%08x\n", + p, + le32toh(p->td_self), + td_next, + td_status, + td_token, + le32toh(p->td_buffer)); + + printf("TD(%p) td_next=%s%s%s td_status=%s%s%s%s%s%s%s%s%s%s%s, errcnt=%d, actlen=%d pid=%02x," + "addr=%d,endpt=%d,D=%d,maxlen=%d\n", + p, + (td_next & 1) ? "-T" : "", + (td_next & 2) ? "-Q" : "", + (td_next & 4) ? "-VF" : "", + (td_status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "", + (td_status & UHCI_TD_CRCTO) ? "-CRCTO" : "", + (td_status & UHCI_TD_NAK) ? "-NAK" : "", + (td_status & UHCI_TD_BABBLE) ? "-BABBLE" : "", + (td_status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "", + (td_status & UHCI_TD_STALLED) ? "-STALLED" : "", + (td_status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "", + (td_status & UHCI_TD_IOC) ? "-IOC" : "", + (td_status & UHCI_TD_IOS) ? "-IOS" : "", + (td_status & UHCI_TD_LS) ? "-LS" : "", + (td_status & UHCI_TD_SPD) ? "-SPD" : "", + UHCI_TD_GET_ERRCNT(td_status), + UHCI_TD_GET_ACTLEN(td_status), + UHCI_TD_GET_PID(td_token), + UHCI_TD_GET_DEVADDR(td_token), + UHCI_TD_GET_ENDPT(td_token), + UHCI_TD_GET_DT(td_token), + UHCI_TD_GET_MAXLEN(td_token)); + + return (temp); +} + +static uint8_t +uhci_dump_qh(uhci_qh_t *sqh) +{ + uint8_t temp; + uint32_t qh_h_next; + uint32_t qh_e_next; + + usb2_pc_cpu_invalidate(sqh->page_cache); + + qh_h_next = le32toh(sqh->qh_h_next); + qh_e_next = le32toh(sqh->qh_e_next); + + DPRINTFN(0, "QH(%p) at 0x%08x: h_next=0x%08x e_next=0x%08x\n", sqh, + le32toh(sqh->qh_self), qh_h_next, qh_e_next); + + temp = ((((sqh->h_next != NULL) && !(qh_h_next & UHCI_PTR_T)) ? 1 : 0) | + (((sqh->e_next != NULL) && !(qh_e_next & UHCI_PTR_T)) ? 2 : 0)); + + return (temp); +} + +static void +uhci_dump_all(uhci_softc_t *sc) +{ + uhci_dumpregs(sc); + uhci_dump_qh(sc->sc_ls_ctl_p_last); + uhci_dump_qh(sc->sc_fs_ctl_p_last); + uhci_dump_qh(sc->sc_bulk_p_last); + uhci_dump_qh(sc->sc_last_qh_p); + return; +} + +static void +uhci_dump_qhs(uhci_qh_t *sqh) +{ + uint8_t temp; + + temp = 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 (temp & 1) + uhci_dump_qhs(sqh->h_next); + else + DPRINTF("No QH\n"); + + if (temp & 2) + uhci_dump_tds(sqh->e_next); + else + DPRINTF("No TD\n"); + + return; +} + +static void +uhci_dump_tds(uhci_td_t *td) +{ + for (; + td != NULL; + td = td->obj_next) { + if (uhci_dump_td(td)) { + break; + } + } + return; +} + +#endif + +/* + * Let the last QH loop back to the full 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. + */ +static void +uhci_add_loop(uhci_softc_t *sc) +{ + struct uhci_qh *qh_lst; + struct uhci_qh *qh_rec; + +#if USB_DEBUG + if (uhcinoloop) { + return; + } +#endif + if (++(sc->sc_loops) == 1) { + DPRINTFN(6, "add\n"); + + qh_lst = sc->sc_last_qh_p; + qh_rec = sc->sc_reclaim_qh_p; + + /* NOTE: we don't loop back the soft pointer */ + + qh_lst->qh_h_next = qh_rec->qh_self; + usb2_pc_cpu_flush(qh_lst->page_cache); + } + return; +} + +static void +uhci_rem_loop(uhci_softc_t *sc) +{ + struct uhci_qh *qh_lst; + +#if USB_DEBUG + if (uhcinoloop) { + return; + } +#endif + if (--(sc->sc_loops) == 0) { + DPRINTFN(6, "remove\n"); + + qh_lst = sc->sc_last_qh_p; + qh_lst->qh_h_next = htole32(UHCI_PTR_T); + usb2_pc_cpu_flush(qh_lst->page_cache); + } + return; +} + +static void +uhci_transfer_intr_enqueue(struct usb2_xfer *xfer) +{ + /* check for early completion */ + if (uhci_check_transfer(xfer)) { + return; + } + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout); + } + return; +} + +#define UHCI_APPEND_TD(std,last) (last) = _uhci_append_td(std,last) +static uhci_td_t * +_uhci_append_td(uhci_td_t *std, uhci_td_t *last) +{ + DPRINTFN(11, "%p to %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->next = last->next; + std->td_next = last->td_next; + + std->prev = last; + + usb2_pc_cpu_flush(std->page_cache); + + /* + * the last->next->prev is never followed: std->next->prev = std; + */ + last->next = std; + last->td_next = std->td_self; + + usb2_pc_cpu_flush(last->page_cache); + + return (std); +} + +#define UHCI_APPEND_QH(sqh,td,last) (last) = _uhci_append_qh(sqh,td,last) +static uhci_qh_t * +_uhci_append_qh(uhci_qh_t *sqh, uhci_td_t *td, uhci_qh_t *last) +{ + DPRINTFN(11, "%p to %p\n", sqh, last); + + /* (sc->sc_bus.mtx) must be locked */ + + sqh->e_next = td; + sqh->qh_e_next = td->td_self; + + sqh->h_next = last->h_next; + sqh->qh_h_next = last->qh_h_next; + + sqh->h_prev = last; + + usb2_pc_cpu_flush(sqh->page_cache); + + /* + * The "last->h_next->h_prev" is never followed: + * + * "sqh->h_next->h_prev" = sqh; + */ + + last->h_next = sqh; + last->qh_h_next = sqh->qh_self; + + usb2_pc_cpu_flush(last->page_cache); + + return (sqh); +} + +/**/ + +#define UHCI_REMOVE_TD(std,last) (last) = _uhci_remove_td(std,last) +static uhci_td_t * +_uhci_remove_td(uhci_td_t *std, uhci_td_t *last) +{ + DPRINTFN(11, "%p from %p\n", std, last); + + /* (sc->sc_bus.mtx) must be locked */ + + std->prev->next = std->next; + std->prev->td_next = std->td_next; + + usb2_pc_cpu_flush(std->prev->page_cache); + + if (std->next) { + std->next->prev = std->prev; + usb2_pc_cpu_flush(std->next->page_cache); + } + return ((last == std) ? std->prev : last); +} + +#define UHCI_REMOVE_QH(sqh,last) (last) = _uhci_remove_qh(sqh,last) +static uhci_qh_t * +_uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last) +{ + DPRINTFN(11, "%p from %p\n", sqh, last); + + /* (sc->sc_bus.mtx) must be locked */ + + /* only remove if not removed from a queue */ + if (sqh->h_prev) { + + sqh->h_prev->h_next = sqh->h_next; + sqh->h_prev->qh_h_next = sqh->qh_h_next; + + usb2_pc_cpu_flush(sqh->h_prev->page_cache); + + if (sqh->h_next) { + sqh->h_next->h_prev = sqh->h_prev; + usb2_pc_cpu_flush(sqh->h_next->page_cache); + } + /* + * set the Terminate-bit in the e_next of the QH, in case + * the transferred packet was short so that the QH still + * points at the last used TD + */ + sqh->qh_e_next = htole32(UHCI_PTR_T); + + last = ((last == sqh) ? sqh->h_prev : last); + + sqh->h_prev = 0; + + usb2_pc_cpu_flush(sqh->page_cache); + } + return (last); +} + +static void +uhci_isoc_done(uhci_softc_t *sc, struct usb2_xfer *xfer) +{ + struct usb2_page_search res; + uint32_t nframes = xfer->nframes; + uint32_t status; + uint32_t offset = 0; + uint32_t *plen = xfer->frlengths; + uint16_t len = 0; + uhci_td_t *td = xfer->td_transfer_first; + uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + /* sync any DMA memory before doing fixups */ + + usb2_bdma_post_sync(xfer); + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_p_last[0]; + } +#if USB_DEBUG + if (uhcidebug > 5) { + DPRINTF("isoc TD\n"); + uhci_dump_td(td); + } +#endif + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->td_status); + + len = UHCI_TD_GET_ACTLEN(status); + + if (len > *plen) { + len = *plen; + } + if (td->fix_pc) { + + usb2_get_page(td->fix_pc, 0, &res); + + /* copy data from fixup location to real location */ + + usb2_pc_cpu_invalidate(td->fix_pc); + + usb2_copy_in(xfer->frbuffers, offset, + res.buffer, len); + } + offset += *plen; + + *plen = len; + + /* remove TD from schedule */ + UHCI_REMOVE_TD(td, *pp_last); + + pp_last++; + plen++; + td = td->obj_next; + } + + xfer->aframes = xfer->nframes; + + return; +} + +static usb2_error_t +uhci_non_isoc_done_sub(struct usb2_xfer *xfer) +{ + struct usb2_page_search res; + uhci_td_t *td; + uhci_td_t *td_alt_next; + uint32_t status; + uint32_t token; + uint16_t len; + + td = xfer->td_transfer_cache; + td_alt_next = td->alt_next; + + if (xfer->aframes != xfer->nframes) { + xfer->frlengths[xfer->aframes] = 0; + } + while (1) { + + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->td_status); + token = le32toh(td->td_token); + + /* + * Verify the status and add + * up the actual length: + */ + + len = UHCI_TD_GET_ACTLEN(status); + if (len > td->len) { + /* should not happen */ + DPRINTF("Invalid status length, " + "0x%04x/0x%04x bytes\n", len, td->len); + status |= UHCI_TD_STALLED; + + } else if ((xfer->aframes != xfer->nframes) && (len > 0)) { + + if (td->fix_pc) { + + usb2_get_page(td->fix_pc, 0, &res); + + /* + * copy data from fixup location to real + * location + */ + + usb2_pc_cpu_invalidate(td->fix_pc); + + usb2_copy_in(xfer->frbuffers + xfer->aframes, + xfer->frlengths[xfer->aframes], res.buffer, len); + } + /* update actual length */ + + xfer->frlengths[xfer->aframes] += len; + } + /* Check for last transfer */ + if (((void *)td) == xfer->td_transfer_last) { + td = NULL; + break; + } + if (status & UHCI_TD_STALLED) { + /* the transfer is finished */ + td = NULL; + break; + } + /* Check for short transfer */ + if (len != td->len) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + td = td->alt_next; + } else { + /* the transfer is finished */ + td = NULL; + } + break; + } + td = td->obj_next; + + if (td->alt_next != td_alt_next) { + /* this USB frame is complete */ + break; + } + } + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + /* update data toggle */ + + xfer->pipe->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 0 : 1; + +#if USB_DEBUG + if (status & UHCI_TD_ERROR) { + DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x " + "status=%s%s%s%s%s%s%s%s%s%s%s\n", + xfer->address, xfer->endpoint, xfer->aframes, + (status & UHCI_TD_BITSTUFF) ? "[BITSTUFF]" : "", + (status & UHCI_TD_CRCTO) ? "[CRCTO]" : "", + (status & UHCI_TD_NAK) ? "[NAK]" : "", + (status & UHCI_TD_BABBLE) ? "[BABBLE]" : "", + (status & UHCI_TD_DBUFFER) ? "[DBUFFER]" : "", + (status & UHCI_TD_STALLED) ? "[STALLED]" : "", + (status & UHCI_TD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", + (status & UHCI_TD_IOC) ? "[IOC]" : "", + (status & UHCI_TD_IOS) ? "[IOS]" : "", + (status & UHCI_TD_LS) ? "[LS]" : "", + (status & UHCI_TD_SPD) ? "[SPD]" : ""); + } +#endif + return (status & UHCI_TD_STALLED) ? + USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION; +} + +static void +uhci_non_isoc_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + +#if USB_DEBUG + if (uhcidebug > 10) { + uhci_dump_tds(xfer->td_transfer_first); + } +#endif + + /* sync any DMA memory before doing fixups */ + + usb2_bdma_post_sync(xfer); + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + err = uhci_non_isoc_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = uhci_non_isoc_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = uhci_non_isoc_done_sub(xfer); + } +done: + uhci_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * uhci_check_transfer_sub + * + * The main purpose of this function is to update the data-toggle + * in case it is wrong. + *------------------------------------------------------------------------*/ +static void +uhci_check_transfer_sub(struct usb2_xfer *xfer) +{ + uhci_qh_t *qh; + uhci_td_t *td; + uhci_td_t *td_alt_next; + + uint32_t td_token; + uint32_t td_self; + + td = xfer->td_transfer_cache; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + td_token = td->obj_next->td_token; + td = td->alt_next; + xfer->td_transfer_cache = td; + td_self = td->td_self; + td_alt_next = td->alt_next; + + if ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) { + + /* + * The data toggle is wrong and + * we need to switch it ! + */ + + while (1) { + + td->td_token ^= htole32(UHCI_TD_SET_DT(1)); + usb2_pc_cpu_flush(td->page_cache); + + if (td == xfer->td_transfer_last) { + /* last transfer */ + break; + } + td = td->obj_next; + + if (td->alt_next != td_alt_next) { + /* next frame */ + break; + } + } + } + /* update the QH */ + qh->qh_e_next = td_self; + usb2_pc_cpu_flush(qh->page_cache); + + DPRINTFN(13, "xfer=%p following alt next\n", xfer); + return; +} + +/*------------------------------------------------------------------------* + * uhci_check_transfer + * + * Return values: + * 0: USB transfer is not finished + * Else: USB transfer is finished + *------------------------------------------------------------------------*/ +static uint8_t +uhci_check_transfer(struct usb2_xfer *xfer) +{ + uint32_t status; + uint32_t token; + uhci_td_t *td; + + DPRINTFN(16, "xfer=%p checking transfer\n", xfer); + + if (xfer->pipe->methods == &uhci_device_isoc_methods) { + /* isochronous transfer */ + + td = xfer->td_transfer_last; + + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->td_status); + + /* check also if the first is complete */ + + td = xfer->td_transfer_first; + + usb2_pc_cpu_invalidate(td->page_cache); + status |= le32toh(td->td_status); + + if (!(status & UHCI_TD_ACTIVE)) { + uhci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); + goto transferred; + } + } else { + /* non-isochronous transfer */ + + /* + * check whether there is an error somewhere + * in the middle, or whether there was a short + * packet (SPD and not ACTIVE) + */ + td = xfer->td_transfer_cache; + + while (1) { + usb2_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->td_status); + token = le32toh(td->td_token); + + /* + * if there is an active TD the transfer isn't done + */ + if (status & UHCI_TD_ACTIVE) { + /* update cache */ + xfer->td_transfer_cache = td; + goto done; + } + /* + * last transfer descriptor makes the transfer done + */ + if (((void *)td) == xfer->td_transfer_last) { + break; + } + /* + * any kind of error makes the transfer done + */ + if (status & UHCI_TD_STALLED) { + break; + } + /* + * check if we reached the last packet + * or if there is a short packet: + */ + if ((td->td_next == htole32(UHCI_PTR_T)) || + (UHCI_TD_GET_ACTLEN(status) < td->len)) { + + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + /* update cache */ + xfer->td_transfer_cache = td; + uhci_check_transfer_sub(xfer); + goto done; + } + } + /* transfer is done */ + break; + } + td = td->obj_next; + } + uhci_non_isoc_done(xfer); + goto transferred; + } + +done: + DPRINTFN(13, "xfer=%p is still active\n", xfer); + return (0); + +transferred: + return (1); +} + +static void +uhci_interrupt_poll(uhci_softc_t *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + /* + * check if transfer is transferred + */ + if (uhci_check_transfer(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + return; +} + +/*------------------------------------------------------------------------* + * uhci_interrupt - UHCI interrupt handler + * + * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, + * hence the interrupt handler will be setup before "sc->sc_bus.bdev" + * is present ! + *------------------------------------------------------------------------*/ +void +uhci_interrupt(uhci_softc_t *sc) +{ + uint32_t status; + + mtx_lock(&sc->sc_bus.mtx); + + DPRINTFN(16, "real interrupt\n"); + +#if USB_DEBUG + if (uhcidebug > 15) { + uhci_dumpregs(sc); + } +#endif + status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; + if (status == 0) { + /* the interrupt was not for us */ + goto done; + } + if (status & (UHCI_STS_RD | UHCI_STS_HSE | + UHCI_STS_HCPE | UHCI_STS_HCH)) { + + if (status & UHCI_STS_RD) { +#if USB_DEBUG + printf("%s: resume detect\n", + __FUNCTION__); +#endif + } + if (status & UHCI_STS_HSE) { + printf("%s: host system error\n", + __FUNCTION__); + } + if (status & UHCI_STS_HCPE) { + printf("%s: host controller process error\n", + __FUNCTION__); + } + if (status & UHCI_STS_HCH) { + /* no acknowledge needed */ + printf("%s: host controller halted\n", + __FUNCTION__); +#if USB_DEBUG + uhci_dump_all(sc); +#endif + } + } + /* get acknowledge bits */ + status &= (UHCI_STS_USBINT | + UHCI_STS_USBEI | + UHCI_STS_RD | + UHCI_STS_HSE | + UHCI_STS_HCPE); + + if (status == 0) { + /* nothing to acknowledge */ + goto done; + } + /* acknowledge interrupts */ + UWRITE2(sc, UHCI_STS, status); + + /* poll all the USB transfers */ + uhci_interrupt_poll(sc); + +done: + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/* + * called when a request does not complete + */ +static void +uhci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + uhci_softc_t *sc = xfer->usb2_sc; + + DPRINTF("xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + uhci_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +uhci_do_poll(struct usb2_bus *bus) +{ + struct uhci_softc *sc = UHCI_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + uhci_interrupt_poll(sc); + uhci_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +static void +uhci_setup_standard_chain_sub(struct uhci_std_temp *temp) +{ + uhci_td_t *td; + uhci_td_t *td_next; + uhci_td_t *td_alt_next; + uint32_t average; + uint32_t len_old; + uint8_t shortpkt_old; + uint8_t precompute; + + td_alt_next = NULL; + shortpkt_old = temp->shortpkt; + len_old = temp->len; + precompute = 1; + + /* software is used to detect short incoming transfers */ + + if ((temp->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) { + temp->td_status |= htole32(UHCI_TD_SPD); + } else { + temp->td_status &= ~htole32(UHCI_TD_SPD); + } + + temp->ml.buf_offset = 0; + +restart: + + temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); + temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->average)); + + td = temp->td; + td_next = temp->td_next; + + while (1) { + + if (temp->len == 0) { + + if (temp->shortpkt) { + break; + } + /* send a Zero Length Packet, ZLP, last */ + + temp->shortpkt = 1; + temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(0)); + average = 0; + + } else { + + average = temp->average; + + if (temp->len < average) { + temp->shortpkt = 1; + temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); + temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->len)); + average = temp->len; + } + } + + if (td_next == NULL) { + panic("%s: out of UHCI transfer descriptors!", __FUNCTION__); + } + /* get next TD */ + + td = td_next; + td_next = td->obj_next; + + /* check if we are pre-computing */ + + if (precompute) { + + /* update remaining length */ + + temp->len -= average; + + continue; + } + /* fill out current TD */ + + td->td_status = temp->td_status; + td->td_token = temp->td_token; + + /* update data toggle */ + + temp->td_token ^= htole32(UHCI_TD_SET_DT(1)); + + if (average == 0) { + + td->len = 0; + td->td_buffer = 0; + td->fix_pc = NULL; + + } else { + + /* update remaining length */ + + temp->len -= average; + + td->len = average; + + /* fill out buffer pointer and do fixup, if any */ + + uhci_mem_layout_fixup(&temp->ml, td); + } + + td->alt_next = td_alt_next; + + if ((td_next == td_alt_next) && temp->setup_alt_next) { + /* we need to receive these frames one by one ! */ + td->td_status |= htole32(UHCI_TD_IOC); + td->td_next = htole32(UHCI_PTR_T); + } else { + if (td_next) { + /* link the current TD with the next one */ + td->td_next = td_next->td_self; + } + } + + usb2_pc_cpu_flush(td->page_cache); + } + + if (precompute) { + precompute = 0; + + /* setup alt next pointer, if any */ + if (temp->short_frames_ok) { + if (temp->setup_alt_next) { + td_alt_next = td_next; + } + } else { + /* we use this field internally */ + td_alt_next = td_next; + } + + /* restore */ + temp->shortpkt = shortpkt_old; + temp->len = len_old; + goto restart; + } + temp->td = td; + temp->td_next = td_next; + + return; +} + +static uhci_td_t * +uhci_setup_standard_chain(struct usb2_xfer *xfer) +{ + struct uhci_std_temp temp; + uhci_td_t *td; + uint32_t x; + + DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.average = xfer->max_frame_size; + temp.max_frame_size = xfer->max_frame_size; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + temp.td = NULL; + temp.td_next = td; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.short_frames_ok = xfer->flags_int.short_frames_ok; + + uhci_mem_layout_init(&temp.ml, xfer); + + temp.td_status = + htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | + UHCI_TD_ACTIVE)); + + if (xfer->udev->speed == USB_SPEED_LOW) { + temp.td_status |= htole32(UHCI_TD_LS); + } + temp.td_token = + htole32(UHCI_TD_SET_ENDPT(xfer->endpoint) | + UHCI_TD_SET_DEVADDR(xfer->address)); + + if (xfer->pipe->toggle_next) { + /* DATA1 is next */ + temp.td_token |= htole32(UHCI_TD_SET_DT(1)); + } + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | + UHCI_TD_SET_ENDPT(0xF)); + temp.td_token |= htole32(UHCI_TD_PID_SETUP | + UHCI_TD_SET_DT(0)); + + temp.len = xfer->frlengths[0]; + temp.ml.buf_pc = xfer->frbuffers + 0; + temp.shortpkt = temp.len ? 1 : 0; + + uhci_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + temp.ml.buf_pc = xfer->frbuffers + x; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + /* + * Keep previous data toggle, + * device address and endpoint number: + */ + + temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | + UHCI_TD_SET_ENDPT(0xF) | + UHCI_TD_SET_DT(1)); + + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.shortpkt = 0; + + } else { + + /* regular data transfer */ + + temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + /* set endpoint direction */ + + temp.td_token |= + (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? + htole32(UHCI_TD_PID_IN) : + htole32(UHCI_TD_PID_OUT); + + uhci_setup_standard_chain_sub(&temp); + } + + /* check if we should append a status stage */ + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + /* + * send a DATA1 message and reverse the current endpoint + * direction + */ + + temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | + UHCI_TD_SET_ENDPT(0xF) | + UHCI_TD_SET_DT(1)); + temp.td_token |= + (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? + htole32(UHCI_TD_PID_IN | UHCI_TD_SET_DT(1)) : + htole32(UHCI_TD_PID_OUT | UHCI_TD_SET_DT(1)); + + temp.len = 0; + temp.ml.buf_pc = NULL; + temp.shortpkt = 0; + + uhci_setup_standard_chain_sub(&temp); + } + td = temp.td; + + td->td_next = htole32(UHCI_PTR_T); + + /* set interrupt bit */ + + td->td_status |= htole32(UHCI_TD_IOC); + + usb2_pc_cpu_flush(td->page_cache); + + /* must have at least one frame! */ + + xfer->td_transfer_last = td; + +#if USB_DEBUG + if (uhcidebug > 8) { + DPRINTF("nexttog=%d; data before transfer:\n", + xfer->pipe->toggle_next); + uhci_dump_tds(xfer->td_transfer_first); + } +#endif + return (xfer->td_transfer_first); +} + +/* NOTE: "done" can be run two times in a row, + * from close and from interrupt + */ + +static void +uhci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct usb2_pipe_methods *methods = xfer->pipe->methods; + uhci_softc_t *sc = xfer->usb2_sc; + uhci_qh_t *qh; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + if (qh) { + usb2_pc_cpu_invalidate(qh->page_cache); + + qh->e_next = 0; + qh->qh_e_next = htole32(UHCI_PTR_T); + + usb2_pc_cpu_flush(qh->page_cache); + } + if (xfer->flags_int.bandwidth_reclaimed) { + xfer->flags_int.bandwidth_reclaimed = 0; + uhci_rem_loop(sc); + } + if (methods == &uhci_device_bulk_methods) { + UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); + } + if (methods == &uhci_device_ctrl_methods) { + if (xfer->udev->speed == USB_SPEED_LOW) { + UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); + } + } + if (methods == &uhci_device_intr_methods) { + UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + } + /* + * Only finish isochronous transfers once + * which will update "xfer->frlengths". + */ + if (xfer->td_transfer_first && + xfer->td_transfer_last) { + if (methods == &uhci_device_isoc_methods) { + uhci_isoc_done(sc, xfer); + } + xfer->td_transfer_first = NULL; + xfer->td_transfer_last = NULL; + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +/*------------------------------------------------------------------------* + * uhci bulk support + *------------------------------------------------------------------------*/ +static void +uhci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_device_bulk_close(struct usb2_xfer *xfer) +{ + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uhci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_device_bulk_start(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + uhci_td_t *td; + uhci_qh_t *qh; + + /* setup TD's */ + td = uhci_setup_standard_chain(xfer); + + /* setup QH */ + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last); + uhci_add_loop(sc); + xfer->flags_int.bandwidth_reclaimed = 1; + + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods uhci_device_bulk_methods = +{ + .open = uhci_device_bulk_open, + .close = uhci_device_bulk_close, + .enter = uhci_device_bulk_enter, + .start = uhci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * uhci control support + *------------------------------------------------------------------------*/ +static void +uhci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_device_ctrl_close(struct usb2_xfer *xfer) +{ + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uhci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_device_ctrl_start(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + uhci_qh_t *qh; + uhci_td_t *td; + + /* setup TD's */ + td = uhci_setup_standard_chain(xfer); + + /* setup QH */ + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + /* + * NOTE: some devices choke on bandwidth- reclamation for control + * transfers + */ + if (xfer->udev->speed == USB_SPEED_LOW) { + UHCI_APPEND_QH(qh, td, sc->sc_ls_ctl_p_last); + } else { + UHCI_APPEND_QH(qh, td, sc->sc_fs_ctl_p_last); + } + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods uhci_device_ctrl_methods = +{ + .open = uhci_device_ctrl_open, + .close = uhci_device_ctrl_close, + .enter = uhci_device_ctrl_enter, + .start = uhci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * uhci interrupt support + *------------------------------------------------------------------------*/ +static void +uhci_device_intr_open(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + uint16_t best; + uint16_t bit; + uint16_t x; + + best = 0; + bit = UHCI_IFRAMELIST_COUNT / 2; + while (bit) { + if (xfer->interval >= bit) { + x = bit; + best = bit; + while (x & bit) { + if (sc->sc_intr_stat[x] < + sc->sc_intr_stat[best]) { + best = x; + } + x++; + } + break; + } + bit >>= 1; + } + + sc->sc_intr_stat[best]++; + xfer->qh_pos = best; + + DPRINTFN(3, "best=%d interval=%d\n", + best, xfer->interval); + return; +} + +static void +uhci_device_intr_close(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + sc->sc_intr_stat[xfer->qh_pos]--; + + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uhci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_device_intr_start(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + uhci_qh_t *qh; + uhci_td_t *td; + + /* setup TD's */ + td = uhci_setup_standard_chain(xfer); + + /* setup QH */ + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + /* enter QHs into the controller data structures */ + UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]); + + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods uhci_device_intr_methods = +{ + .open = uhci_device_intr_open, + .close = uhci_device_intr_close, + .enter = uhci_device_intr_enter, + .start = uhci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * uhci isochronous support + *------------------------------------------------------------------------*/ +static void +uhci_device_isoc_open(struct usb2_xfer *xfer) +{ + uhci_td_t *td; + uint32_t td_token; + uint8_t ds; + + td_token = + (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? + UHCI_TD_IN(0, xfer->endpoint, xfer->address, 0) : + UHCI_TD_OUT(0, xfer->endpoint, xfer->address, 0); + + td_token = htole32(td_token); + + /* initialize all TD's */ + + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { + + /* mark TD as inactive */ + td->td_status = htole32(UHCI_TD_IOS); + td->td_token = td_token; + + usb2_pc_cpu_flush(td->page_cache); + } + } + return; +} + +static void +uhci_device_isoc_close(struct usb2_xfer *xfer) +{ + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uhci_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct uhci_mem_layout ml; + uhci_softc_t *sc = xfer->usb2_sc; + uint32_t nframes; + uint32_t temp; + uint32_t *plen; + +#if USB_DEBUG + uint8_t once = 1; + +#endif + uhci_td_t *td; + uhci_td_t *td_last = NULL; + uhci_td_t **pp_last; + + DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + nframes = UREAD2(sc, UHCI_FRNUM); + + temp = (nframes - xfer->pipe->isoc_next) & + (UHCI_VFRAMELIST_COUNT - 1); + + if ((xfer->pipe->is_synced == 0) || + (temp < xfer->nframes)) { + /* + * If there is data underflow or the pipe queue is empty we + * schedule the transfer a few frames ahead of the current + * frame position. Else two isochronous transfers might + * overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT - 1); + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + temp = (xfer->pipe->isoc_next - nframes) & + (UHCI_VFRAMELIST_COUNT - 1); + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + + xfer->nframes; + + /* get the real number of frames */ + + nframes = xfer->nframes; + + uhci_mem_layout_init(&ml, xfer); + + plen = xfer->frlengths; + + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; + + pp_last = &sc->sc_isoc_p_last[xfer->pipe->isoc_next]; + + /* store starting position */ + + xfer->qh_pos = xfer->pipe->isoc_next; + + while (nframes--) { + if (td == NULL) { + panic("%s:%d: out of TD's\n", + __FUNCTION__, __LINE__); + } + if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { + pp_last = &sc->sc_isoc_p_last[0]; + } + if (*plen > xfer->max_frame_size) { +#if USB_DEBUG + if (once) { + once = 0; + printf("%s: frame length(%d) exceeds %d " + "bytes (frame truncated)\n", + __FUNCTION__, *plen, + xfer->max_frame_size); + } +#endif + *plen = xfer->max_frame_size; + } + /* reuse td_token from last transfer */ + + td->td_token &= htole32(~UHCI_TD_MAXLEN_MASK); + td->td_token |= htole32(UHCI_TD_SET_MAXLEN(*plen)); + + td->len = *plen; + + if (td->len == 0) { + /* + * Do not call "uhci_mem_layout_fixup()" when the + * length is zero! + */ + td->td_buffer = 0; + td->fix_pc = NULL; + + } else { + + /* fill out buffer pointer and do fixup, if any */ + + uhci_mem_layout_fixup(&ml, td); + + } + + /* update status */ + if (nframes == 0) { + td->td_status = htole32 + (UHCI_TD_ZERO_ACTLEN + (UHCI_TD_SET_ERRCNT(0) | + UHCI_TD_ACTIVE | + UHCI_TD_IOS | + UHCI_TD_IOC)); + } else { + td->td_status = htole32 + (UHCI_TD_ZERO_ACTLEN + (UHCI_TD_SET_ERRCNT(0) | + UHCI_TD_ACTIVE | + UHCI_TD_IOS)); + } + + usb2_pc_cpu_flush(td->page_cache); + +#if USB_DEBUG + if (uhcidebug > 5) { + DPRINTF("TD %d\n", nframes); + uhci_dump_td(td); + } +#endif + /* insert TD into schedule */ + UHCI_APPEND_TD(td, *pp_last); + pp_last++; + + plen++; + td_last = td; + td = td->obj_next; + } + + xfer->td_transfer_last = td_last; + + /* update isoc_next */ + xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) & + (UHCI_VFRAMELIST_COUNT - 1); + + return; +} + +static void +uhci_device_isoc_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); + return; +} + +struct usb2_pipe_methods uhci_device_isoc_methods = +{ + .open = uhci_device_isoc_open, + .close = uhci_device_isoc_close, + .enter = uhci_device_isoc_enter, + .start = uhci_device_isoc_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * uhci root control support + *------------------------------------------------------------------------* + * simulate a hardware hub by handling + * all the necessary requests + *------------------------------------------------------------------------*/ + +static void +uhci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_root_ctrl_close(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* data structures and routines + * to emulate the root hub: + */ + +static const +struct usb2_device_descriptor uhci_devd = +{ + sizeof(struct usb2_device_descriptor), + 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 const struct uhci_config_desc uhci_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(uhci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0 /* max power */ + }, + + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_FSHUB, + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = UE_DIR_IN | UHCI_INTR_ENDPT, + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ + .bInterval = 255, + }, +}; + +static const +struct usb2_hub_descriptor_min uhci_hubd_piix = +{ + sizeof(uhci_hubd_piix), + UDESC_HUB, + 2, + {UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0}, + 50, /* power on to power good */ + 0, + {0x00}, /* both ports are removable */ +}; + +/* + * 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 usb2_error_t +uhci_portreset(uhci_softc_t *sc, uint16_t index, uint8_t use_polling) +{ + uint16_t port; + uint16_t x; + uint8_t lim; + + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else + return (USB_ERR_IOERROR); + + x = URWMASK(UREAD2(sc, port)); + UWRITE2(sc, port, x | UHCI_PORTSC_PR); + + if (use_polling) { + /* polling */ + DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_PORT_ROOT_RESET_DELAY); + } + + DPRINTFN(4, "uhci port %d reset, status0 = 0x%04x\n", + index, UREAD2(sc, port)); + + x = URWMASK(UREAD2(sc, port)); + UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); + + if (use_polling) { + /* polling */ + DELAY(1000); + } else { + usb2_pause_mtx(&sc->sc_bus.mtx, 1); + } + + DPRINTFN(4, "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 = 0; lim < 12; lim++) { + + if (use_polling) { + /* polling */ + DELAY(USB_PORT_RESET_DELAY * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.mtx, + USB_PORT_RESET_DELAY); + } + + x = UREAD2(sc, port); + + DPRINTFN(4, "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(4, "uhci port %d loop %u, device detached\n", + index, lim); + goto done; + } + 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 */ + goto done; + } + UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE); + } + + DPRINTFN(2, "uhci port %d reset timed out\n", index); + return (USB_ERR_TIMEOUT); + +done: + DPRINTFN(4, "uhci port %d reset, status2 = 0x%04x\n", + index, UREAD2(sc, port)); + + sc->sc_isreset = 1; + return (USB_ERR_NORMAL_COMPLETION); +} + +static void +uhci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_root_ctrl_start(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + DPRINTF("\n"); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &uhci_root_ctrl_task, 0, 0); + + return; +} + +static void +uhci_root_ctrl_task(struct uhci_softc *sc, + struct uhci_config_copy *cc, uint16_t refcount) +{ + uhci_root_ctrl_poll(sc); + return; +} + +static void +uhci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + uhci_softc_t *sc = xfer->usb2_sc; + char *ptr; + uint16_t x; + uint16_t port; + uint16_t value; + uint16_t index; + uint16_t status; + uint16_t change; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + uhci_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = sc->sc_hub_desc.temp; + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " + "wValue=0x%04x wIndex=0x%04x\n", + std->req.bmRequestType, std->req.bRequest, + UGETW(std->req.wLength), value, index); + +#define C(x,y) ((x) | ((y) << 8)) + switch (C(std->req.bRequest, std->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): + std->len = 1; + sc->sc_hub_desc.temp[0] = sc->sc_conf; + break; + case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): + switch (value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(uhci_devd); + sc->sc_hub_desc.devd = uhci_devd; + break; + + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(uhci_confd); + std->ptr = USB_ADD_BYTES(&uhci_confd, 0); + break; + + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + ptr = "\001"; + break; + + case 1: /* Vendor */ + ptr = sc->sc_vendor; + break; + + case 2: /* Product */ + ptr = "UHCI root HUB"; + break; + + default: + ptr = ""; + break; + } + + std->len = usb2_make_str_desc + (sc->sc_hub_desc.temp, + sizeof(sc->sc_hub_desc.temp), + ptr); + break; + + default: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + case C(UR_GET_INTERFACE, UT_READ_INTERFACE): + std->len = 1; + sc->sc_hub_desc.temp[0] = 0; + break; + case C(UR_GET_STATUS, UT_READ_DEVICE): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); + break; + case C(UR_GET_STATUS, UT_READ_INTERFACE): + case C(UR_GET_STATUS, UT_READ_ENDPOINT): + std->len = 2; + USETW(sc->sc_hub_desc.stat.wStatus, 0); + break; + case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): + if (value >= USB_MAX_DEVICES) { + std->err = USB_ERR_IOERROR; + goto done; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if ((value != 0) && (value != 1)) { + std->err = USB_ERR_IOERROR; + goto done; + } + 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): + std->err = USB_ERR_IOERROR; + goto done; + 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(4, "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 { + std->err = USB_ERR_IOERROR; + goto done; + } + 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; + std->err = USB_ERR_NORMAL_COMPLETION; + goto done; + case UHF_PORT_CONNECTION: + case UHF_PORT_OVER_CURRENT: + case UHF_PORT_POWER: + case UHF_PORT_LOW_SPEED: + case UHF_C_PORT_SUSPEND: + default: + std->err = USB_ERR_IOERROR; + goto done; + } + 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 { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = 1; + sc->sc_hub_desc.temp[0] = + ((UREAD2(sc, port) & UHCI_PORTSC_LS) >> + UHCI_PORTSC_LS_SHIFT); + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if ((value & 0xff) != 0) { + std->err = USB_ERR_IOERROR; + goto done; + } + std->len = sizeof(uhci_hubd_piix); + std->ptr = USB_ADD_BYTES(&uhci_hubd_piix, 0); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + std->len = 16; + bzero(sc->sc_hub_desc.temp, 16); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else { + std->err = USB_ERR_IOERROR; + goto done; + } + 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(sc->sc_hub_desc.ps.wPortStatus, status); + USETW(sc->sc_hub_desc.ps.wPortChange, change); + std->len = sizeof(sc->sc_hub_desc.ps); + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + std->err = USB_ERR_IOERROR; + goto done; + 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 { + std->err = USB_ERR_IOERROR; + goto done; + } + 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: + std->err = uhci_portreset(sc, index, use_polling); + goto done; + case UHF_PORT_POWER: + /* pretend we turned on power */ + std->err = USB_ERR_NORMAL_COMPLETION; + goto done; + 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: + std->err = USB_ERR_IOERROR; + goto done; + } + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } +done: + return; +} + +static void +uhci_root_ctrl_poll(struct uhci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &uhci_root_ctrl_done); + return; +} + +struct usb2_pipe_methods uhci_root_ctrl_methods = +{ + .open = uhci_root_ctrl_open, + .close = uhci_root_ctrl_close, + .enter = uhci_root_ctrl_enter, + .start = uhci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * uhci root interrupt support + *------------------------------------------------------------------------*/ +static void +uhci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_root_intr_close(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + uhci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uhci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_root_intr_start(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + + usb2_transfer_timeout_ms(xfer, + &uhci_root_intr_check, xfer->interval); + return; +} + +static void +uhci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + uhci_softc_t *sc = xfer->usb2_sc; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer is transferred */ + uhci_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); +done: + return; +} + +/* + * this routine is executed periodically and simulates interrupts + * from the root controller interrupt pipe for port status change + */ +static void +uhci_root_intr_check(void *arg) +{ + struct usb2_xfer *xfer = arg; + uhci_softc_t *sc = xfer->usb2_sc; + + DPRINTFN(21, "\n"); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + sc->sc_hub_idata[0] = 0; + + if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { + sc->sc_hub_idata[0] |= 1 << 1; + } + if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { + sc->sc_hub_idata[0] |= 1 << 2; + } + if ((sc->sc_hub_idata[0] == 0) || !(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { + /* + * no change or controller not running, try again in a while + */ + uhci_root_intr_start(xfer); + } else { + usb2_sw_transfer(&sc->sc_root_intr, + &uhci_root_intr_done); + } + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +struct usb2_pipe_methods uhci_root_intr_methods = +{ + .open = uhci_root_intr_open, + .close = uhci_root_intr_close, + .enter = uhci_root_intr_enter, + .start = uhci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +uhci_xfer_setup(struct usb2_setup_params *parm) +{ + struct usb2_page_search page_info; + struct usb2_page_cache *pc; + uhci_softc_t *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t nqh; + uint32_t nfixup; + uint32_t n; + uint16_t align; + + sc = UHCI_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + parm->hc_max_packet_size = 0x500; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = 0x500; + + /* + * compute ntd and nqh + */ + if (parm->methods == &uhci_device_ctrl_methods) { + xfer->flags_int.bdma_enable = 1; + xfer->flags_int.bdma_no_post_sync = 1; + + usb2_transfer_setup_sub(parm); + + /* see EHCI HC driver for proof of "ntd" formula */ + + nqh = 1; + ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + + (xfer->max_data_length / xfer->max_frame_size)); + + } else if (parm->methods == &uhci_device_bulk_methods) { + xfer->flags_int.bdma_enable = 1; + xfer->flags_int.bdma_no_post_sync = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 1; + ntd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_frame_size)); + + } else if (parm->methods == &uhci_device_intr_methods) { + xfer->flags_int.bdma_enable = 1; + xfer->flags_int.bdma_no_post_sync = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 1; + ntd = ((2 * xfer->nframes) + + (xfer->max_data_length / xfer->max_frame_size)); + + } else if (parm->methods == &uhci_device_isoc_methods) { + xfer->flags_int.bdma_enable = 1; + xfer->flags_int.bdma_no_post_sync = 1; + + usb2_transfer_setup_sub(parm); + + nqh = 0; + ntd = xfer->nframes; + + } else { + + usb2_transfer_setup_sub(parm); + + nqh = 0; + ntd = 0; + } + + if (parm->err) { + return; + } + /* + * NOTE: the UHCI controller requires that + * every packet must be contiguous on + * the same USB memory page ! + */ + nfixup = (parm->bufsize / USB_PAGE_SIZE) + 1; + + /* + * Compute a suitable power of two alignment + * for our "max_frame_size" fixup buffer(s): + */ + align = xfer->max_frame_size; + n = 0; + while (align) { + align >>= 1; + n++; + } + + /* check for power of two */ + if (!(xfer->max_frame_size & + (xfer->max_frame_size - 1))) { + n--; + } + /* + * We don't allow alignments of + * less than 8 bytes: + * + * NOTE: Allocating using an aligment + * of 1 byte has special meaning! + */ + if (n < 3) { + n = 3; + } + align = (1 << n); + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, xfer->max_frame_size, + align, nfixup)) { + parm->err = USB_ERR_NOMEM; + return; + } + xfer->buf_fixup = pc; + +alloc_dma_set: + + if (parm->err) { + return; + } + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(uhci_td_t), + UHCI_TD_ALIGN, ntd)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != ntd; n++) { + uhci_td_t *td; + + usb2_get_page(pc + n, 0, &page_info); + + td = page_info.buffer; + + /* init TD */ + if ((parm->methods == &uhci_device_bulk_methods) || + (parm->methods == &uhci_device_ctrl_methods) || + (parm->methods == &uhci_device_intr_methods)) { + /* set depth first bit */ + td->td_self = htole32(page_info.physaddr | + UHCI_PTR_TD | UHCI_PTR_VF); + } else { + td->td_self = htole32(page_info.physaddr | + UHCI_PTR_TD); + } + + td->obj_next = last_obj; + td->page_cache = pc + n; + + last_obj = td; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; + + last_obj = NULL; + + if (usb2_transfer_setup_sub_malloc( + parm, &pc, sizeof(uhci_qh_t), + UHCI_QH_ALIGN, nqh)) { + parm->err = USB_ERR_NOMEM; + return; + } + if (parm->buf) { + for (n = 0; n != nqh; n++) { + uhci_qh_t *qh; + + usb2_get_page(pc + n, 0, &page_info); + + qh = page_info.buffer; + + /* init QH */ + qh->qh_self = htole32(page_info.physaddr | UHCI_PTR_QH); + qh->obj_next = last_obj; + qh->page_cache = pc + n; + + last_obj = qh; + + usb2_pc_cpu_flush(pc + n); + } + } + xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; + + if (!xfer->flags_int.curr_dma_set) { + xfer->flags_int.curr_dma_set = 1; + goto alloc_dma_set; + } + return; +} + +static void +uhci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_addr); + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + if (udev->device_index == sc->sc_addr) { + switch (edesc->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: + /* do nothing */ + break; + } + } else { + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &uhci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &uhci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + if (udev->speed == USB_SPEED_FULL) { + pipe->methods = &uhci_device_isoc_methods; + } + break; + case UE_BULK: + if (udev->speed != USB_SPEED_LOW) { + pipe->methods = &uhci_device_bulk_methods; + } + break; + default: + /* do nothing */ + break; + } + } + return; +} + +static void +uhci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +uhci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) +{ + /* + * Wait until hardware has finished any possible use of the + * transfer descriptor(s) and QH + */ + *pus = (1125); /* microseconds */ + return; +} + +struct usb2_bus_methods uhci_bus_methods = +{ + .pipe_init = uhci_pipe_init, + .xfer_setup = uhci_xfer_setup, + .xfer_unsetup = uhci_xfer_unsetup, + .do_poll = uhci_do_poll, + .get_dma_delay = uhci_get_dma_delay, +}; diff --git a/sys/dev/usb2/controller/uhci2.h b/sys/dev/usb2/controller/uhci2.h new file mode 100644 index 000000000000..9be89ed349be --- /dev/null +++ b/sys/dev/usb2/controller/uhci2.h @@ -0,0 +1,318 @@ +/* $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 _UHCI_H_ +#define _UHCI_H_ + +/* PCI config registers */ +#define PCI_USBREV 0x60 /* USB protocol revision */ +#define PCI_USB_REV_MASK 0xff +#define PCI_USB_REV_PRE_1_0 0x00 +#define PCI_USB_REV_1_0 0x10 +#define PCI_USB_REV_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 /* units */ +#define UHCI_FRAMELIST_ALIGN 4096 /* bytes */ + +/* Structures alignment (bytes) */ +#define UHCI_TD_ALIGN 16 +#define UHCI_QH_ALIGN 16 + +#if ((USB_PAGE_SIZE < UHCI_TD_ALIGN) || (UHCI_TD_ALIGN == 0) || \ + (USB_PAGE_SIZE < UHCI_QH_ALIGN) || (UHCI_QH_ALIGN == 0)) +#error "Invalid USB page size!" +#endif + +typedef uint32_t uhci_physaddr_t; + +#define UHCI_PTR_T 0x00000001 +#define UHCI_PTR_TD 0x00000000 +#define UHCI_PTR_QH 0x00000002 +#define UHCI_PTR_VF 0x00000004 + +#define UHCI_QH_REMOVE_DELAY 5 /* us - QH remove delay */ + +/* + * The Queue Heads (QH) and Transfer Descriptors (TD) are accessed by + * both the CPU and the USB-controller which run concurrently. Great + * care must be taken. When the data-structures are linked into the + * USB controller's frame list, the USB-controller "owns" the + * td_status and qh_elink fields, which will not be written by the + * CPU. + * + */ + +struct uhci_td { +/* + * Data used by the UHCI controller. + * volatile is used in order to mantain struct members ordering. + */ + volatile uint32_t td_next; + volatile uint32_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 + volatile uint32_t td_token; +#define UHCI_TD_PID 0x000000ff +#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 + volatile uint32_t td_buffer; +/* + * Extra information needed: + */ + struct uhci_td *next; + struct uhci_td *prev; + struct uhci_td *obj_next; + struct usb2_page_cache *page_cache; + struct usb2_page_cache *fix_pc; + uint32_t td_self; + uint16_t len; +} __aligned(UHCI_TD_ALIGN); + +typedef struct uhci_td 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)) + +struct uhci_qh { +/* + * Data used by the UHCI controller. + */ + volatile uint32_t qh_h_next; + volatile uint32_t qh_e_next; +/* + * Extra information needed: + */ + struct uhci_qh *h_next; + struct uhci_qh *h_prev; + struct uhci_qh *obj_next; + struct uhci_td *e_next; + struct usb2_page_cache *page_cache; + uint32_t qh_self; + uint16_t intr_pos; +} __aligned(UHCI_QH_ALIGN); + +typedef struct uhci_qh uhci_qh_t; + +/* Maximum number of isochronous TD's and QH's interrupt */ +#define UHCI_VFRAMELIST_COUNT 128 +#define UHCI_IFRAMELIST_COUNT (2 * UHCI_VFRAMELIST_COUNT) + +#if (((UHCI_VFRAMELIST_COUNT & (UHCI_VFRAMELIST_COUNT-1)) != 0) || \ + (UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT)) +#error "UHCI_VFRAMELIST_COUNT is not power of two" +#error "or UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT" +#endif + +#if (UHCI_VFRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER) +#error "maximum number of full-speed isochronous frames is higher than supported!" +#endif + +struct uhci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union uhci_hub_desc { + struct usb2_status stat; + struct usb2_port_status ps; + struct usb2_device_descriptor devd; + uint8_t temp[128]; +}; + +struct uhci_hw_softc { + struct usb2_page_cache pframes_pc; + struct usb2_page_cache isoc_start_pc[UHCI_VFRAMELIST_COUNT]; + struct usb2_page_cache intr_start_pc[UHCI_IFRAMELIST_COUNT]; + struct usb2_page_cache ls_ctl_start_pc; + struct usb2_page_cache fs_ctl_start_pc; + struct usb2_page_cache bulk_start_pc; + struct usb2_page_cache last_qh_pc; + struct usb2_page_cache last_td_pc; + + struct usb2_page pframes_pg; + struct usb2_page isoc_start_pg[UHCI_VFRAMELIST_COUNT]; + struct usb2_page intr_start_pg[UHCI_IFRAMELIST_COUNT]; + struct usb2_page ls_ctl_start_pg; + struct usb2_page fs_ctl_start_pg; + struct usb2_page bulk_start_pg; + struct usb2_page last_qh_pg; + struct usb2_page last_td_pg; +}; + +typedef struct uhci_softc { + struct uhci_hw_softc sc_hw; + struct usb2_bus sc_bus; /* base device */ + struct usb2_config_td sc_config_td; + union uhci_hub_desc sc_hub_desc; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + + struct uhci_td *sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]; /* pointer to last TD + * for isochronous */ + struct uhci_qh *sc_intr_p_last[UHCI_IFRAMELIST_COUNT]; /* pointer to last QH + * for interrupt */ + struct uhci_qh *sc_ls_ctl_p_last; /* pointer to last QH for low + * speed control */ + struct uhci_qh *sc_fs_ctl_p_last; /* pointer to last QH for full + * speed control */ + struct uhci_qh *sc_bulk_p_last; /* pointer to last QH for bulk */ + struct uhci_qh *sc_reclaim_qh_p; + struct uhci_qh *sc_last_qh_p; + struct uhci_td *sc_last_td_p; + struct resource *sc_io_res; + struct resource *sc_irq_res; + void *sc_intr_hdl; + device_t sc_dev; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + uint32_t sc_loops; /* number of QHs that wants looping */ + + uint16_t sc_intr_stat[UHCI_IFRAMELIST_COUNT]; + uint16_t sc_saved_frnum; + + uint8_t sc_addr; /* device address */ + uint8_t sc_conf; /* device configuration */ + uint8_t sc_isreset; + uint8_t sc_saved_sof; + uint8_t sc_hub_idata[1]; + + char sc_vendor[16]; /* vendor string for root hub */ +} uhci_softc_t; + +usb2_bus_mem_cb_t uhci_iterate_hw_softc; + +usb2_error_t uhci_init(uhci_softc_t *sc); +void uhci_suspend(uhci_softc_t *sc); +void uhci_resume(uhci_softc_t *sc); +void uhci_reset(uhci_softc_t *sc); +void uhci_interrupt(uhci_softc_t *sc); + +#endif /* _UHCI_H_ */ diff --git a/sys/dev/usb2/controller/uhci2_pci.c b/sys/dev/usb2/controller/uhci2_pci.c new file mode 100644 index 000000000000..947f9afbc8ee --- /dev/null +++ b/sys/dev/usb2/controller/uhci2_pci.c @@ -0,0 +1,453 @@ +/*- + * 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 +__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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PCI_UHCI_VENDORID_INTEL 0x8086 +#define PCI_UHCI_VENDORID_VIA 0x1106 + +/* PIIX4E has no separate stepping */ + +#define PCI_UHCI_BASE_REG 0x20 + +static device_probe_t uhci_pci_probe; +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_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_resume(sc); + + bus_generic_resume(self); + return (0); +} + +static const char * +uhci_pci_match(device_t self) +{ + uint32_t device_id = pci_get_devid(self); + + switch (device_id) { + case 0x26888086: + return ("Intel 631XESB/632XESB/3100 USB controller USB-1"); + + case 0x26898086: + return ("Intel 631XESB/632XESB/3100 USB controller USB-2"); + + case 0x268a8086: + return ("Intel 631XESB/632XESB/3100 USB controller USB-3"); + + case 0x268b8086: + return ("Intel 631XESB/632XESB/3100 USB controller USB-4"); + + case 0x70208086: + return ("Intel 82371SB (PIIX3) USB controller"); + + case 0x71128086: + return ("Intel 82371AB/EB (PIIX4) USB controller"); + + case 0x24128086: + return ("Intel 82801AA (ICH) USB controller"); + + case 0x24228086: + return ("Intel 82801AB (ICH0) USB controller"); + + case 0x24428086: + return ("Intel 82801BA/BAM (ICH2) USB controller USB-A"); + + case 0x24448086: + return ("Intel 82801BA/BAM (ICH2) USB controller USB-B"); + + case 0x24828086: + return ("Intel 82801CA/CAM (ICH3) USB controller USB-A"); + + case 0x24848086: + return ("Intel 82801CA/CAM (ICH3) USB controller USB-B"); + + case 0x24878086: + return ("Intel 82801CA/CAM (ICH3) USB controller USB-C"); + + case 0x24c28086: + return ("Intel 82801DB (ICH4) USB controller USB-A"); + + case 0x24c48086: + return ("Intel 82801DB (ICH4) USB controller USB-B"); + + case 0x24c78086: + return ("Intel 82801DB (ICH4) USB controller USB-C"); + + case 0x24d28086: + return ("Intel 82801EB (ICH5) USB controller USB-A"); + + case 0x24d48086: + return ("Intel 82801EB (ICH5) USB controller USB-B"); + + case 0x24d78086: + return ("Intel 82801EB (ICH5) USB controller USB-C"); + + case 0x24de8086: + return ("Intel 82801EB (ICH5) USB controller USB-D"); + + case 0x26588086: + return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A"); + + case 0x26598086: + return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B"); + + case 0x265a8086: + return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C"); + + case 0x265b8086: + return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D"); + + case 0x28308086: + return ("Intel 82801H (ICH8) USB controller USB-A"); + case 0x28318086: + return ("Intel 82801H (ICH8) USB controller USB-B"); + case 0x28328086: + return ("Intel 82801H (ICH8) USB controller USB-C"); + case 0x28348086: + return ("Intel 82801H (ICH8) USB controller USB-D"); + case 0x28358086: + return ("Intel 82801H (ICH8) USB controller USB-E"); + case 0x29348086: + return ("Intel 82801I (ICH9) USB controller"); + case 0x29358086: + return ("Intel 82801I (ICH9) USB controller"); + case 0x29368086: + return ("Intel 82801I (ICH9) USB controller"); + case 0x29378086: + return ("Intel 82801I (ICH9) USB controller"); + case 0x29388086: + return ("Intel 82801I (ICH9) USB controller"); + case 0x29398086: + return ("Intel 82801I (ICH9) USB controller"); + + case 0x719a8086: + return ("Intel 82443MX USB controller"); + + case 0x76028086: + return ("Intel 82372FB/82468GX USB controller"); + + case 0x30381106: + return ("VIA 83C572 USB controller"); + + default: + break; + } + + if ((pci_get_class(self) == PCIC_SERIALBUS) && + (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && + (pci_get_progif(self) == PCI_INTERFACE_UHCI)) { + return ("UHCI (generic) USB controller"); + } + return (NULL); +} + +static int +uhci_pci_probe(device_t self) +{ + const char *desc = uhci_pci_match(self); + + if (desc) { + device_set_desc(self, desc); + return (0); + } else { + return (ENXIO); + } +} + +static int +uhci_pci_attach(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + int rid; + int err; + + if (sc == NULL) { + device_printf(self, "Could not allocate sc\n"); + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), + &uhci_iterate_hw_softc)) { + return ENOMEM; + } + sc->sc_dev = self; + + pci_enable_busmaster(self); + + rid = PCI_UHCI_BASE_REG; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map ports\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + /* disable interrupts */ + bus_space_write_2(sc->sc_io_tag, sc->sc_io_hdl, UHCI_INTR, 0); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + 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_USB_REV_MASK) { + case PCI_USB_REV_PRE_1_0: + sc->sc_bus.usbrev = USB_REV_PRE_1_0; + break; + case PCI_USB_REV_1_0: + sc->sc_bus.usbrev = USB_REV_1_0; + break; + default: + sc->sc_bus.usbrev = USB_REV_UNKNOWN; + break; + } + + err = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.mtx, + NULL, 0, 4); + if (err) { + device_printf(self, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl); +#endif + + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + /* + * 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? + */ +#if 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); + + err = uhci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed\n"); + goto error; + } + return (0); + +error: + uhci_pci_detach(self); + return (ENXIO); +} + +int +uhci_pci_detach(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + device_t bdev; + + usb2_config_td_drain(&sc->sc_config_td); + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(self); + + /* + * disable interrupts that might have been switched on in + * uhci_init. + */ + if (sc->sc_io_res) { + mtx_lock(&sc->sc_bus.mtx); + + /* stop the controller */ + uhci_reset(sc); + + mtx_unlock(&sc->sc_bus.mtx); + } + pci_disable_busmaster(self); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) { + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + } + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_bus, &uhci_iterate_hw_softc); + + return (0); +} + +static driver_t uhci_driver = +{ + .name = "uhci", + .methods = (device_method_t[]){ + /* 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} + }, + .size = sizeof(struct uhci_softc), +}; + +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, usb2_controller, 1, 1, 1); +MODULE_DEPEND(uhci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/usb2_bus.h b/sys/dev/usb2/controller/usb2_bus.h new file mode 100644 index 000000000000..cfe98f7c64fb --- /dev/null +++ b/sys/dev/usb2/controller/usb2_bus.h @@ -0,0 +1,88 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_BUS_H_ +#define _USB2_BUS_H_ + +/* + * The following structure defines the USB explore message sent to the + * USB explore process. + */ + +struct usb2_bus_msg { + struct usb2_proc_msg hdr; + struct usb2_bus *bus; +}; + +/* + * The following structure defines the USB statistics structure. + */ +struct usb2_bus_stat { + uint32_t uds_requests[4]; +}; + +/* + * The following structure defines an USB BUS. There is one USB BUS + * for every Host or Device controller. + */ +struct usb2_bus { + struct usb2_bus_stat stats_err; + struct usb2_bus_stat stats_ok; + struct usb2_process explore_proc; + struct usb2_bus_msg explore_msg[2]; + struct usb2_bus_msg detach_msg[2]; + struct mtx mtx; /* This mutex protects the USB + * hardware */ + struct usb2_perm perm; + struct usb2_xfer_queue intr_q; + + device_t bdev; /* filled by HC driver */ + + struct usb2_dma_parent_tag dma_parent_tag[1]; + struct usb2_dma_tag dma_tags[USB_BUS_DMA_TAG_MAX]; + + struct usb2_bus_methods *methods; /* filled by HC driver */ + struct usb2_device *devices[USB_MAX_DEVICES]; + + uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; + uint32_t transfer_count[4]; + uint16_t isoc_time_last; /* in milliseconds */ + + uint8_t alloc_failed; /* Set if memory allocation failed. */ + uint8_t driver_added_refcount; /* Current driver generation count */ + uint8_t usbrev; /* USB revision. See "USB_REV_XXX". */ + + uint8_t devices_max; /* maximum number of USB devices */ + uint8_t do_probe; /* set if USB BUS should be re-probed */ + + union { + struct usb2_hw_ep_scratch hw_ep_scratch[1]; + struct usb2_temp_setup temp_setup[1]; + uint8_t data[128]; + } scratch[1]; +}; + +#endif /* _USB2_BUS_H_ */ diff --git a/sys/dev/usb2/controller/usb2_controller.c b/sys/dev/usb2/controller/usb2_controller.c new file mode 100644 index 000000000000..fdfa3a657e41 --- /dev/null +++ b/sys/dev/usb2/controller/usb2_controller.c @@ -0,0 +1,477 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#define USB_DEBUG_VAR usb2_ctrl_debug + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* function prototypes */ + +static device_probe_t usb2_probe; +static device_attach_t usb2_attach; +static device_detach_t usb2_detach; + +static void usb2_attach_sub(device_t dev, struct usb2_bus *bus); +static void usb2_post_init(void *arg); +static void usb2_bus_mem_flush_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align); +static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align); +static void usb2_bus_mem_free_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align); + +/* static variables */ + +#if USB_DEBUG +static int usb2_ctrl_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); +SYSCTL_INT(_hw_usb2_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb2_ctrl_debug, 0, + "Debug level"); +#endif + +static uint8_t usb2_post_init_called = 0; + +static devclass_t usb2_devclass; + +static device_method_t usb2_methods[] = { + DEVMETHOD(device_probe, usb2_probe), + DEVMETHOD(device_attach, usb2_attach), + DEVMETHOD(device_detach, usb2_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + {0, 0} +}; + +static driver_t usb2_driver = { + .name = "usbus", + .methods = usb2_methods, + .size = 0, +}; + +DRIVER_MODULE(usbus, ohci, usb2_driver, usb2_devclass, 0, 0); +DRIVER_MODULE(usbus, uhci, usb2_driver, usb2_devclass, 0, 0); +DRIVER_MODULE(usbus, ehci, usb2_driver, usb2_devclass, 0, 0); +DRIVER_MODULE(usbus, at91_udp, usb2_driver, usb2_devclass, 0, 0); +DRIVER_MODULE(usbus, uss820, usb2_driver, usb2_devclass, 0, 0); + +MODULE_DEPEND(usb2_controller, usb2_core, 1, 1, 1); +MODULE_VERSION(usb2_controller, 1); + +/*------------------------------------------------------------------------* + * usb2_probe + * + * This function is called from "{ehci,ohci,uhci}_pci_attach()". + *------------------------------------------------------------------------*/ +static int +usb2_probe(device_t dev) +{ + DPRINTF("\n"); + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_attach + *------------------------------------------------------------------------*/ +static int +usb2_attach(device_t dev) +{ + struct usb2_bus *bus = device_get_ivars(dev); + + DPRINTF("\n"); + + if (bus == NULL) { + DPRINTFN(0, "USB device has no ivars\n"); + return (ENXIO); + } + if (usb2_post_init_called) { + mtx_lock(&Giant); + usb2_attach_sub(dev, bus); + mtx_unlock(&Giant); + usb2_needs_explore(bus, 1); + } + return (0); /* return success */ +} + +/*------------------------------------------------------------------------* + * usb2_detach + *------------------------------------------------------------------------*/ +static int +usb2_detach(device_t dev) +{ + struct usb2_bus *bus = device_get_softc(dev); + + DPRINTF("\n"); + + if (bus == NULL) { + /* was never setup properly */ + return (0); + } + /* Let the USB explore process detach all devices. */ + + mtx_lock(&bus->mtx); + if (usb2_proc_msignal(&bus->explore_proc, + &bus->detach_msg[0], &bus->detach_msg[1])) { + /* ignore */ + } + /* Wait for detach to complete */ + + usb2_proc_mwait(&bus->explore_proc, + &bus->detach_msg[0], &bus->detach_msg[1]); + + mtx_unlock(&bus->mtx); + + /* Get rid of USB explore process */ + + usb2_proc_unsetup(&bus->explore_proc); + + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_bus_explore + * + * This function is used to explore the device tree from the root. + *------------------------------------------------------------------------*/ +static void +usb2_bus_explore(struct usb2_proc_msg *pm) +{ + struct usb2_bus *bus; + struct usb2_device *udev; + + bus = ((struct usb2_bus_msg *)pm)->bus; + udev = bus->devices[USB_ROOT_HUB_ADDR]; + + if (udev && udev->hub) { + + if (bus->do_probe) { + bus->do_probe = 0; + bus->driver_added_refcount++; + } + if (bus->driver_added_refcount == 0) { + /* avoid zero, hence that is memory default */ + bus->driver_added_refcount = 1; + } + mtx_unlock(&bus->mtx); + + mtx_lock(&Giant); + + /* + * Explore the Root USB HUB. This call can sleep, + * exiting Giant, which is actually Giant. + */ + (udev->hub->explore) (udev); + + mtx_unlock(&Giant); + + mtx_lock(&bus->mtx); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_detach + * + * This function is used to detach the device tree from the root. + *------------------------------------------------------------------------*/ +static void +usb2_bus_detach(struct usb2_proc_msg *pm) +{ + struct usb2_bus *bus; + struct usb2_device *udev; + device_t dev; + + bus = ((struct usb2_bus_msg *)pm)->bus; + udev = bus->devices[USB_ROOT_HUB_ADDR]; + dev = bus->bdev; + /* clear the softc */ + device_set_softc(dev, NULL); + mtx_unlock(&bus->mtx); + + mtx_lock(&Giant); + + /* detach children first */ + bus_generic_detach(dev); + + /* + * Free USB Root device, but not any sub-devices, hence they + * are freed by the caller of this function: + */ + usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 0); + usb2_free_device(udev); + + mtx_unlock(&Giant); + mtx_lock(&bus->mtx); + /* clear bdev variable last */ + bus->bdev = NULL; + return; +} + +/*------------------------------------------------------------------------* + * usb2_attach_sub + * + * This function is the real USB bus attach code. It is factored out, + * hence it can be called at two different places in time. During + * bootup this function is called from "usb2_post_init". During + * hot-plug it is called directly from the "usb2_attach()" method. + *------------------------------------------------------------------------*/ +static void +usb2_attach_sub(device_t dev, struct usb2_bus *bus) +{ + struct usb2_device *child; + usb2_error_t err; + uint8_t speed; + + DPRINTF("\n"); + + mtx_assert(&Giant, MA_OWNED); + + switch (bus->usbrev) { + case USB_REV_1_0: + speed = USB_SPEED_FULL; + device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); + break; + + case USB_REV_1_1: + speed = USB_SPEED_FULL; + device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); + break; + + case USB_REV_2_0: + speed = USB_SPEED_HIGH; + device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); + break; + + case USB_REV_2_5: + speed = USB_SPEED_VARIABLE; + device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); + break; + + default: + device_printf(bus->bdev, "Unsupported USB revision!\n"); + return; + } + + /* Allocate the Root USB device */ + + child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, + speed, USB_MODE_HOST); + if (child) { + err = usb2_probe_and_attach(child, + USB_IFACE_INDEX_ANY); + if (!err) { + if (!bus->devices[USB_ROOT_HUB_ADDR]->hub) { + err = USB_ERR_NO_ROOT_HUB; + } + } + } else { + err = USB_ERR_NOMEM; + } + + if (err) { + device_printf(bus->bdev, "Root HUB problem, error=%s\n", + usb2_errstr(err)); + } + /* Initialise USB process messages */ + bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore; + bus->explore_msg[0].bus = bus; + bus->explore_msg[1].hdr.pm_callback = &usb2_bus_explore; + bus->explore_msg[1].bus = bus; + + bus->detach_msg[0].hdr.pm_callback = &usb2_bus_detach; + bus->detach_msg[0].bus = bus; + bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach; + bus->detach_msg[1].bus = bus; + + /* Create a new USB process */ + if (usb2_proc_setup(&bus->explore_proc, + &bus->mtx, USB_PRI_MED)) { + printf("WARNING: Creation of USB explore process failed.\n"); + } + /* set softc - we are ready */ + device_set_softc(dev, bus); + return; +} + +/*------------------------------------------------------------------------* + * usb2_post_init + * + * This function is called to attach all USB busses that were found + * during bootup. + *------------------------------------------------------------------------*/ +static void +usb2_post_init(void *arg) +{ + struct usb2_bus *bus; + devclass_t dc; + device_t dev; + int max; + int n; + + mtx_lock(&Giant); + + usb2_devclass_ptr = devclass_find("usbus"); + + dc = usb2_devclass_ptr; + if (dc) { + max = devclass_get_maxunit(dc) + 1; + for (n = 0; n != max; n++) { + dev = devclass_get_device(dc, n); + if (dev && device_is_attached(dev)) { + bus = device_get_ivars(dev); + if (bus) { + mtx_lock(&Giant); + usb2_attach_sub(dev, bus); + mtx_unlock(&Giant); + } + } + } + } else { + DPRINTFN(0, "no devclass\n"); + } + usb2_post_init_called = 1; + + /* explore all USB busses in parallell */ + + usb2_needs_explore_all(); + + mtx_unlock(&Giant); + + return; +} + +SYSINIT(usb2_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_post_init, NULL); +SYSUNINIT(usb2_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb2_bus_unload, NULL); + +/*------------------------------------------------------------------------* + * usb2_bus_mem_flush_all_cb + *------------------------------------------------------------------------*/ +static void +usb2_bus_mem_flush_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, + struct usb2_page *pg, uint32_t size, uint32_t align) +{ + usb2_pc_cpu_flush(pc); + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_mem_flush_all - factored out code + *------------------------------------------------------------------------*/ +void +usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) +{ + if (cb) { + cb(bus, &usb2_bus_mem_flush_all_cb); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_mem_alloc_all_cb + *------------------------------------------------------------------------*/ +static void +usb2_bus_mem_alloc_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, + struct usb2_page *pg, uint32_t size, uint32_t align) +{ + /* need to initialize the page cache */ + pc->tag_parent = bus->dma_parent_tag; + + if (usb2_pc_alloc_mem(pc, pg, size, align)) { + bus->alloc_failed = 1; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_mem_alloc_all - factored out code + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, + usb2_bus_mem_cb_t *cb) +{ + bus->alloc_failed = 0; + + bus->devices_max = USB_MAX_DEVICES; + + mtx_init(&bus->mtx, "USB lock", + NULL, MTX_DEF | MTX_RECURSE); + + TAILQ_INIT(&bus->intr_q.head); + + usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, + dmat, &bus->mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX); + + if (cb) { + cb(bus, &usb2_bus_mem_alloc_all_cb); + } + if (bus->alloc_failed) { + usb2_bus_mem_free_all(bus, cb); + } + return (bus->alloc_failed); +} + +/*------------------------------------------------------------------------* + * usb2_bus_mem_free_all_cb + *------------------------------------------------------------------------*/ +static void +usb2_bus_mem_free_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, + struct usb2_page *pg, uint32_t size, uint32_t align) +{ + usb2_pc_free_mem(pc); + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_mem_free_all - factored out code + *------------------------------------------------------------------------*/ +void +usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) +{ + if (cb) { + cb(bus, &usb2_bus_mem_free_all_cb); + } + usb2_dma_tag_unsetup(bus->dma_parent_tag); + + mtx_destroy(&bus->mtx); + + return; +} diff --git a/sys/dev/usb2/controller/usb2_controller.h b/sys/dev/usb2/controller/usb2_controller.h new file mode 100644 index 000000000000..496e8c253b4a --- /dev/null +++ b/sys/dev/usb2/controller/usb2_controller.h @@ -0,0 +1,172 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_CONTROLLER_H_ +#define _USB2_CONTROLLER_H_ + +/* defines */ + +#define USB_BUS_DMA_TAG_MAX 8 + +/* structure prototypes */ + +struct usb2_bus; +struct usb2_page; +struct usb2_pipe; +struct usb2_page_cache; +struct usb2_setup_params; +struct usb2_hw_ep_profile; +struct usb2_fs_isoc_schedule; +struct usb2_config_descriptor; +struct usb2_endpoint_descriptor; + +/* typedefs */ + +typedef void (usb2_bus_mem_sub_cb_t)(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align); +typedef void (usb2_bus_mem_cb_t)(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *scb); + +/* + * The following structure is used to define all the USB BUS + * callbacks. + */ +struct usb2_bus_methods { + + /* USB Device and Host mode - Mandatory */ + + void (*pipe_init) (struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe); + void (*do_poll) (struct usb2_bus *); + void (*xfer_setup) (struct usb2_setup_params *parm); + void (*xfer_unsetup) (struct usb2_xfer *xfer); + void (*get_dma_delay) (struct usb2_bus *, uint32_t *pdelay); + + /* USB Device mode only - Mandatory */ + + void (*get_hw_ep_profile) (struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr); + void (*set_stall) (struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe); + void (*clear_stall) (struct usb2_device *udev, struct usb2_pipe *pipe); + void (*rem_wakeup_set) (struct usb2_device *udev, uint8_t is_on); + + /* USB Device mode only - Optional */ + + void (*vbus_interrupt) (struct usb2_bus *, uint8_t is_on); +}; + +/* + * The following structure is used to define all the USB pipe + * callbacks. + */ +struct usb2_pipe_methods { + + /* Mandatory USB Device and Host mode callbacks: */ + + void (*open) (struct usb2_xfer *xfer); + void (*close) (struct usb2_xfer *xfer); + + void (*enter) (struct usb2_xfer *xfer); + void (*start) (struct usb2_xfer *xfer); + + /* Optional */ + + uint8_t (*isdone) (struct usb2_xfer *xfer); + void *info; + + /* Flags */ + + uint8_t enter_is_cancelable:1; + uint8_t start_is_cancelable:1; +}; + +/* + * The following structure keeps information about what a hardware USB + * endpoint supports. + */ +struct usb2_hw_ep_profile { + uint16_t max_in_frame_size; /* IN-token direction */ + uint16_t max_out_frame_size; /* OUT-token direction */ + uint8_t is_simplex:1; + uint8_t support_multi_buffer:1; + uint8_t support_bulk:1; + uint8_t support_control:1; + uint8_t support_interrupt:1; + uint8_t support_isochronous:1; + uint8_t support_in:1; /* IN-token is supported */ + uint8_t support_out:1; /* OUT-token is supported */ +}; + +/* + * The following structure is used when trying to allocate hardware + * endpoints for an USB configuration in USB device side mode. + */ +struct usb2_hw_ep_scratch_sub { + const struct usb2_hw_ep_profile *pf; + uint16_t max_frame_size; + uint8_t hw_endpoint_out; + uint8_t hw_endpoint_in; + uint8_t needs_ep_type; + uint8_t needs_in:1; + uint8_t needs_out:1; +}; + +/* + * The following structure is used when trying to allocate hardware + * endpoints for an USB configuration in USB device side mode. + */ +struct usb2_hw_ep_scratch { + struct usb2_hw_ep_scratch_sub ep[USB_EP_MAX]; + struct usb2_hw_ep_scratch_sub *ep_max; + struct usb2_config_descriptor *cd; + struct usb2_device *udev; + struct usb2_bus_methods *methods; + uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; + uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; +}; + +/* + * The following structure is used when generating USB descriptors + * from USB templates. + */ +struct usb2_temp_setup { + void *buf; + uint32_t size; + uint8_t usb2_speed; + uint8_t self_powered; + uint8_t bNumEndpoints; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bConfigurationValue; + usb2_error_t err; +}; + +/* prototypes */ + +void usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb); +uint8_t usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, usb2_bus_mem_cb_t *cb); +void usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb); +uint16_t usb2_isoc_time_expand(struct usb2_bus *bus, uint16_t isoc_time_curr); +uint16_t usb2_fs_isoc_schedule_isoc_time_expand(struct usb2_device *udev, struct usb2_fs_isoc_schedule **pp_start, struct usb2_fs_isoc_schedule **pp_end, uint16_t isoc_time); +uint8_t usb2_fs_isoc_schedule_alloc(struct usb2_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len); + +#endif /* _USB2_CONTROLLER_H_ */ diff --git a/sys/dev/usb2/controller/usb2_pci.h b/sys/dev/usb2/controller/usb2_pci.h new file mode 100644 index 000000000000..9297c298f0ba --- /dev/null +++ b/sys/dev/usb2/controller/usb2_pci.h @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_PCI_H_ +#define _USB2_PCI_H_ + +/* + * We don't want the following files included everywhere, that's why + * they are in a separate file. + */ +#include +#include + +#include + +#endif /* _USB2_PCI_H_ */ diff --git a/sys/dev/usb2/controller/uss820dci.c b/sys/dev/usb2/controller/uss820dci.c new file mode 100644 index 000000000000..cc9f2196d5c8 --- /dev/null +++ b/sys/dev/usb2/controller/uss820dci.c @@ -0,0 +1,2572 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky + * 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 file contains the driver for the USS820 series USB Device + * Controller + * + * NOTE: The datasheet does not document everything! + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uss820dcidebug +#define usb2_config_td_cc uss820dci_config_copy +#define usb2_config_td_softc uss820dci_softc + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define USS820_DCI_BUS2SC(bus) \ + ((struct uss820dci_softc *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((struct uss820dci_softc *)0)->sc_bus)))) + +#define USS820_DCI_PC2SC(pc) \ + USS820_DCI_BUS2SC((pc)->tag_parent->info->bus) + +#if USB_DEBUG +static int uss820dcidebug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uss820dci, CTLFLAG_RW, 0, "USB uss820dci"); +SYSCTL_INT(_hw_usb2_uss820dci, OID_AUTO, debug, CTLFLAG_RW, + &uss820dcidebug, 0, "uss820dci debug level"); +#endif + +#define USS820_DCI_INTR_ENDPT 1 + +/* prototypes */ + +struct usb2_bus_methods uss820dci_bus_methods; +struct usb2_pipe_methods uss820dci_device_bulk_methods; +struct usb2_pipe_methods uss820dci_device_ctrl_methods; +struct usb2_pipe_methods uss820dci_device_intr_methods; +struct usb2_pipe_methods uss820dci_device_isoc_fs_methods; +struct usb2_pipe_methods uss820dci_root_ctrl_methods; +struct usb2_pipe_methods uss820dci_root_intr_methods; + +static uss820dci_cmd_t uss820dci_setup_rx; +static uss820dci_cmd_t uss820dci_data_rx; +static uss820dci_cmd_t uss820dci_data_tx; +static uss820dci_cmd_t uss820dci_data_tx_sync; +static void uss820dci_device_done(struct usb2_xfer *xfer, usb2_error_t error); +static void uss820dci_do_poll(struct usb2_bus *bus); +static void uss820dci_root_ctrl_poll(struct uss820dci_softc *sc); +static void uss820dci_standard_done(struct usb2_xfer *xfer); +static void uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set); +static void uss820dci_update_shared_1(struct uss820dci_softc *sc, uint8_t reg, uint8_t keep_mask, uint8_t set_mask); + +static usb2_sw_transfer_func_t uss820dci_root_intr_done; +static usb2_sw_transfer_func_t uss820dci_root_ctrl_done; +static usb2_config_td_command_t uss820dci_root_ctrl_task; + +/* + * Here is a list of what the USS820D chip can support. The main + * limitation is that the sum of the buffer sizes must be less than + * 1120 bytes. + */ +static const struct usb2_hw_ep_profile + uss820dci_ep_profile[] = { + + [0] = { + .max_in_frame_size = 32, + .max_out_frame_size = 32, + .is_simplex = 0, + .support_control = 1, + }, + [1] = { + .max_in_frame_size = 64, + .max_out_frame_size = 64, + .is_simplex = 0, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_in = 1, + .support_out = 1, + }, + [2] = { + .max_in_frame_size = 8, + .max_out_frame_size = 8, + .is_simplex = 0, + .support_multi_buffer = 1, + .support_bulk = 1, + .support_interrupt = 1, + .support_in = 1, + .support_out = 1, + }, + [3] = { + .max_in_frame_size = 256, + .max_out_frame_size = 256, + .is_simplex = 0, + .support_multi_buffer = 1, + .support_isochronous = 1, + .support_in = 1, + .support_out = 1, + }, +}; + +static void +uss820dci_update_shared_1(struct uss820dci_softc *sc, uint8_t reg, + uint8_t keep_mask, uint8_t set_mask) +{ + uint8_t temp; + + USS820_WRITE_1(sc, USS820_PEND, 1); + temp = USS820_READ_1(sc, reg); + temp &= (keep_mask); + temp |= (set_mask); + USS820_WRITE_1(sc, reg, temp); + USS820_WRITE_1(sc, USS820_PEND, 0); + return; +} + +static void +uss820dci_get_hw_ep_profile(struct usb2_device *udev, + const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) +{ + if (ep_addr == 0) { + *ppf = uss820dci_ep_profile + 0; + } else if (ep_addr < 5) { + *ppf = uss820dci_ep_profile + 1; + } else if (ep_addr < 7) { + *ppf = uss820dci_ep_profile + 2; + } else if (ep_addr == 7) { + *ppf = uss820dci_ep_profile + 3; + } else { + *ppf = NULL; + } + return; +} + +static void +uss820dci_pull_up(struct uss820dci_softc *sc) +{ + uint8_t temp; + + /* pullup D+, if possible */ + + if (!sc->sc_flags.d_pulled_up && + sc->sc_flags.port_powered) { + sc->sc_flags.d_pulled_up = 1; + + DPRINTF("\n"); + + temp = USS820_READ_1(sc, USS820_MCSR); + temp |= USS820_MCSR_DPEN; + USS820_WRITE_1(sc, USS820_MCSR, temp); + } + return; +} + +static void +uss820dci_pull_down(struct uss820dci_softc *sc) +{ + uint8_t temp; + + /* pulldown D+, if possible */ + + if (sc->sc_flags.d_pulled_up) { + sc->sc_flags.d_pulled_up = 0; + + DPRINTF("\n"); + + temp = USS820_READ_1(sc, USS820_MCSR); + temp &= ~USS820_MCSR_DPEN; + USS820_WRITE_1(sc, USS820_MCSR, temp); + } + return; +} + +static void +uss820dci_wakeup_peer(struct uss820dci_softc *sc) +{ + if (!(sc->sc_flags.status_suspend)) { + return; + } + DPRINTFN(0, "not supported\n"); + + return; +} + +static void +uss820dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) +{ + struct uss820dci_softc *sc; + uint8_t temp; + + DPRINTFN(5, "is_on=%u\n", is_on); + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + sc = USS820_DCI_BUS2SC(udev->bus); + + temp = USS820_READ_1(sc, USS820_SCR); + + if (is_on) { + temp |= USS820_SCR_RWUPE; + } else { + temp &= ~USS820_SCR_RWUPE; + } + + USS820_WRITE_1(sc, USS820_SCR, temp); + + return; +} + +static void +uss820dci_set_address(struct uss820dci_softc *sc, uint8_t addr) +{ + DPRINTFN(5, "addr=%d\n", addr); + + USS820_WRITE_1(sc, USS820_FADDR, addr); + + return; +} + +static uint8_t +uss820dci_setup_rx(struct uss820dci_td *td) +{ + struct uss820dci_softc *sc; + struct usb2_device_request req; + uint16_t count; + uint8_t rx_stat; + uint8_t temp; + + /* select the correct endpoint */ + bus_space_write_1(td->io_tag, td->io_hdl, + td->ep_reg, td->ep_index); + + /* read out FIFO status */ + rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_stat_reg); + + /* get pointer to softc */ + sc = USS820_DCI_PC2SC(td->pc); + + DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder); + + if (!(rx_stat & USS820_RXSTAT_RXSETUP)) { + /* abort any ongoing transfer */ + if (!td->did_stall) { + DPRINTFN(5, "stalling\n"); + + /* set stall */ + + uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, + (USS820_EPCON_TXSTL | USS820_EPCON_RXSTL)); + + td->did_stall = 1; + } + goto not_complete; + } + /* clear stall and all I/O */ + uss820dci_update_shared_1(sc, USS820_EPCON, + 0xFF ^ (USS820_EPCON_TXSTL | + USS820_EPCON_RXSTL | + USS820_EPCON_RXIE | + USS820_EPCON_TXOE), 0); + + /* clear end overwrite flag */ + uss820dci_update_shared_1(sc, USS820_RXSTAT, + 0xFF ^ USS820_RXSTAT_EDOVW, 0); + + /* get the packet byte count */ + count = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_count_low_reg); + count |= (bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_count_high_reg) << 8); + count &= 0x3FF; + + /* verify data length */ + if (count != td->remainder) { + DPRINTFN(0, "Invalid SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + if (count != sizeof(req)) { + DPRINTFN(0, "Unsupported SETUP packet " + "length, %d bytes\n", count); + goto not_complete; + } + /* receive data */ + bus_space_read_multi_1(td->io_tag, td->io_hdl, + td->rx_fifo_reg, (void *)&req, sizeof(req)); + + /* read out FIFO status */ + rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_stat_reg); + + if (rx_stat & (USS820_RXSTAT_EDOVW | + USS820_RXSTAT_STOVW)) { + DPRINTF("new SETUP packet received\n"); + return (1); /* not complete */ + } + /* clear receive setup bit */ + uss820dci_update_shared_1(sc, USS820_RXSTAT, + 0xFF ^ (USS820_RXSTAT_RXSETUP | + USS820_RXSTAT_EDOVW | + USS820_RXSTAT_STOVW), 0); + + /* set RXFFRC bit */ + temp = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_cntl_reg); + temp |= USS820_RXCON_RXFFRC; + bus_space_write_1(td->io_tag, td->io_hdl, + td->rx_cntl_reg, temp); + + /* copy data into real buffer */ + usb2_copy_in(td->pc, 0, &req, sizeof(req)); + + td->offset = sizeof(req); + td->remainder = 0; + + /* sneak peek the set address */ + if ((req.bmRequestType == UT_WRITE_DEVICE) && + (req.bRequest == UR_SET_ADDRESS)) { + sc->sc_dv_addr = req.wValue[0] & 0x7F; + } else { + sc->sc_dv_addr = 0xFF; + } + return (0); /* complete */ + +not_complete: + /* clear end overwrite flag, if any */ + if (rx_stat & USS820_RXSTAT_RXSETUP) { + uss820dci_update_shared_1(sc, USS820_RXSTAT, + 0xFF ^ (USS820_RXSTAT_EDOVW | + USS820_RXSTAT_STOVW | + USS820_RXSTAT_RXSETUP), 0); + } + return (1); /* not complete */ + +} + +static uint8_t +uss820dci_data_rx(struct uss820dci_td *td) +{ + struct usb2_page_search buf_res; + uint16_t count; + uint8_t rx_flag; + uint8_t rx_stat; + uint8_t rx_cntl; + uint8_t to; + uint8_t got_short; + + to = 2; /* don't loop forever! */ + got_short = 0; + + /* select the correct endpoint */ + bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index); + + /* check if any of the FIFO banks have data */ +repeat: + /* read out FIFO flag */ + rx_flag = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_flag_reg); + /* read out FIFO status */ + rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_stat_reg); + + DPRINTFN(5, "rx_stat=0x%02x rx_flag=0x%02x rem=%u\n", + rx_stat, rx_flag, td->remainder); + + if (rx_stat & (USS820_RXSTAT_RXSETUP | + USS820_RXSTAT_RXSOVW | + USS820_RXSTAT_EDOVW)) { + if (td->remainder == 0) { + /* + * We are actually complete and have + * received the next SETUP + */ + DPRINTFN(5, "faking complete\n"); + return (0); /* complete */ + } + /* + * USB Host Aborted the transfer. + */ + td->error = 1; + return (0); /* complete */ + } + /* check for errors */ + if (rx_flag & (USS820_RXFLG_RXOVF | + USS820_RXFLG_RXURF)) { + DPRINTFN(5, "overflow or underflow\n"); + /* should not happen */ + td->error = 1; + return (0); /* complete */ + } + /* check status */ + if (!(rx_flag & (USS820_RXFLG_RXFIF0 | + USS820_RXFLG_RXFIF1))) { + + /* read out EPCON register */ + /* enable RX input */ + if (!td->did_stall) { + uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc), + USS820_EPCON, 0xFF, USS820_EPCON_RXIE); + td->did_stall = 1; + } + return (1); /* not complete */ + } + /* get the packet byte count */ + count = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_count_low_reg); + + count |= (bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_count_high_reg) << 8); + count &= 0x3FF; + + DPRINTFN(5, "count=0x%04x\n", count); + + /* verify the packet byte count */ + if (count != td->max_packet_size) { + if (count < td->max_packet_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + return (0); /* we are complete */ + } + while (count > 0) { + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* receive data */ + bus_space_read_multi_1(td->io_tag, td->io_hdl, + td->rx_fifo_reg, buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* set RXFFRC bit */ + rx_cntl = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_cntl_reg); + rx_cntl |= USS820_RXCON_RXFFRC; + bus_space_write_1(td->io_tag, td->io_hdl, + td->rx_cntl_reg, rx_cntl); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + return (0); + } + /* else need to receive a zero length packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +uss820dci_data_tx(struct uss820dci_td *td) +{ + struct usb2_page_search buf_res; + uint16_t count; + uint16_t count_copy; + uint8_t rx_stat; + uint8_t tx_flag; + uint8_t to; + + /* select the correct endpoint */ + bus_space_write_1(td->io_tag, td->io_hdl, + td->ep_reg, td->ep_index); + + to = 2; /* don't loop forever! */ + +repeat: + /* read out TX FIFO flags */ + tx_flag = bus_space_read_1(td->io_tag, td->io_hdl, + td->tx_flag_reg); + + /* read out RX FIFO status last */ + rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_stat_reg); + + DPRINTFN(5, "rx_stat=0x%02x tx_flag=0x%02x rem=%u\n", + rx_stat, tx_flag, td->remainder); + + if (rx_stat & (USS820_RXSTAT_RXSETUP | + USS820_RXSTAT_RXSOVW | + USS820_RXSTAT_EDOVW)) { + /* + * The current transfer was aborted + * by the USB Host + */ + td->error = 1; + return (0); /* complete */ + } + if (tx_flag & (USS820_TXFLG_TXOVF | + USS820_TXFLG_TXURF)) { + td->error = 1; + return (0); /* complete */ + } + if (tx_flag & USS820_TXFLG_TXFIF0) { + if (tx_flag & USS820_TXFLG_TXFIF1) { + return (1); /* not complete */ + } + } + if ((!td->support_multi_buffer) && + (tx_flag & (USS820_TXFLG_TXFIF0 | + USS820_TXFLG_TXFIF1))) { + return (1); /* not complete */ + } + count = td->max_packet_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + count_copy = count; + while (count > 0) { + + usb2_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* transmit data */ + bus_space_write_multi_1(td->io_tag, td->io_hdl, + td->tx_fifo_reg, buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* post-write high packet byte count first */ + bus_space_write_1(td->io_tag, td->io_hdl, + td->tx_count_high_reg, count_copy >> 8); + + /* post-write low packet byte count last */ + bus_space_write_1(td->io_tag, td->io_hdl, + td->tx_count_low_reg, count_copy); + + /* + * Enable TX output, which must happen after that we have written + * data into the FIFO. This is undocumented. + */ + if (!td->did_stall) { + uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc), + USS820_EPCON, 0xFF, USS820_EPCON_TXOE); + td->did_stall = 1; + } + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + if (--to) { + goto repeat; + } + return (1); /* not complete */ +} + +static uint8_t +uss820dci_data_tx_sync(struct uss820dci_td *td) +{ + struct uss820dci_softc *sc; + uint8_t rx_stat; + uint8_t tx_flag; + + /* select the correct endpoint */ + bus_space_write_1(td->io_tag, td->io_hdl, + td->ep_reg, td->ep_index); + + /* read out TX FIFO flag */ + tx_flag = bus_space_read_1(td->io_tag, td->io_hdl, + td->tx_flag_reg); + + /* read out RX FIFO status last */ + rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, + td->rx_stat_reg); + + DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder); + + if (rx_stat & (USS820_RXSTAT_RXSETUP | + USS820_RXSTAT_RXSOVW | + USS820_RXSTAT_EDOVW)) { + DPRINTFN(5, "faking complete\n"); + /* Race condition */ + return (0); /* complete */ + } + DPRINTFN(5, "tx_flag=0x%02x rem=%u\n", + tx_flag, td->remainder); + + if (tx_flag & (USS820_TXFLG_TXOVF | + USS820_TXFLG_TXURF)) { + td->error = 1; + return (0); /* complete */ + } + if (tx_flag & (USS820_TXFLG_TXFIF0 | + USS820_TXFLG_TXFIF1)) { + return (1); /* not complete */ + } + sc = USS820_DCI_PC2SC(td->pc); + if (sc->sc_dv_addr != 0xFF) { + /* write function address */ + uss820dci_set_address(sc, sc->sc_dv_addr); + } + return (0); /* complete */ +} + +static uint8_t +uss820dci_xfer_do_fifo(struct usb2_xfer *xfer) +{ + struct uss820dci_td *td; + + DPRINTFN(9, "\n"); + + td = xfer->td_transfer_cache; + while (1) { + if ((td->func) (td)) { + /* operation in progress */ + break; + } + if (((void *)td) == xfer->td_transfer_last) { + goto done; + } + if (td->error) { + goto done; + } else if (td->remainder > 0) { + /* + * We had a short transfer. If there is no alternate + * next, stop processing ! + */ + if (!td->alt_next) { + goto done; + } + } + /* + * Fetch the next transfer descriptor. + */ + td = td->obj_next; + xfer->td_transfer_cache = td; + } + return (1); /* not complete */ + +done: + /* compute all actual lengths */ + + uss820dci_standard_done(xfer); + + return (0); /* complete */ +} + +static void +uss820dci_interrupt_poll(struct uss820dci_softc *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!uss820dci_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + return; +} + +static void +uss820dci_wait_suspend(struct uss820dci_softc *sc, uint8_t on) +{ + uint8_t scr; + uint8_t scratch; + + scr = USS820_READ_1(sc, USS820_SCR); + scratch = USS820_READ_1(sc, USS820_SCRATCH); + + if (on) { + scr |= USS820_SCR_IE_SUSP; + scratch &= ~USS820_SCRATCH_IE_RESUME; + } else { + scr &= ~USS820_SCR_IE_SUSP; + scratch |= USS820_SCRATCH_IE_RESUME; + } + + USS820_WRITE_1(sc, USS820_SCR, scr); + USS820_WRITE_1(sc, USS820_SCRATCH, scratch); + return; +} + +void +uss820dci_interrupt(struct uss820dci_softc *sc) +{ + uint8_t ssr; + uint8_t event; + + mtx_lock(&sc->sc_bus.mtx); + + ssr = USS820_READ_1(sc, USS820_SSR); + + ssr &= (USS820_SSR_SUSPEND | + USS820_SSR_RESUME | + USS820_SSR_RESET); + + /* acknowledge all interrupts */ + + uss820dci_update_shared_1(sc, USS820_SSR, 0, 0); + + /* check for any bus state change interrupts */ + + if (ssr) { + + event = 0; + + if (ssr & USS820_SSR_RESET) { + sc->sc_flags.status_bus_reset = 1; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + /* disable resume interrupt */ + uss820dci_wait_suspend(sc, 1); + + event = 1; + } + /* + * If "RESUME" and "SUSPEND" is set at the same time + * we interpret that like "RESUME". Resume is set when + * there is at least 3 milliseconds of inactivity on + * the USB BUS. + */ + if (ssr & USS820_SSR_RESUME) { + if (sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 1; + /* disable resume interrupt */ + uss820dci_wait_suspend(sc, 1); + event = 1; + } + } else if (ssr & USS820_SSR_SUSPEND) { + if (!sc->sc_flags.status_suspend) { + sc->sc_flags.status_suspend = 1; + sc->sc_flags.change_suspend = 1; + /* enable resume interrupt */ + uss820dci_wait_suspend(sc, 0); + event = 1; + } + } + if (event) { + + DPRINTF("real bus interrupt 0x%02x\n", ssr); + + /* complete root HUB interrupt endpoint */ + + usb2_sw_transfer(&sc->sc_root_intr, + &uss820dci_root_intr_done); + } + } + /* acknowledge all SBI interrupts */ + uss820dci_update_shared_1(sc, USS820_SBI, 0, 0); + + /* acknowledge all SBI1 interrupts */ + uss820dci_update_shared_1(sc, USS820_SBI1, 0, 0); + + /* poll all active transfers */ + uss820dci_interrupt_poll(sc); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +uss820dci_setup_standard_chain_sub(struct uss820_std_temp *temp) +{ + struct uss820dci_td *td; + + /* get current Transfer Descriptor */ + td = temp->td_next; + temp->td = td; + + /* prepare for next TD */ + temp->td_next = td->obj_next; + + /* fill out the Transfer Descriptor */ + td->func = temp->func; + td->pc = temp->pc; + td->offset = temp->offset; + td->remainder = temp->len; + td->error = 0; + td->did_stall = 0; + td->short_pkt = temp->short_pkt; + td->alt_next = temp->setup_alt_next; + return; +} + +static void +uss820dci_setup_standard_chain(struct usb2_xfer *xfer) +{ + struct uss820_std_temp temp; + struct uss820dci_softc *sc; + struct uss820dci_td *td; + uint32_t x; + uint8_t ep_no; + + DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", + xfer->address, UE_GET_ADDR(xfer->endpoint), + xfer->sumlen, usb2_get_speed(xfer->udev)); + + temp.max_frame_size = xfer->max_frame_size; + + td = xfer->td_start[0]; + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; + + /* setup temp */ + + temp.td = NULL; + temp.td_next = xfer->td_start[0]; + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + temp.offset = 0; + + sc = xfer->usb2_sc; + ep_no = (xfer->endpoint & UE_ADDR); + + /* check if we should prepend a setup message */ + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + + temp.func = &uss820dci_setup_rx; + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.short_pkt = temp.len ? 1 : 0; + + uss820dci_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + if (x != xfer->nframes) { + if (xfer->endpoint & UE_DIR_IN) { + temp.func = &uss820dci_data_tx; + } else { + temp.func = &uss820dci_data_rx; + } + + /* setup "pc" pointer */ + temp.pc = xfer->frbuffers + x; + } + while (x != xfer->nframes) { + + /* DATA0 / DATA1 message */ + + temp.len = xfer->frlengths[x]; + + x++; + + if (x == xfer->nframes) { + temp.setup_alt_next = 0; + } + if (temp.len == 0) { + + /* make sure that we send an USB packet */ + + temp.short_pkt = 0; + + } else { + + /* regular data transfer */ + + temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; + } + + uss820dci_setup_standard_chain_sub(&temp); + + if (xfer->flags_int.isochronous_xfr) { + temp.offset += temp.len; + } else { + /* get next Page Cache pointer */ + temp.pc = xfer->frbuffers + x; + } + } + + /* always setup a valid "pc" pointer for status and sync */ + temp.pc = xfer->frbuffers + 0; + + /* check if we should append a status stage */ + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + uint8_t need_sync; + + /* + * Send a DATA1 message and invert the current + * endpoint direction. + */ + if (xfer->endpoint & UE_DIR_IN) { + temp.func = &uss820dci_data_rx; + need_sync = 0; + } else { + temp.func = &uss820dci_data_tx; + need_sync = 1; + } + temp.len = 0; + temp.short_pkt = 0; + + uss820dci_setup_standard_chain_sub(&temp); + if (need_sync) { + /* we need a SYNC point after TX */ + temp.func = &uss820dci_data_tx_sync; + temp.len = 0; + temp.short_pkt = 0; + + uss820dci_setup_standard_chain_sub(&temp); + } + } + /* must have at least one frame! */ + td = temp.td; + xfer->td_transfer_last = td; + + return; +} + +static void +uss820dci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + struct uss820dci_softc *sc = xfer->usb2_sc; + + DPRINTF("xfer=%p\n", xfer); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + /* transfer is transferred */ + uss820dci_device_done(xfer, USB_ERR_TIMEOUT); + + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +static void +uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + uint8_t ep_no = (xfer->endpoint & UE_ADDR); + uint8_t ep_reg; + uint8_t temp; + + DPRINTFN(15, "endpoint 0x%02x\n", xfer->endpoint); + + if (ep_no > 3) { + ep_reg = USS820_SBIE1; + } else { + ep_reg = USS820_SBIE; + } + + ep_no &= 3; + ep_no = 1 << (2 * ep_no); + + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { + ep_no <<= 1; /* RX interrupt only */ + } else { + ep_no |= (ep_no << 1); /* RX and TX interrupt */ + } + } else { + if (!(xfer->endpoint & UE_DIR_IN)) { + ep_no <<= 1; + } + } + temp = USS820_READ_1(sc, ep_reg); + if (set) { + temp |= ep_no; + } else { + temp &= ~ep_no; + } + USS820_WRITE_1(sc, ep_reg, temp); + return; +} + +static void +uss820dci_start_standard_chain(struct usb2_xfer *xfer) +{ + DPRINTFN(9, "\n"); + + /* poll one time */ + if (uss820dci_xfer_do_fifo(xfer)) { + + /* + * Only enable the endpoint interrupt when we are + * actually waiting for data, hence we are dealing + * with level triggered interrupts ! + */ + uss820dci_intr_set(xfer, 1); + + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &uss820dci_timeout, xfer->timeout); + } + } + return; +} + +static void +uss820dci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + + DPRINTFN(9, "\n"); + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + uss820dci_device_done(xfer, std->err); + } + goto done; + } + /* setup buffer */ + std->ptr = sc->sc_hub_idata; + std->len = sizeof(sc->sc_hub_idata); + + /* set port bit */ + sc->sc_hub_idata[0] = 0x02; /* we only have one port */ + +done: + return; +} + +static usb2_error_t +uss820dci_standard_done_sub(struct usb2_xfer *xfer) +{ + struct uss820dci_td *td; + uint32_t len; + uint8_t error; + + DPRINTFN(9, "\n"); + + td = xfer->td_transfer_cache; + + do { + len = td->remainder; + + if (xfer->aframes != xfer->nframes) { + /* + * Verify the length and subtract + * the remainder from "frlengths[]": + */ + if (len > xfer->frlengths[xfer->aframes]) { + td->error = 1; + } else { + xfer->frlengths[xfer->aframes] -= len; + } + } + /* Check for transfer error */ + if (td->error) { + /* the transfer is finished */ + error = 1; + td = NULL; + break; + } + /* Check for short transfer */ + if (len > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + td = td->obj_next; + } else { + td = NULL; + } + } else { + /* the transfer is finished */ + td = NULL; + } + error = 0; + break; + } + td = td->obj_next; + + /* this USB frame is complete */ + error = 0; + break; + + } while (0); + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + return (error ? + USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); +} + +static void +uss820dci_standard_done(struct usb2_xfer *xfer) +{ + usb2_error_t err = 0; + + DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", + xfer, xfer->pipe); + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { + + err = uss820dci_standard_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = uss820dci_standard_done_sub(xfer); + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + err = uss820dci_standard_done_sub(xfer); + } +done: + uss820dci_device_done(xfer, err); + return; +} + +/*------------------------------------------------------------------------* + * uss820dci_device_done + * + * NOTE: this function can be called more than one time on the + * same USB transfer! + *------------------------------------------------------------------------*/ +static void +uss820dci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", + xfer, xfer->pipe, error); + + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + uss820dci_intr_set(xfer, 0); + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); + return; +} + +static void +uss820dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, + struct usb2_pipe *pipe) +{ + struct uss820dci_softc *sc; + uint8_t ep_no; + uint8_t ep_type; + uint8_t ep_dir; + uint8_t temp; + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + DPRINTFN(5, "pipe=%p\n", pipe); + + if (xfer) { + /* cancel any ongoing transfers */ + uss820dci_device_done(xfer, USB_ERR_STALLED); + } + /* set FORCESTALL */ + sc = USS820_DCI_BUS2SC(udev->bus); + ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); + ep_dir = (pipe->edesc->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)); + ep_type = (pipe->edesc->bmAttributes & UE_XFERTYPE); + + if (ep_type == UE_CONTROL) { + /* should not happen */ + return; + } + USS820_WRITE_1(sc, USS820_EPINDEX, ep_no); + + if (ep_dir == UE_DIR_IN) { + temp = USS820_EPCON_TXSTL; + } else { + temp = USS820_EPCON_RXSTL; + } + uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp); + return; +} + +static void +uss820dci_clear_stall_sub(struct uss820dci_softc *sc, + uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) +{ + uint8_t temp; + + if (ep_type == UE_CONTROL) { + /* clearing stall is not needed */ + return; + } + /* select endpoint index */ + USS820_WRITE_1(sc, USS820_EPINDEX, ep_no); + + /* clear stall and disable I/O transfers */ + if (ep_dir == UE_DIR_IN) { + temp = 0xFF ^ (USS820_EPCON_TXOE | + USS820_EPCON_TXSTL); + } else { + temp = 0xFF ^ (USS820_EPCON_RXIE | + USS820_EPCON_RXSTL); + } + uss820dci_update_shared_1(sc, USS820_EPCON, temp, 0); + + if (ep_dir == UE_DIR_IN) { + /* reset data toggle */ + USS820_WRITE_1(sc, USS820_TXSTAT, + USS820_TXSTAT_TXSOVW); + + /* reset FIFO */ + temp = USS820_READ_1(sc, USS820_TXCON); + temp |= USS820_TXCON_TXCLR; + USS820_WRITE_1(sc, USS820_TXCON, temp); + temp &= ~USS820_TXCON_TXCLR; + USS820_WRITE_1(sc, USS820_TXCON, temp); + } else { + + /* reset data toggle */ + uss820dci_update_shared_1(sc, USS820_RXSTAT, + 0, USS820_RXSTAT_RXSOVW); + + /* reset FIFO */ + temp = USS820_READ_1(sc, USS820_RXCON); + temp |= USS820_RXCON_RXCLR; + temp &= ~USS820_RXCON_RXFFRC; + USS820_WRITE_1(sc, USS820_RXCON, temp); + temp &= ~USS820_RXCON_RXCLR; + USS820_WRITE_1(sc, USS820_RXCON, temp); + } + return; +} + +static void +uss820dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + struct uss820dci_softc *sc; + struct usb2_endpoint_descriptor *ed; + + mtx_assert(&udev->bus->mtx, MA_OWNED); + + DPRINTFN(5, "pipe=%p\n", pipe); + + /* check mode */ + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + /* get softc */ + sc = USS820_DCI_BUS2SC(udev->bus); + + /* get endpoint descriptor */ + ed = pipe->edesc; + + /* reset endpoint */ + uss820dci_clear_stall_sub(sc, + (ed->bEndpointAddress & UE_ADDR), + (ed->bmAttributes & UE_XFERTYPE), + (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); + + return; +} + +usb2_error_t +uss820dci_init(struct uss820dci_softc *sc) +{ + const struct usb2_hw_ep_profile *pf; + uint8_t n; + uint8_t temp; + + DPRINTF("start\n"); + + /* set up the bus structure */ + sc->sc_bus.usbrev = USB_REV_1_1; + sc->sc_bus.methods = &uss820dci_bus_methods; + + mtx_lock(&sc->sc_bus.mtx); + + /* we always have VBUS */ + sc->sc_flags.status_vbus = 1; + + /* reset the chip */ + USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_SRESET); + DELAY(100); + USS820_WRITE_1(sc, USS820_SCR, 0); + + /* wait for reset to complete */ + for (n = 0;; n++) { + + temp = USS820_READ_1(sc, USS820_MCSR); + + if (temp & USS820_MCSR_INIT) { + break; + } + if (n == 100) { + mtx_unlock(&sc->sc_bus.mtx); + return (USB_ERR_INVAL); + } + /* wait a little for things to stabilise */ + DELAY(100); + } + + /* do a pulldown */ + uss820dci_pull_down(sc); + + /* wait 10ms for pulldown to stabilise */ + usb2_pause_mtx(&sc->sc_bus.mtx, 10); + + /* check hardware revision */ + temp = USS820_READ_1(sc, USS820_REV); + + if (temp < 0x13) { + mtx_unlock(&sc->sc_bus.mtx); + return (USB_ERR_INVAL); + } + /* enable interrupts */ + USS820_WRITE_1(sc, USS820_SCR, + USS820_SCR_T_IRQ | + USS820_SCR_IE_RESET | + USS820_SCR_IE_SUSP | + USS820_SCR_IRQPOL); + + /* enable interrupts */ + USS820_WRITE_1(sc, USS820_SCRATCH, + USS820_SCRATCH_IE_RESUME); + + /* enable features */ + USS820_WRITE_1(sc, USS820_MCSR, + USS820_MCSR_BDFEAT | + USS820_MCSR_FEAT); + + sc->sc_flags.mcsr_feat = 1; + + /* disable interrupts */ + USS820_WRITE_1(sc, USS820_SBIE, 0); + + /* disable interrupts */ + USS820_WRITE_1(sc, USS820_SBIE1, 0); + + /* disable all endpoints */ + for (n = 0; n != USS820_EP_MAX; n++) { + + /* select endpoint */ + USS820_WRITE_1(sc, USS820_EPINDEX, n); + + /* disable endpoint */ + uss820dci_update_shared_1(sc, USS820_EPCON, 0, 0); + } + + /* + * Initialise default values for some registers that cannot be + * changed during operation! + */ + for (n = 0; n != USS820_EP_MAX; n++) { + + uss820dci_get_hw_ep_profile(NULL, &pf, n); + + /* the maximum frame sizes should be the same */ + if (pf->max_in_frame_size != pf->max_out_frame_size) { + DPRINTF("Max frame size mismatch %u != %u\n", + pf->max_in_frame_size, pf->max_out_frame_size); + } + if (pf->support_isochronous) { + if (pf->max_in_frame_size <= 64) { + temp = (USS820_TXCON_FFSZ_16_64 | + USS820_TXCON_TXISO | + USS820_TXCON_ATM); + } else if (pf->max_in_frame_size <= 256) { + temp = (USS820_TXCON_FFSZ_64_256 | + USS820_TXCON_TXISO | + USS820_TXCON_ATM); + } else if (pf->max_in_frame_size <= 512) { + temp = (USS820_TXCON_FFSZ_8_512 | + USS820_TXCON_TXISO | + USS820_TXCON_ATM); + } else { /* 1024 bytes */ + temp = (USS820_TXCON_FFSZ_32_1024 | + USS820_TXCON_TXISO | + USS820_TXCON_ATM); + } + } else { + if ((pf->max_in_frame_size <= 8) && + (sc->sc_flags.mcsr_feat)) { + temp = (USS820_TXCON_FFSZ_8_512 | + USS820_TXCON_ATM); + } else if (pf->max_in_frame_size <= 16) { + temp = (USS820_TXCON_FFSZ_16_64 | + USS820_TXCON_ATM); + } else if ((pf->max_in_frame_size <= 32) && + (sc->sc_flags.mcsr_feat)) { + temp = (USS820_TXCON_FFSZ_32_1024 | + USS820_TXCON_ATM); + } else { /* 64 bytes */ + temp = (USS820_TXCON_FFSZ_64_256 | + USS820_TXCON_ATM); + } + } + + /* need to configure the chip early */ + + USS820_WRITE_1(sc, USS820_EPINDEX, n); + USS820_WRITE_1(sc, USS820_TXCON, temp); + USS820_WRITE_1(sc, USS820_RXCON, temp); + + if (pf->support_control) { + temp = USS820_EPCON_CTLEP | + USS820_EPCON_RXSPM | + USS820_EPCON_RXIE | + USS820_EPCON_RXEPEN | + USS820_EPCON_TXOE | + USS820_EPCON_TXEPEN; + } else { + temp = USS820_EPCON_RXEPEN | USS820_EPCON_TXEPEN; + } + + uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp); + } + + mtx_unlock(&sc->sc_bus.mtx); + + /* catch any lost interrupts */ + + uss820dci_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +uss820dci_uninit(struct uss820dci_softc *sc) +{ + uint8_t temp; + + mtx_lock(&sc->sc_bus.mtx); + + /* disable all interrupts */ + temp = USS820_READ_1(sc, USS820_SCR); + temp &= ~USS820_SCR_T_IRQ; + USS820_WRITE_1(sc, USS820_SCR, temp); + + sc->sc_flags.port_powered = 0; + sc->sc_flags.status_vbus = 0; + sc->sc_flags.status_bus_reset = 0; + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 0; + sc->sc_flags.change_connect = 1; + + uss820dci_pull_down(sc); + mtx_unlock(&sc->sc_bus.mtx); + + return; +} + +void +uss820dci_suspend(struct uss820dci_softc *sc) +{ + return; +} + +void +uss820dci_resume(struct uss820dci_softc *sc) +{ + return; +} + +static void +uss820dci_do_poll(struct usb2_bus *bus) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(bus); + + mtx_lock(&sc->sc_bus.mtx); + uss820dci_interrupt_poll(sc); + uss820dci_root_ctrl_poll(sc); + mtx_unlock(&sc->sc_bus.mtx); + return; +} + +/*------------------------------------------------------------------------* + * at91dci bulk support + *------------------------------------------------------------------------*/ +static void +uss820dci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_bulk_close(struct usb2_xfer *xfer) +{ + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uss820dci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_bulk_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + uss820dci_setup_standard_chain(xfer); + uss820dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods uss820dci_device_bulk_methods = +{ + .open = uss820dci_device_bulk_open, + .close = uss820dci_device_bulk_close, + .enter = uss820dci_device_bulk_enter, + .start = uss820dci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci control support + *------------------------------------------------------------------------*/ +static void +uss820dci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_ctrl_close(struct usb2_xfer *xfer) +{ + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uss820dci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_ctrl_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + uss820dci_setup_standard_chain(xfer); + uss820dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods uss820dci_device_ctrl_methods = +{ + .open = uss820dci_device_ctrl_open, + .close = uss820dci_device_ctrl_close, + .enter = uss820dci_device_ctrl_enter, + .start = uss820dci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci interrupt support + *------------------------------------------------------------------------*/ +static void +uss820dci_device_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_intr_close(struct usb2_xfer *xfer) +{ + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uss820dci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_intr_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + uss820dci_setup_standard_chain(xfer); + uss820dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods uss820dci_device_intr_methods = +{ + .open = uss820dci_device_intr_open, + .close = uss820dci_device_intr_close, + .enter = uss820dci_device_intr_enter, + .start = uss820dci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci full speed isochronous support + *------------------------------------------------------------------------*/ +static void +uss820dci_device_isoc_fs_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_device_isoc_fs_close(struct usb2_xfer *xfer) +{ + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uss820dci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + uint32_t temp; + uint32_t nframes; + + DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", + xfer, xfer->pipe->isoc_next, xfer->nframes); + + /* get the current frame index - we don't need the high bits */ + + nframes = USS820_READ_1(sc, USS820_SOFL); + + /* + * check if the frame index is within the window where the + * frames will be inserted + */ + temp = (nframes - xfer->pipe->isoc_next) & USS820_SOFL_MASK; + + if ((xfer->pipe->is_synced == 0) || + (temp < xfer->nframes)) { + /* + * If there is data underflow or the pipe queue is + * empty we schedule the transfer a few frames ahead + * of the current frame position. Else two isochronous + * transfers might overlap. + */ + xfer->pipe->isoc_next = (nframes + 3) & USS820_SOFL_MASK; + xfer->pipe->is_synced = 1; + DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); + } + /* + * compute how many milliseconds the insertion is ahead of the + * current frame position: + */ + temp = (xfer->pipe->isoc_next - nframes) & USS820_SOFL_MASK; + + /* + * pre-compute when the isochronous transfer will be finished: + */ + xfer->isoc_time_complete = + usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + + xfer->nframes; + + /* compute frame number for next insertion */ + xfer->pipe->isoc_next += xfer->nframes; + + /* setup TDs */ + uss820dci_setup_standard_chain(xfer); + return; +} + +static void +uss820dci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + uss820dci_start_standard_chain(xfer); + return; +} + +struct usb2_pipe_methods uss820dci_device_isoc_fs_methods = +{ + .open = uss820dci_device_isoc_fs_open, + .close = uss820dci_device_isoc_fs_close, + .enter = uss820dci_device_isoc_fs_enter, + .start = uss820dci_device_isoc_fs_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci root control support + *------------------------------------------------------------------------* + * simulate a hardware HUB by handling + * all the necessary requests + *------------------------------------------------------------------------*/ + +static void +uss820dci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_root_ctrl_close(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +/* + * USB descriptors for the virtual Root HUB: + */ + +static const struct usb2_device_descriptor uss820dci_devd = { + .bLength = sizeof(struct usb2_device_descriptor), + .bDescriptorType = UDESC_DEVICE, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_HSHUBSTT, + .bMaxPacketSize = 64, + .bcdDevice = {0x00, 0x01}, + .iManufacturer = 1, + .iProduct = 2, + .bNumConfigurations = 1, +}; + +static const struct usb2_device_qualifier uss820dci_odevd = { + .bLength = sizeof(struct usb2_device_qualifier), + .bDescriptorType = UDESC_DEVICE_QUALIFIER, + .bcdUSB = {0x00, 0x02}, + .bDeviceClass = UDCLASS_HUB, + .bDeviceSubClass = UDSUBCLASS_HUB, + .bDeviceProtocol = UDPROTO_FSHUB, + .bMaxPacketSize0 = 0, + .bNumConfigurations = 0, +}; + +static const struct uss820dci_config_desc uss820dci_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(uss820dci_confd), + .bNumInterface = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = UC_SELF_POWERED, + .bMaxPower = 0, + }, + .ifcd = { + .bLength = sizeof(struct usb2_interface_descriptor), + .bDescriptorType = UDESC_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = UICLASS_HUB, + .bInterfaceSubClass = UISUBCLASS_HUB, + .bInterfaceProtocol = UIPROTO_HSHUBSTT, + }, + + .endpd = { + .bLength = sizeof(struct usb2_endpoint_descriptor), + .bDescriptorType = UDESC_ENDPOINT, + .bEndpointAddress = (UE_DIR_IN | USS820_DCI_INTR_ENDPT), + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, + .bInterval = 255, + }, +}; + +static const struct usb2_hub_descriptor_min uss820dci_hubd = { + .bDescLength = sizeof(uss820dci_hubd), + .bDescriptorType = UDESC_HUB, + .bNbrPorts = 1, + .wHubCharacteristics[0] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, + .wHubCharacteristics[1] = + (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, + .bPwrOn2PwrGood = 50, + .bHubContrCurrent = 0, + .DeviceRemovable = {0}, /* port is removable */ +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_VENDOR \ + 'A', 0, 'G', 0, 'E', 0, 'R', 0, 'E', 0 + +#define STRING_PRODUCT \ + 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \ + 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ + 'U', 0, 'B', 0, + +USB_MAKE_STRING_DESC(STRING_LANG, uss820dci_langtab); +USB_MAKE_STRING_DESC(STRING_VENDOR, uss820dci_vendor); +USB_MAKE_STRING_DESC(STRING_PRODUCT, uss820dci_product); + +static void +uss820dci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_root_ctrl_start(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + + sc->sc_root_ctrl.xfer = xfer; + + usb2_config_td_queue_command( + &sc->sc_config_td, NULL, &uss820dci_root_ctrl_task, 0, 0); + + return; +} + +static void +uss820dci_root_ctrl_task(struct uss820dci_softc *sc, + struct uss820dci_config_copy *cc, uint16_t refcount) +{ + uss820dci_root_ctrl_poll(sc); + return; +} + +static void +uss820dci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + uint16_t value; + uint16_t index; + uint8_t use_polling; + + mtx_assert(&sc->sc_bus.mtx, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + uss820dci_device_done(xfer, std->err); + } + goto done; + } + /* buffer reset */ + std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); + std->len = 0; + + value = UGETW(std->req.wValue); + index = UGETW(std->req.wIndex); + + use_polling = mtx_owned(xfer->priv_mtx) ? 1 : 0; + + /* demultiplex the control request */ + + switch (std->req.bmRequestType) { + case UT_READ_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_descriptor; + case UR_GET_CONFIG: + goto tr_handle_get_config; + case UR_GET_STATUS: + goto tr_handle_get_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_DEVICE: + switch (std->req.bRequest) { + case UR_SET_ADDRESS: + goto tr_handle_set_address; + case UR_SET_CONFIG: + goto tr_handle_set_config; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_DESCRIPTOR: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_WRITE_ENDPOINT: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_clear_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_clear_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SET_FEATURE: + switch (UGETW(std->req.wValue)) { + case UF_ENDPOINT_HALT: + goto tr_handle_set_halt; + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_set_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SYNCH_FRAME: + goto tr_valid; /* nop */ + default: + goto tr_stalled; + } + break; + + case UT_READ_ENDPOINT: + switch (std->req.bRequest) { + case UR_GET_STATUS: + goto tr_handle_get_ep_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_INTERFACE: + switch (std->req.bRequest) { + case UR_SET_INTERFACE: + goto tr_handle_set_interface; + case UR_CLEAR_FEATURE: + goto tr_valid; /* nop */ + case UR_SET_FEATURE: + default: + goto tr_stalled; + } + break; + + case UT_READ_INTERFACE: + switch (std->req.bRequest) { + case UR_GET_INTERFACE: + goto tr_handle_get_interface; + case UR_GET_STATUS: + goto tr_handle_get_iface_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_INTERFACE: + case UT_WRITE_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_READ_CLASS_INTERFACE: + case UT_READ_VENDOR_INTERFACE: + /* XXX forward */ + break; + + case UT_WRITE_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_valid; + case UR_SET_DESCRIPTOR: + case UR_SET_FEATURE: + break; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_CLEAR_FEATURE: + goto tr_handle_clear_port_feature; + case UR_SET_FEATURE: + goto tr_handle_set_port_feature; + case UR_CLEAR_TT_BUFFER: + case UR_RESET_TT: + case UR_STOP_TT: + goto tr_valid; + + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_OTHER: + switch (std->req.bRequest) { + case UR_GET_TT_STATE: + goto tr_handle_get_tt_state; + case UR_GET_STATUS: + goto tr_handle_get_port_status; + default: + goto tr_stalled; + } + break; + + case UT_READ_CLASS_DEVICE: + switch (std->req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_class_descriptor; + case UR_GET_STATUS: + goto tr_handle_get_class_status; + + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + goto tr_valid; + +tr_handle_get_descriptor: + switch (value >> 8) { + case UDESC_DEVICE: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(uss820dci_devd); + std->ptr = USB_ADD_BYTES(&uss820dci_devd, 0); + goto tr_valid; + case UDESC_CONFIG: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(uss820dci_confd); + std->ptr = USB_ADD_BYTES(&uss820dci_confd, 0); + goto tr_valid; + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + std->len = sizeof(uss820dci_langtab); + std->ptr = USB_ADD_BYTES(&uss820dci_langtab, 0); + goto tr_valid; + + case 1: /* Vendor */ + std->len = sizeof(uss820dci_vendor); + std->ptr = USB_ADD_BYTES(&uss820dci_vendor, 0); + goto tr_valid; + + case 2: /* Product */ + std->len = sizeof(uss820dci_product); + std->ptr = USB_ADD_BYTES(&uss820dci_product, 0); + goto tr_valid; + default: + break; + } + break; + default: + goto tr_stalled; + } + goto tr_stalled; + +tr_handle_get_config: + std->len = 1; + sc->sc_hub_temp.wValue[0] = sc->sc_conf; + goto tr_valid; + +tr_handle_get_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); + goto tr_valid; + +tr_handle_set_address: + if (value & 0xFF00) { + goto tr_stalled; + } + sc->sc_rt_addr = value; + goto tr_valid; + +tr_handle_set_config: + if (value >= 2) { + goto tr_stalled; + } + sc->sc_conf = value; + goto tr_valid; + +tr_handle_get_interface: + std->len = 1; + sc->sc_hub_temp.wValue[0] = 0; + goto tr_valid; + +tr_handle_get_tt_state: +tr_handle_get_class_status: +tr_handle_get_iface_status: +tr_handle_get_ep_status: + std->len = 2; + USETW(sc->sc_hub_temp.wValue, 0); + goto tr_valid; + +tr_handle_set_halt: +tr_handle_set_interface: +tr_handle_set_wakeup: +tr_handle_clear_wakeup: +tr_handle_clear_halt: + goto tr_valid; + +tr_handle_clear_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); + + switch (value) { + case UHF_PORT_SUSPEND: + uss820dci_wakeup_peer(sc); + break; + + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 0; + break; + + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 0; + uss820dci_pull_down(sc); + break; + case UHF_C_PORT_CONNECTION: + sc->sc_flags.change_connect = 0; + break; + case UHF_C_PORT_SUSPEND: + sc->sc_flags.change_suspend = 0; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_set_port_feature: + if (index != 1) { + goto tr_stalled; + } + DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); + + switch (value) { + case UHF_PORT_ENABLE: + sc->sc_flags.port_enabled = 1; + break; + case UHF_PORT_SUSPEND: + case UHF_PORT_RESET: + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: + /* nops */ + break; + case UHF_PORT_POWER: + sc->sc_flags.port_powered = 1; + break; + default: + std->err = USB_ERR_IOERROR; + goto done; + } + goto tr_valid; + +tr_handle_get_port_status: + + DPRINTFN(9, "UR_GET_PORT_STATUS\n"); + + if (index != 1) { + goto tr_stalled; + } + if (sc->sc_flags.status_vbus) { + uss820dci_pull_up(sc); + } else { + uss820dci_pull_down(sc); + } + + /* Select FULL-speed and Device Side Mode */ + + value = UPS_PORT_MODE_DEVICE; + + if (sc->sc_flags.port_powered) { + value |= UPS_PORT_POWER; + } + if (sc->sc_flags.port_enabled) { + value |= UPS_PORT_ENABLED; + } + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + value |= UPS_CURRENT_CONNECT_STATUS; + } + if (sc->sc_flags.status_suspend) { + value |= UPS_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortStatus, value); + + value = 0; + + if (sc->sc_flags.change_connect) { + value |= UPS_C_CONNECT_STATUS; + } + if (sc->sc_flags.change_suspend) { + value |= UPS_C_SUSPEND; + } + USETW(sc->sc_hub_temp.ps.wPortChange, value); + std->len = sizeof(sc->sc_hub_temp.ps); + goto tr_valid; + +tr_handle_get_class_descriptor: + if (value & 0xFF) { + goto tr_stalled; + } + std->ptr = USB_ADD_BYTES(&uss820dci_hubd, 0); + std->len = sizeof(uss820dci_hubd); + goto tr_valid; + +tr_stalled: + std->err = USB_ERR_STALLED; +tr_valid: +done: + return; +} + +static void +uss820dci_root_ctrl_poll(struct uss820dci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &uss820dci_root_ctrl_done); + return; +} + +struct usb2_pipe_methods uss820dci_root_ctrl_methods = +{ + .open = uss820dci_root_ctrl_open, + .close = uss820dci_root_ctrl_close, + .enter = uss820dci_root_ctrl_enter, + .start = uss820dci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * at91dci root interrupt support + *------------------------------------------------------------------------*/ +static void +uss820dci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_root_intr_close(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + uss820dci_device_done(xfer, USB_ERR_CANCELLED); + return; +} + +static void +uss820dci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_root_intr_start(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = xfer->usb2_sc; + + sc->sc_root_intr.xfer = xfer; + return; +} + +struct usb2_pipe_methods uss820dci_root_intr_methods = +{ + .open = uss820dci_root_intr_open, + .close = uss820dci_root_intr_close, + .enter = uss820dci_root_intr_enter, + .start = uss820dci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +uss820dci_xfer_setup(struct usb2_setup_params *parm) +{ + const struct usb2_hw_ep_profile *pf; + struct uss820dci_softc *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t n; + uint8_t ep_no; + + sc = USS820_DCI_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * setup xfer + */ + xfer->usb2_sc = sc; + + /* + * NOTE: This driver does not use any of the parameters that + * are computed from the following values. Just set some + * reasonable dummies: + */ + parm->hc_max_packet_size = 0x500; + parm->hc_max_packet_count = 1; + parm->hc_max_frame_size = 0x500; + + usb2_transfer_setup_sub(parm); + + /* + * compute maximum number of TDs + */ + if (parm->methods == &uss820dci_device_ctrl_methods) { + + ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ; + + } else if (parm->methods == &uss820dci_device_bulk_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &uss820dci_device_intr_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &uss820dci_device_isoc_fs_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else { + + ntd = 0; + } + + /* + * check if "usb2_transfer_setup_sub" set an error + */ + if (parm->err) { + return; + } + /* + * allocate transfer descriptors + */ + last_obj = NULL; + + /* + * get profile stuff + */ + if (ntd) { + + ep_no = xfer->endpoint & UE_ADDR; + uss820dci_get_hw_ep_profile(parm->udev, &pf, ep_no); + + if (pf == NULL) { + /* should not happen */ + parm->err = USB_ERR_INVAL; + return; + } + } else { + ep_no = 0; + pf = NULL; + } + + /* align data */ + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); + + for (n = 0; n != ntd; n++) { + + struct uss820dci_td *td; + + if (parm->buf) { + + td = USB_ADD_BYTES(parm->buf, parm->size[0]); + + /* init TD */ + td->io_tag = sc->sc_io_tag; + td->io_hdl = sc->sc_io_hdl; + td->max_packet_size = xfer->max_packet_size; + td->rx_stat_reg = USS820_GET_REG(sc, USS820_RXSTAT); + td->tx_stat_reg = USS820_GET_REG(sc, USS820_TXSTAT); + td->rx_flag_reg = USS820_GET_REG(sc, USS820_RXFLG); + td->tx_flag_reg = USS820_GET_REG(sc, USS820_TXFLG); + td->rx_fifo_reg = USS820_GET_REG(sc, USS820_RXDAT); + td->tx_fifo_reg = USS820_GET_REG(sc, USS820_TXDAT); + td->rx_count_low_reg = USS820_GET_REG(sc, USS820_RXCNTL); + td->rx_count_high_reg = USS820_GET_REG(sc, USS820_RXCNTH); + td->tx_count_low_reg = USS820_GET_REG(sc, USS820_TXCNTL); + td->tx_count_high_reg = USS820_GET_REG(sc, USS820_TXCNTH); + td->rx_cntl_reg = USS820_GET_REG(sc, USS820_RXCON); + td->tx_cntl_reg = USS820_GET_REG(sc, USS820_TXCON); + td->pend_reg = USS820_GET_REG(sc, USS820_PEND); + td->ep_reg = USS820_GET_REG(sc, USS820_EPINDEX); + td->ep_index = ep_no; + if (pf->support_multi_buffer && + (parm->methods != &uss820dci_device_ctrl_methods)) { + td->support_multi_buffer = 1; + } + td->obj_next = last_obj; + + last_obj = td; + } + parm->size[0] += sizeof(*td); + } + + xfer->td_start[0] = last_obj; + return; +} + +static void +uss820dci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +uss820dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(udev->bus); + + DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", + pipe, udev->address, + edesc->bEndpointAddress, udev->flags.usb2_mode, + sc->sc_rt_addr); + + if (udev->device_index == sc->sc_rt_addr) { + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return; + } + switch (edesc->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &uss820dci_root_ctrl_methods; + break; + case UE_DIR_IN | USS820_DCI_INTR_ENDPT: + pipe->methods = &uss820dci_root_intr_methods; + break; + default: + /* do nothing */ + break; + } + } else { + + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + if (udev->speed != USB_SPEED_FULL) { + /* not supported */ + return; + } + switch (edesc->bmAttributes & UE_XFERTYPE) { + case UE_CONTROL: + pipe->methods = &uss820dci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &uss820dci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + pipe->methods = &uss820dci_device_isoc_fs_methods; + break; + case UE_BULK: + pipe->methods = &uss820dci_device_bulk_methods; + break; + default: + /* do nothing */ + break; + } + } + return; +} + +struct usb2_bus_methods uss820dci_bus_methods = +{ + .pipe_init = &uss820dci_pipe_init, + .xfer_setup = &uss820dci_xfer_setup, + .xfer_unsetup = &uss820dci_xfer_unsetup, + .do_poll = &uss820dci_do_poll, + .get_hw_ep_profile = &uss820dci_get_hw_ep_profile, + .set_stall = &uss820dci_set_stall, + .clear_stall = &uss820dci_clear_stall, + .rem_wakeup_set = &uss820dci_rem_wakeup_set, +}; diff --git a/sys/dev/usb2/controller/uss820dci.h b/sys/dev/usb2/controller/uss820dci.h new file mode 100644 index 000000000000..aa8b535a9fd5 --- /dev/null +++ b/sys/dev/usb2/controller/uss820dci.h @@ -0,0 +1,375 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007 Hans Petter Selasky + * 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. + */ + +#ifndef _USS820_DCI_H_ +#define _USS820_DCI_H_ + +#define USS820_EP_MAX 8 /* maximum number of endpoints */ + +#define USS820_TXDAT 0x00 /* Transmit FIFO data */ + +#define USS820_TXCNTL 0x01 /* Transmit FIFO byte count low */ +#define USS820_TXCNTL_MASK 0xFF + +#define USS820_TXCNTH 0x02 /* Transmit FIFO byte count high */ +#define USS820_TXCNTH_MASK 0x03 +#define USS820_TXCNTH_UNUSED 0xFC + +#define USS820_TXCON 0x03 /* USB transmit FIFO control */ +#define USS820_TXCON_REVRP 0x01 +#define USS820_TXCON_ADVRM 0x02 +#define USS820_TXCON_ATM 0x04 /* Automatic Transmit Management */ +#define USS820_TXCON_TXISO 0x08 /* Transmit Isochronous Data */ +#define USS820_TXCON_UNUSED 0x10 +#define USS820_TXCON_FFSZ_16_64 0x00 +#define USS820_TXCON_FFSZ_64_256 0x20 +#define USS820_TXCON_FFSZ_8_512 0x40 +#define USS820_TXCON_FFSZ_32_1024 0x60 +#define USS820_TXCON_FFSZ_MASK 0x60 +#define USS820_TXCON_TXCLR 0x80 /* Transmit FIFO clear */ + +#define USS820_TXFLG 0x04 /* Transmit FIFO flag (Read Only) */ +#define USS820_TXFLG_TXOVF 0x01 /* TX overrun */ +#define USS820_TXFLG_TXURF 0x02 /* TX underrun */ +#define USS820_TXFLG_TXFULL 0x04 /* TX full */ +#define USS820_TXFLG_TXEMP 0x08 /* TX empty */ +#define USS820_TXFLG_UNUSED 0x30 +#define USS820_TXFLG_TXFIF0 0x40 +#define USS820_TXFLG_TXFIF1 0x80 + +#define USS820_RXDAT 0x05 /* Receive FIFO data */ + +#define USS820_RXCNTL 0x06 /* Receive FIFO byte count low */ +#define USS820_RXCNTL_MASK 0xFF + +#define USS820_RXCNTH 0x07 /* Receive FIFO byte count high */ +#define USS820_RXCNTH_MASK 0x03 +#define USS820_RXCNTH_UNUSED 0xFC + +#define USS820_RXCON 0x08 /* Receive FIFO control */ +#define USS820_RXCON_REVWP 0x01 +#define USS820_RXCON_ADVWM 0x02 +#define USS820_RXCON_ARM 0x04 /* Auto Receive Management */ +#define USS820_RXCON_RXISO 0x08 /* Receive Isochronous Data */ +#define USS820_RXCON_RXFFRC 0x10 /* FIFO Read Complete */ +#define USS820_RXCON_FFSZ_16_64 0x00 +#define USS820_RXCON_FFSZ_64_256 0x20 +#define USS820_RXCON_FFSZ_8_512 0x40 +#define USS820_RXCON_FFSZ_32_1024 0x60 +#define USS820_RXCON_RXCLR 0x80 /* Receive FIFO clear */ + +#define USS820_RXFLG 0x09 /* Receive FIFO flag (Read Only) */ +#define USS820_RXFLG_RXOVF 0x01 /* RX overflow */ +#define USS820_RXFLG_RXURF 0x02 /* RX underflow */ +#define USS820_RXFLG_RXFULL 0x04 /* RX full */ +#define USS820_RXFLG_RXEMP 0x08 /* RX empty */ +#define USS820_RXFLG_RXFLUSH 0x10 /* RX flush */ +#define USS820_RXFLG_UNUSED 0x20 +#define USS820_RXFLG_RXFIF0 0x40 +#define USS820_RXFLG_RXFIF1 0x80 + +#define USS820_EPINDEX 0x0a /* Endpoint index selection */ +#define USS820_EPINDEX_MASK 0x07 +#define USS820_EPINDEX_UNUSED 0xF8 + +#define USS820_EPCON 0x0b /* Endpoint control */ +#define USS820_EPCON_TXEPEN 0x01 /* Transmit Endpoint Enable */ +#define USS820_EPCON_TXOE 0x02 /* Transmit Output Enable */ +#define USS820_EPCON_RXEPEN 0x04 /* Receive Endpoint Enable */ +#define USS820_EPCON_RXIE 0x08 /* Receive Input Enable */ +#define USS820_EPCON_RXSPM 0x10 /* Receive Single-Packet Mode */ +#define USS820_EPCON_CTLEP 0x20 /* Control Endpoint */ +#define USS820_EPCON_TXSTL 0x40 /* Stall Transmit Endpoint */ +#define USS820_EPCON_RXSTL 0x80 /* Stall Receive Endpoint */ + +#define USS820_TXSTAT 0x0c /* Transmit status */ +#define USS820_TXSTAT_TXACK 0x01 /* Transmit Acknowledge */ +#define USS820_TXSTAT_TXERR 0x02 /* Transmit Error */ +#define USS820_TXSTAT_TXVOID 0x04 /* Transmit Void */ +#define USS820_TXSTAT_TXSOVW 0x08 /* Transmit Data Sequence Overwrite + * Bit */ +#define USS820_TXSTAT_TXFLUSH 0x10 /* Transmit FIFO Packet Flushed */ +#define USS820_TXSTAT_TXNAKE 0x20 /* Transmit NAK Mode Enable */ +#define USS820_TXSTAT_TXDSAM 0x40 /* Transmit Data-Set-Available Mode */ +#define USS820_TXSTAT_TXSEQ 0x80 /* Transmitter Current Sequence Bit */ + +#define USS820_RXSTAT 0x0d /* Receive status */ +#define USS820_RXSTAT_RXACK 0x01 /* Receive Acknowledge */ +#define USS820_RXSTAT_RXERR 0x02 /* Receive Error */ +#define USS820_RXSTAT_RXVOID 0x04 /* Receive Void */ +#define USS820_RXSTAT_RXSOVW 0x08 /* Receive Data Sequence Overwrite Bit */ +#define USS820_RXSTAT_EDOVW 0x10 /* End Overwrite Flag */ +#define USS820_RXSTAT_STOVW 0x20 /* Start Overwrite Flag */ +#define USS820_RXSTAT_RXSETUP 0x40 /* Received SETUP token */ +#define USS820_RXSTAT_RXSEQ 0x80 /* Receiver Endpoint Sequence Bit */ + +#define USS820_SOFL 0x0e /* Start Of Frame counter low */ +#define USS820_SOFL_MASK 0xFF + +#define USS820_SOFH 0x0f /* Start Of Frame counter high */ +#define USS820_SOFH_MASK 0x07 +#define USS820_SOFH_SOFDIS 0x08 /* SOF Pin Output Disable */ +#define USS820_SOFH_FTLOCK 0x10 /* Frame Timer Lock */ +#define USS820_SOFH_SOFIE 0x20 /* SOF Interrupt Enable */ +#define USS820_SOFH_ASOF 0x40 /* Any Start of Frame */ +#define USS820_SOFH_SOFACK 0x80 /* SOF Token Received Without Error */ + +#define USS820_FADDR 0x10 /* Function Address */ +#define USS820_FADDR_MASK 0x7F +#define USS820_FADDR_UNUSED 0x80 + +#define USS820_SCR 0x11 /* System Control */ +#define USS820_SCR_UNUSED 0x01 +#define USS820_SCR_T_IRQ 0x02 /* Global Interrupt Enable */ +#define USS820_SCR_IRQLVL 0x04 /* Interrupt Mode */ +#define USS820_SCR_SRESET 0x08 /* Software reset */ +#define USS820_SCR_IE_RESET 0x10 /* Enable Reset Interrupt */ +#define USS820_SCR_IE_SUSP 0x20 /* Enable Suspend Interrupt */ +#define USS820_SCR_RWUPE 0x40 /* Enable Remote Wake-Up Feature */ +#define USS820_SCR_IRQPOL 0x80 /* IRQ polarity */ + +#define USS820_SSR 0x12 /* System Status */ +#define USS820_SSR_RESET 0x01 /* Reset Condition Detected on USB + * cable */ +#define USS820_SSR_SUSPEND 0x02 /* Suspend Detected */ +#define USS820_SSR_RESUME 0x04 /* Resume Detected */ +#define USS820_SSR_SUSPDIS 0x08 /* Suspend Disable */ +#define USS820_SSR_SUSPPO 0x10 /* Suspend Power Off */ +#define USS820_SSR_UNUSED 0xE0 + +#define USS820_UNK0 0x13 /* Unknown */ +#define USS820_UNK0_UNUSED 0xFF + +#define USS820_SBI 0x14 /* Serial bus interrupt low */ +#define USS820_SBI_FTXD0 0x01 /* Function Transmit Done, EP 0 */ +#define USS820_SBI_FRXD0 0x02 /* Function Receive Done, EP 0 */ +#define USS820_SBI_FTXD1 0x04 +#define USS820_SBI_FRXD1 0x08 +#define USS820_SBI_FTXD2 0x10 +#define USS820_SBI_FRXD2 0x20 +#define USS820_SBI_FTXD3 0x40 +#define USS820_SBI_FRXD3 0x80 + +#define USS820_SBI1 0x15 /* Serial bus interrupt high */ +#define USS820_SBI1_FTXD4 0x01 +#define USS820_SBI1_FRXD4 0x02 +#define USS820_SBI1_FTXD5 0x04 +#define USS820_SBI1_FRXD5 0x08 +#define USS820_SBI1_FTXD6 0x10 +#define USS820_SBI1_FRXD6 0x20 +#define USS820_SBI1_FTXD7 0x40 +#define USS820_SBI1_FRXD7 0x80 + +#define USS820_SBIE 0x16 /* Serial bus interrupt enable low */ +#define USS820_SBIE_FTXIE0 0x01 +#define USS820_SBIE_FRXIE0 0x02 +#define USS820_SBIE_FTXIE1 0x04 +#define USS820_SBIE_FRXIE1 0x08 +#define USS820_SBIE_FTXIE2 0x10 +#define USS820_SBIE_FRXIE2 0x20 +#define USS820_SBIE_FTXIE3 0x40 +#define USS820_SBIE_FRXIE3 0x80 + +#define USS820_SBIE1 0x17 /* Serial bus interrupt enable high */ +#define USS820_SBIE1_FTXIE4 0x01 +#define USS820_SBIE1_FRXIE4 0x02 +#define USS820_SBIE1_FTXIE5 0x04 +#define USS820_SBIE1_FRXIE5 0x08 +#define USS820_SBIE1_FTXIE6 0x10 +#define USS820_SBIE1_FRXIE6 0x20 +#define USS820_SBIE1_FTXIE7 0x40 +#define USS820_SBIE1_FRXIE7 0x80 + +#define USS820_REV 0x18 /* Hardware revision */ +#define USS820_REV_MIN 0x0F +#define USS820_REV_MAJ 0xF0 + +#define USS820_LOCK 0x19 /* Suspend power-off locking */ +#define USS820_LOCK_UNLOCKED 0x01 +#define USS820_LOCK_UNUSED 0xFE + +#define USS820_PEND 0x1a /* Pend hardware status update */ +#define USS820_PEND_PEND 0x01 +#define USS820_PEND_UNUSED 0xFE + +#define USS820_SCRATCH 0x1b /* Scratch firmware information */ +#define USS820_SCRATCH_MASK 0x7F +#define USS820_SCRATCH_IE_RESUME 0x80 /* Enable Resume Interrupt */ + +#define USS820_MCSR 0x1c /* Miscellaneous control and status */ +#define USS820_MCSR_DPEN 0x01 /* DPLS Pull-Up Enable */ +#define USS820_MCSR_SUSPLOE 0x02 /* Suspend Lock Out Enable */ +#define USS820_MCSR_BDFEAT 0x04 /* Board Feature Enable */ +#define USS820_MCSR_FEAT 0x08 /* Feature Enable */ +#define USS820_MCSR_PKGID 0x10 /* Package Identification */ +#define USS820_MCSR_SUSPS 0x20 /* Suspend Status */ +#define USS820_MCSR_INIT 0x40 /* Device Initialized */ +#define USS820_MCSR_RWUPR 0x80 /* Remote Wakeup-Up Remember */ + +#define USS820_DSAV 0x1d /* Data set available low (Read Only) */ +#define USS820_DSAV_TXAV0 0x01 +#define USS820_DSAV_RXAV0 0x02 +#define USS820_DSAV_TXAV1 0x04 +#define USS820_DSAV_RXAV1 0x08 +#define USS820_DSAV_TXAV2 0x10 +#define USS820_DSAV_RXAV2 0x20 +#define USS820_DSAV_TXAV3 0x40 +#define USS820_DSAV_RXAV3 0x80 + +#define USS820_DSAV1 0x1e /* Data set available high */ +#define USS820_DSAV1_TXAV4 0x01 +#define USS820_DSAV1_RXAV4 0x02 +#define USS820_DSAV1_TXAV5 0x04 +#define USS820_DSAV1_RXAV5 0x08 +#define USS820_DSAV1_TXAV6 0x10 +#define USS820_DSAV1_RXAV6 0x20 +#define USS820_DSAV1_TXAV7 0x40 +#define USS820_DSAV1_RXAV7 0x80 + +#define USS820_UNK1 0x1f /* Unknown */ +#define USS820_UNK1_UNKNOWN 0xFF + +#define USS820_GET_REG(sc,reg) \ + ((reg) << (sc)->sc_reg_shift) + +#define USS820_READ_1(sc, reg) \ + bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ + USS820_GET_REG(sc,reg)) + +#define USS820_WRITE_1(sc, reg, data) \ + bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ + USS820_GET_REG(sc,reg), data) + +struct uss820dci_td; + +typedef uint8_t (uss820dci_cmd_t)(struct uss820dci_td *td); + +struct uss820dci_td { + bus_space_tag_t io_tag; + bus_space_handle_t io_hdl; + struct uss820dci_td *obj_next; + uss820dci_cmd_t *func; + struct usb2_page_cache *pc; + uint32_t offset; + uint32_t remainder; + uint16_t max_packet_size; + uint8_t rx_stat_reg; + uint8_t tx_stat_reg; + uint8_t rx_flag_reg; + uint8_t tx_flag_reg; + uint8_t rx_fifo_reg; + uint8_t tx_fifo_reg; + uint8_t rx_count_low_reg; + uint8_t rx_count_high_reg; + uint8_t tx_count_low_reg; + uint8_t tx_count_high_reg; + uint8_t rx_cntl_reg; + uint8_t tx_cntl_reg; + uint8_t ep_reg; + uint8_t pend_reg; + uint8_t ep_index; + uint8_t error:1; + uint8_t alt_next:1; + uint8_t short_pkt:1; + uint8_t support_multi_buffer:1; + uint8_t did_stall:1; +}; + +struct uss820_std_temp { + uss820dci_cmd_t *func; + struct usb2_page_cache *pc; + struct uss820dci_td *td; + struct uss820dci_td *td_next; + uint32_t len; + uint32_t offset; + uint16_t max_frame_size; + uint8_t short_pkt; + /* + * short_pkt = 0: transfer should be short terminated + * short_pkt = 1: transfer should not be short terminated + */ + uint8_t setup_alt_next; +}; + +struct uss820dci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union uss820_hub_temp { + uWord wValue; + struct usb2_port_status ps; +}; + +struct uss820_flags { + uint8_t change_connect:1; + uint8_t change_suspend:1; + uint8_t status_suspend:1; /* set if suspended */ + uint8_t status_vbus:1; /* set if present */ + uint8_t status_bus_reset:1; /* set if reset complete */ + uint8_t clocks_off:1; + uint8_t port_powered:1; + uint8_t port_enabled:1; + uint8_t d_pulled_up:1; + uint8_t mcsr_feat:1; +}; + +struct uss820dci_softc { + struct usb2_bus sc_bus; + union uss820_hub_temp sc_hub_temp; + LIST_HEAD(, usb2_xfer) sc_interrupt_list_head; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + struct usb2_config_td sc_config_td; + + struct resource *sc_io_res; + struct resource *sc_irq_res; + void *sc_intr_hdl; + bus_size_t sc_io_size; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; + + uint8_t sc_rt_addr; /* root HUB address */ + uint8_t sc_dv_addr; /* device address */ + uint8_t sc_conf; /* root HUB config */ + uint8_t sc_reg_shift; + + uint8_t sc_hub_idata[1]; + + struct uss820_flags sc_flags; +}; + +/* prototypes */ + +usb2_error_t uss820dci_init(struct uss820dci_softc *sc); +void uss820dci_uninit(struct uss820dci_softc *sc); +void uss820dci_suspend(struct uss820dci_softc *sc); +void uss820dci_resume(struct uss820dci_softc *sc); +void uss820dci_interrupt(struct uss820dci_softc *sc); + +#endif /* _USS820_DCI_H_ */ diff --git a/sys/dev/usb2/controller/uss820dci_atmelarm.c b/sys/dev/usb2/controller/uss820dci_atmelarm.c new file mode 100644 index 000000000000..be71fb310abb --- /dev/null +++ b/sys/dev/usb2/controller/uss820dci_atmelarm.c @@ -0,0 +1,247 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2008 Hans Petter Selasky + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static device_probe_t uss820_atmelarm_probe; +static device_attach_t uss820_atmelarm_attach; +static device_detach_t uss820_atmelarm_detach; +static device_suspend_t uss820_atmelarm_suspend; +static device_resume_t uss820_atmelarm_resume; +static device_shutdown_t uss820_atmelarm_shutdown; + +static device_method_t uss820dci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uss820_atmelarm_probe), + DEVMETHOD(device_attach, uss820_atmelarm_attach), + DEVMETHOD(device_detach, uss820_atmelarm_detach), + DEVMETHOD(device_suspend, uss820_atmelarm_suspend), + DEVMETHOD(device_resume, uss820_atmelarm_resume), + DEVMETHOD(device_shutdown, uss820_atmelarm_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t uss820dci_driver = { + .name = "uss820", + .methods = uss820dci_methods, + .size = sizeof(struct uss820dci_softc), +}; + +static devclass_t uss820dci_devclass; + +DRIVER_MODULE(uss820, atmelarm, uss820dci_driver, uss820dci_devclass, 0, 0); +MODULE_DEPEND(uss820, usb2_controller, 1, 1, 1); +MODULE_DEPEND(uss820, usb2_core, 1, 1, 1); + +static const char *const uss820_desc = "USS820 USB Device Controller"; + +static int +uss820_atmelarm_suspend(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_suspend(dev); + if (err == 0) { + uss820dci_suspend(sc); + } + return (err); +} + +static int +uss820_atmelarm_resume(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + uss820dci_resume(sc); + + err = bus_generic_resume(dev); + + return (err); +} + +static int +uss820_atmelarm_shutdown(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_shutdown(dev); + if (err) + return (err); + + uss820dci_uninit(sc); + + return (0); +} + +static int +uss820_atmelarm_probe(device_t dev) +{ + device_set_desc(dev, uss820_desc); + return (0); /* success */ +} + +static int +uss820_atmelarm_attach(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + int rid; + + if (sc == NULL) { + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + return (ENOMEM); + } + rid = 0; + sc->sc_io_res = + bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); + + if (!sc->sc_io_res) { + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + /* multiply all addresses by 4 */ + sc->sc_reg_shift = 2; + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + goto error; + } + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + + err = usb2_config_td_setup(&sc->sc_config_td, sc, + &sc->sc_bus.mtx, NULL, 0, 4); + if (err) { + device_printf(dev, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); +#endif + if (err) { + sc->sc_intr_hdl = NULL; + goto error; + } + err = uss820dci_init(sc); + if (err) { + device_printf(dev, "Init failed\n"); + goto error; + } + err = device_probe_and_attach(sc->sc_bus.bdev); + if (err) { + device_printf(dev, "USB probe and attach failed\n"); + goto error; + } + return (0); + +error: + uss820_atmelarm_detach(dev); + return (ENXIO); +} + +static int +uss820_atmelarm_detach(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call at91_udp_uninit() after at91_udp_init() + */ + uss820dci_uninit(sc); + + err = bus_teardown_intr(dev, sc->sc_irq_res, + sc->sc_intr_hdl); + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_bus, NULL); + + return (0); +} diff --git a/sys/dev/usb2/controller/uss820dci_pccard.c b/sys/dev/usb2/controller/uss820dci_pccard.c new file mode 100644 index 000000000000..f57935d7bc79 --- /dev/null +++ b/sys/dev/usb2/controller/uss820dci_pccard.c @@ -0,0 +1,266 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2008 Hans Petter Selasky + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +static device_probe_t uss820_pccard_probe; +static device_attach_t uss820_pccard_attach; +static device_detach_t uss820_pccard_detach; +static device_suspend_t uss820_pccard_suspend; +static device_resume_t uss820_pccard_resume; +static device_shutdown_t uss820_pccard_shutdown; + +static uint8_t uss820_pccard_lookup(device_t dev); + +static device_method_t uss820dci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uss820_pccard_probe), + DEVMETHOD(device_attach, uss820_pccard_attach), + DEVMETHOD(device_detach, uss820_pccard_detach), + DEVMETHOD(device_suspend, uss820_pccard_suspend), + DEVMETHOD(device_resume, uss820_pccard_resume), + DEVMETHOD(device_shutdown, uss820_pccard_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t uss820dci_driver = { + .name = "uss820", + .methods = uss820dci_methods, + .size = sizeof(struct uss820dci_softc), +}; + +static devclass_t uss820dci_devclass; + +DRIVER_MODULE(uss820, pccard, uss820dci_driver, uss820dci_devclass, 0, 0); +MODULE_DEPEND(uss820, usb2_core, 1, 1, 1); + +static const char *const uss820_desc = "USS820 USB Device Controller"; + +static int +uss820_pccard_suspend(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_suspend(dev); + if (err == 0) { + uss820dci_suspend(sc); + } + return (err); +} + +static int +uss820_pccard_resume(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + uss820dci_resume(sc); + + err = bus_generic_resume(dev); + + return (err); +} + +static int +uss820_pccard_shutdown(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_shutdown(dev); + if (err) + return (err); + + uss820dci_uninit(sc); + + return (0); +} + +static uint8_t +uss820_pccard_lookup(device_t dev) +{ + uint32_t prod; + uint32_t vend; + + pccard_get_vendor(dev, &vend); + pccard_get_product(dev, &prod); + + /* ID's will be added later */ + return (0); +} + +static int +uss820_pccard_probe(device_t dev) +{ + if (uss820_pccard_lookup(dev)) { + device_set_desc(dev, uss820_desc); + return (0); + } + return (ENXIO); +} +static int +uss820_pccard_attach(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + int err; + int rid; + + if (sc == NULL) { + return (ENXIO); + } + /* get all DMA memory */ + + if (usb2_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + return (ENOMEM); + } + rid = 0; + sc->sc_io_res = + bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); + + if (!sc->sc_io_res) { + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + /* multiply all addresses by 4 */ + sc->sc_reg_shift = 2; + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + goto error; + } + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_bus.bdev)) { + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + + err = usb2_config_td_setup(&sc->sc_config_td, sc, + &sc->sc_bus.mtx, NULL, 0, 4); + if (err) { + device_printf(dev, "could not setup config thread!\n"); + goto error; + } +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); +#endif + if (err) { + sc->sc_intr_hdl = NULL; + goto error; + } + err = uss820dci_init(sc); + if (err) { + device_printf(dev, "Init failed\n"); + goto error; + } + err = device_probe_and_attach(sc->sc_bus.bdev); + if (err) { + device_printf(dev, "USB probe and attach failed\n"); + goto error; + } + return (0); + +error: + uss820_pccard_detach(dev); + return (ENXIO); +} + +static int +uss820_pccard_detach(device_t dev) +{ + struct uss820dci_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(dev); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call at91_udp_uninit() after at91_udp_init() + */ + uss820dci_uninit(sc); + + err = bus_teardown_intr(dev, sc->sc_irq_res, + sc->sc_intr_hdl); + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_bus_mem_free_all(&sc->sc_bus, NULL); + + return (0); +} diff --git a/sys/dev/usb2/core/README.TXT b/sys/dev/usb2/core/README.TXT new file mode 100644 index 000000000000..736723049424 --- /dev/null +++ b/sys/dev/usb2/core/README.TXT @@ -0,0 +1,411 @@ + +$FreeBSD$ + +DESCRIPTION OF THE NEW USB API + +The new USB 2.0 API consists of 5 functions. All transfer types are +managed using these functions. There is no longer need for separate +functions to setup INTERRUPT- and ISOCHRONOUS- transfers. + ++--------------------------------------------------------------+ +| | +| "usb2_transfer_setup" - This function will allocate all | +| necessary DMA memory and might | +| sleep! | +| | +| "usb2_transfer_unsetup" - This function will stop the USB | +| transfer, if it is currently | +| active, release all DMA | +| memory and might sleep! | +| | +| "usb2_transfer_start" - This function will start an USB | +| transfer, if not already started.| +| This function is always | +| non-blocking. ** | +| | +| "usb2_transfer_stop" - This function will stop an USB | +| transfer, if not already stopped.| +| The callback function will be | +| called before this function | +| returns. This function is always | +| non-blocking. ** | +| | +| "usb2_transfer_drain" - This function will stop an USB | +| transfer, if not already stopped | +| and wait for any additional | +| DMA load operations to complete. | +| Buffers that are loaded into DMA | +| using "usb2_set_frame_data" can | +| safely be freed after that | +| this function has returned. This | +| function can block the caller. | +| | +| ** These functions must be called with the private driver's | +| lock locked. | +| | +| NOTE: These USB API functions are NULL safe, with regard | +| to the USB transfer structure pointer. | ++--------------------------------------------------------------+ + +Reference: /sys/dev/usb2/core/usb2_transfer.c + +/* + * A simple USB callback state-machine: + * + * +->-----------------------+ + * | | + * +-<-+-------[tr_setup]--------+-<-+-<-[start/restart] + * | | + * | | + * | | + * +------>-[tr_transferred]---------+ + * | | + * +--------->-[tr_error]------------+ + */ + +void +usb2_default_callback(struct usb2_xfer *xfer) +{ + /* + * NOTE: it is not allowed to return + * before "USB_CHECK_STATUS()", + * even if the system is tearing down! + */ + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + /* + * Setup xfer->frlengths[], xfer->nframes + * and write data to xfer->frbuffers[], if any + */ + + /**/ + usb2_start_hardware(xfer); + return; + + case USB_ST_TRANSFERRED: + /* + * Read data from xfer->frbuffers[], if any. + * "xfer->frlengths[]" should now have been + * updated to the actual length. + */ + return; + + default: /* Error */ + /* print error message and clear stall for example */ + return; + } +} + +=== Notes for USB control transfers === + +An USB control transfer has three parts. First the SETUP packet, then +DATA packet(s) and then a STATUS packet. The SETUP packet is always +pointed to by "xfer->frbuffers[0]" and the length is stored in +"xfer->frlengths[0]" also if there should not be sent any SETUP +packet! If an USB control transfer has no DATA stage, then +"xfer->nframes" should be set to 1. Else the default value is +"xfer->nframes" equal to 2. + +Example1: SETUP + STATUS + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usb2_start_hardware(xfer); + +Example2: SETUP + DATA + STATUS + xfer->nframes = 2; + xfer->frlenghts[0] = 8; + xfer->frlenghts[1] = 1; + usb2_start_hardware(xfer); + +Example3: SETUP + DATA + STATUS - split +1st callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usb2_start_hardware(xfer); + +2nd callback: + /* IMPORTANT: frbuffer[0] must still point at the setup packet! */ + xfer->nframes = 2; + xfer->frlenghts[0] = 0; + xfer->frlenghts[1] = 1; + usb2_start_hardware(xfer); + +Example4: SETUP + STATUS - split +1st callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + xfer->flags.manual_status = 1; + usb2_start_hardware(xfer); + +2nd callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 0; + xfer->flags.manual_status = 0; + usb2_start_hardware(xfer); + + +=== General USB transfer notes === + + 1) Something that one should be aware of is that all USB callbacks support +recursation. That means one can start/stop whatever transfer from the callback +of another transfer one desires. Also the transfer that is currently called +back. Recursion is handled like this that when the callback that wants to +recurse returns it is called one more time. + + 2) After that the "usb2_start_hardware()" function has been called in +the callback one can always depend on that "tr_error" or "tr_transferred" +will get jumped afterwards. Always! + + 3) Sleeping functions can only be called from the attach routine of the +driver. Else one should not use sleeping functions unless one has to. It is +very difficult with sleep, because one has to think that the device might have +detached when the thread returns from sleep. + + 4) Polling. + + use_polling + This flag can be used with any callback and will cause the + "usb2_transfer_start()" function to wait using "DELAY()", + without exiting any mutexes, until the transfer is finished or + has timed out. This flag can be changed during operation. + + NOTE: If polling is used the "timeout" field should be non-zero! + NOTE: USB_ERR_CANCELLED is returned in case of timeout + instead of USB_ERR_TIMEOUT! + + + +USB device driver examples: + +/sys/dev/usb2/ethernet/if_axe.c +/sys/dev/usb2/ethernet/if_aue.c + +QUICK REFERENCE +=============== + + +/*------------------------------------------------------------------------* + * usb2_error_t + * usb2_transfer_setup(udev, ifaces, pxfer, setup_start, + * n_setup, priv_sc, priv_mtx) + *------------------------------------------------------------------------*/ + +- "udev" is a pointer to "struct usb2_device". + +- "ifaces" array of interface index numbers to use. See "if_index". + +- "pxfer" is a pointer to an array of USB transfer pointers that are + initialized to NULL, and then pointed to allocated USB transfers. + +- "setup_start" is a pointer to an array of USB config structures. + +- "n_setup" is a number telling the USB system how many USB transfers + should be setup. + +- "priv_sc" is the private softc pointer, which will be used to + initialize "xfer->priv_sc". + +- "priv_mtx" is the private mutex protecting the transfer structure and + the softc. This pointer is used to initialize "xfer->priv_mtx". + +/*------------------------------------------------------------------------* + * void + * usb2_transfer_unsetup(pxfer, n_setup) + *------------------------------------------------------------------------*/ + +- "pxfer" is a pointer to an array of USB transfer pointers, that may + be NULL, that should be freed by the USB system. + +- "n_setup" is a number telling the USB system how many USB transfers + should be unsetup + +NOTE: This function can sleep, waiting for active mutexes to become unlocked! +NOTE: It is not allowed to call "usb2_transfer_unsetup" from the callback + of a USB transfer. + +/*------------------------------------------------------------------------* + * void + * usb2_transfer_start(xfer) + *------------------------------------------------------------------------*/ + +- "xfer" is pointer to a USB transfer that should be started + +NOTE: this function must be called with "priv_mtx" locked + +/*------------------------------------------------------------------------* + * void + * usb2_transfer_stop(xfer) + *------------------------------------------------------------------------*/ + +- "xfer" is a pointer to a USB transfer that should be stopped + +NOTE: this function must be called with "priv_mtx" locked + +NOTE: if the transfer was in progress, the callback will called with + "xfer->error=USB_ERR_CANCELLED", before this function returns + +/*------------------------------------------------------------------------* + * struct usb2_config { + * type, endpoint, direction, interval, timeout, frames, index + * flags, bufsize, callback + * }; + *------------------------------------------------------------------------*/ + +- The "type" field selects the USB pipe type. Valid values are: + UE_INTERRUPT, UE_CONTROL, UE_BULK, UE_ISOCHRONOUS. The special + value UE_BULK_INTR will select BULK and INTERRUPT pipes. + This field is mandatory. + +- The "endpoint" field selects the USB endpoint number. A value of + 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching endpoint. + This field is mandatory. + +- The "direction" field selects the USB endpoint direction. A value of + "UE_DIR_ANY" will select the first matching endpoint. Else valid + values are: "UE_DIR_IN" and "UE_DIR_OUT". "UE_DIR_IN" and + "UE_DIR_OUT" can be binary ORed by "UE_DIR_SID" which means that the + direction will be swapped in case of USB_MODE_DEVICE. Note that + "UE_DIR_IN" refers to the data transfer direction of the "IN" tokens + and "UE_DIR_OUT" refers to the data transfer direction of the "OUT" + tokens. This field is mandatory. + +- The "interval" field selects the interrupt interval. The value of this + field is given in milliseconds and is independent of device speed. Depending + on the endpoint type, this field has different meaning: + + UE_INTERRUPT) + "0" use the default interrupt interval based on endpoint descriptor. + "Else" use the given value for polling rate. + + UE_ISOCHRONOUS) + "0" use default. + "Else" the value is ignored. + + UE_BULK) + UE_CONTROL) + "0" no transfer pre-delay. + "Else" a delay as given by this field in milliseconds is + inserted before the hardware is started when + "usb2_start_hardware()" is called. + NOTE: The transfer timeout, if any, is started after that + the pre-delay has elapsed! + +- The "timeout" field, if non-zero, will set the transfer timeout in + milliseconds. If the "timeout" field is zero and the transfer type + is ISOCHRONOUS a timeout of 250ms will be used. + +- The "frames" field sets the maximum number of frames. If zero is + specified it will yield the following results: + + UE_BULK) + UE_INTERRUPT) + xfer->nframes = 1; + + UE_CONTROL) + xfer->nframes = 2; + + UE_ISOCHRONOUS) + Not allowed. Will cause an error. + +- The "ep_index" field allows you to give a number, in case more + endpoints match the description, that selects which matching + "ep_index" should be used. + +- The "if_index" field allows you to select which of the interface + numbers in the "ifaces" array parameter passed to "usb2_transfer_setup" + that should be used when setting up the given USB transfer. + +- The "flags" field has type "struct usb2_xfer_flags" and allows one + to set initial flags an USB transfer. Valid flags are: + + force_short_xfer + This flag forces the last transmitted USB packet to be short. + A short packet has a length of less than "xfer->max_packet_size", + which derives from "wMaxPacketSize". This flag can be changed + during operation. + + short_xfer_ok + This flag allows the received transfer length, "xfer->actlen" + to be less than "xfer->sumlen" upon completion of a transfer. + This flag can be changed during operation. + + pipe_bof + This flag causes a failing USB transfer to remain first + in the PIPE queue except in the case of "xfer->error" equal + to "USB_ERR_CANCELLED". No other USB transfers in the affected + PIPE queue will be started until either: + + 1) The failing USB transfer is stopped using "usb2_transfer_stop()". + 2) The failing USB transfer performs a successful transfer. + + The purpose of this flag is to avoid races when multiple + transfers are queued for execution on an USB endpoint, and the + first executing transfer fails leading to the need for + clearing of stall for example. In this case this flag is used + to prevent the following USB transfers from being executed at + the same time the clear-stall command is executed on the USB + control endpoint. This flag can be changed during operation. + + "BOF" is short for "Block On Failure" + + NOTE: This flag should be set on all BULK and INTERRUPT + USB transfers which use an endpoint that can be shared + between userland and kernel. + + proxy_buffer + Setting this flag will cause that the total buffer size will + be rounded up to the nearest atomic hardware transfer + size. The maximum data length of any USB transfer is always + stored in the "xfer->max_data_length". For control transfers + the USB kernel will allocate additional space for the 8-bytes + of SETUP header. These 8-bytes are not counted by the + "xfer->max_data_length" variable. This flag can not be changed + during operation. + + ext_buffer + Setting this flag will cause that no data buffer will be + allocated. Instead the USB client must supply a data buffer. + This flag can not be changed during operation. + + manual_status + Setting this flag prevents an USB STATUS stage to be appended + to the end of the USB control transfer. If no control data is + transferred this flag must be cleared. Else an error will be + returned to the USB callback. This flag is mostly useful for + the USB device side. This flag can be changed during + operation. + + no_pipe_ok + Setting this flag causes the USB_ERR_NO_PIPE error to be + ignored. This flag can not be changed during operation. + + stall_pipe + Setting this flag will cause STALL pids to be sent to the + endpoint belonging to this transfer before the transfer is + started. The transfer is started at the moment the host issues + a clear-stall command on the STALL'ed endpoint. This flag can + be changed during operation. This flag does only have effect + in USB device side mode except for control endpoints. This + flag is cleared when the stall command has been executed. This + flag can only be changed outside the callback function by + using the functions "usb2_transfer_set_stall()" and + "usb2_transfer_clear_stall()" ! + +- The "bufsize" field sets the total buffer size in bytes. If + this field is zero, "wMaxPacketSize" will be used, multiplied by the + "frames" field if the transfer type is ISOCHRONOUS. This is useful for + setting up interrupt pipes. This field is mandatory. + + NOTE: For control transfers "bufsize" includes + the length of the request structure. + +- The "callback" pointer sets the USB callback. This field is mandatory. + +MUTEX NOTE: +=========== + +When you create a mutex using "mtx_init()", don't forget to call +"mtx_destroy()" at detach, else you can get "freed memory accessed" +panics. + +--HPS diff --git a/sys/dev/usb2/core/usb2_busdma.c b/sys/dev/usb2/core/usb2_busdma.c new file mode 100644 index 000000000000..1b8c4a1fc3ee --- /dev/null +++ b/sys/dev/usb2/core/usb2_busdma.c @@ -0,0 +1,1401 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include + +#include +#include +#include + +static void usb2_dma_tag_create(struct usb2_dma_tag *udt, uint32_t size, uint32_t align); +static void usb2_dma_tag_destroy(struct usb2_dma_tag *udt); + +#ifdef __FreeBSD__ +static void usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op); +static int32_t usb2_m_copy_in_cb(void *arg, void *src, uint32_t count); +static void usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error); +static void usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error); +static void usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error, uint8_t isload); + +#endif + +#ifdef __NetBSD__ +static int32_t usb2_m_copy_in_cb(void *arg, caddr_t src, uint32_t count); +static void usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs, int nseg, int error, uint8_t isload); + +#endif + +/*------------------------------------------------------------------------* + * usb2_get_page - lookup DMA-able memory for the given offset + * + * NOTE: Only call this function when the "page_cache" structure has + * been properly initialized ! + *------------------------------------------------------------------------*/ +void +usb2_get_page(struct usb2_page_cache *pc, uint32_t offset, + struct usb2_page_search *res) +{ + struct usb2_page *page; + + if (pc->page_start) { + + /* Case 1 - something has been loaded into DMA */ + + if (pc->buffer) { + + /* Case 1a - Kernel Virtual Address */ + + res->buffer = USB_ADD_BYTES(pc->buffer, offset); + } + offset += pc->page_offset_buf; + + /* compute destination page */ + + page = pc->page_start; + + if (pc->ismultiseg) { + + page += (offset / USB_PAGE_SIZE); + + offset %= USB_PAGE_SIZE; + + res->length = USB_PAGE_SIZE - offset; + res->physaddr = page->physaddr + offset; + } else { + res->length = 0 - 1; + res->physaddr = page->physaddr + offset; + } + if (!pc->buffer) { + + /* Case 1b - Non Kernel Virtual Address */ + + res->buffer = USB_ADD_BYTES(page->buffer, offset); + } + } else { + + /* Case 2 - Plain PIO */ + + res->buffer = USB_ADD_BYTES(pc->buffer, offset); + res->length = 0 - 1; + res->physaddr = 0; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_copy_in - copy directly to DMA-able memory + *------------------------------------------------------------------------*/ +void +usb2_copy_in(struct usb2_page_cache *cache, uint32_t offset, + const void *ptr, uint32_t len) +{ + struct usb2_page_search buf_res; + + while (len != 0) { + + usb2_get_page(cache, offset, &buf_res); + + if (buf_res.length > len) { + buf_res.length = len; + } + bcopy(ptr, buf_res.buffer, buf_res.length); + + offset += buf_res.length; + len -= buf_res.length; + ptr = USB_ADD_BYTES(ptr, buf_res.length); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_copy_in_user - copy directly to DMA-able memory from userland + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +int +usb2_copy_in_user(struct usb2_page_cache *cache, uint32_t offset, + const void *ptr, uint32_t len) +{ + struct usb2_page_search buf_res; + int error; + + while (len != 0) { + + usb2_get_page(cache, offset, &buf_res); + + if (buf_res.length > len) { + buf_res.length = len; + } + error = copyin(ptr, buf_res.buffer, buf_res.length); + if (error) + return (error); + + offset += buf_res.length; + len -= buf_res.length; + ptr = USB_ADD_BYTES(ptr, buf_res.length); + } + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_m_copy_in - copy a mbuf chain directly into DMA-able memory + *------------------------------------------------------------------------*/ +struct usb2_m_copy_in_arg { + struct usb2_page_cache *cache; + uint32_t dst_offset; +}; + +static int32_t +#ifdef __FreeBSD__ +usb2_m_copy_in_cb(void *arg, void *src, uint32_t count) +#else +usb2_m_copy_in_cb(void *arg, caddr_t src, uint32_t count) +#endif +{ + register struct usb2_m_copy_in_arg *ua = arg; + + usb2_copy_in(ua->cache, ua->dst_offset, src, count); + ua->dst_offset += count; + return (0); +} + +void +usb2_m_copy_in(struct usb2_page_cache *cache, uint32_t dst_offset, + struct mbuf *m, uint32_t src_offset, uint32_t src_len) +{ + struct usb2_m_copy_in_arg arg = {cache, dst_offset}; + register int error; + + error = m_apply(m, src_offset, src_len, &usb2_m_copy_in_cb, &arg); + return; +} + +/*------------------------------------------------------------------------* + * usb2_uiomove - factored out code + *------------------------------------------------------------------------*/ +int +usb2_uiomove(struct usb2_page_cache *pc, struct uio *uio, + uint32_t pc_offset, uint32_t len) +{ + struct usb2_page_search res; + int error = 0; + + while (len != 0) { + + usb2_get_page(pc, pc_offset, &res); + + if (res.length > len) { + res.length = len; + } + /* + * "uiomove()" can sleep so one needs to make a wrapper, + * exiting the mutex and checking things + */ + error = uiomove(res.buffer, res.length, uio); + + if (error) { + break; + } + pc_offset += res.length; + len -= res.length; + } + return (error); +} + +/*------------------------------------------------------------------------* + * usb2_copy_out - copy directly from DMA-able memory + *------------------------------------------------------------------------*/ +void +usb2_copy_out(struct usb2_page_cache *cache, uint32_t offset, + void *ptr, uint32_t len) +{ + struct usb2_page_search res; + + while (len != 0) { + + usb2_get_page(cache, offset, &res); + + if (res.length > len) { + res.length = len; + } + bcopy(res.buffer, ptr, res.length); + + offset += res.length; + len -= res.length; + ptr = USB_ADD_BYTES(ptr, res.length); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_copy_out_user - copy directly from DMA-able memory to userland + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +int +usb2_copy_out_user(struct usb2_page_cache *cache, uint32_t offset, + void *ptr, uint32_t len) +{ + struct usb2_page_search res; + int error; + + while (len != 0) { + + usb2_get_page(cache, offset, &res); + + if (res.length > len) { + res.length = len; + } + error = copyout(res.buffer, ptr, res.length); + if (error) + return (error); + + offset += res.length; + len -= res.length; + ptr = USB_ADD_BYTES(ptr, res.length); + } + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_bzero - zero DMA-able memory + *------------------------------------------------------------------------*/ +void +usb2_bzero(struct usb2_page_cache *cache, uint32_t offset, uint32_t len) +{ + struct usb2_page_search res; + + while (len != 0) { + + usb2_get_page(cache, offset, &res); + + if (res.length > len) { + res.length = len; + } + bzero(res.buffer, res.length); + + offset += res.length; + len -= res.length; + } + return; +} + + +#ifdef __FreeBSD__ + +/*------------------------------------------------------------------------* + * usb2_dma_lock_cb - dummy callback + *------------------------------------------------------------------------*/ +static void +usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op) +{ + /* we use "mtx_owned()" instead of this function */ + return; +} + +/*------------------------------------------------------------------------* + * usb2_dma_tag_create - allocate a DMA tag + * + * NOTE: If the "align" parameter has a value of 1 the DMA-tag will + * allow multi-segment mappings. Else all mappings are single-segment. + *------------------------------------------------------------------------*/ +static void +usb2_dma_tag_create(struct usb2_dma_tag *udt, + uint32_t size, uint32_t align) +{ + bus_dma_tag_t tag; + + if (bus_dma_tag_create + ( /* parent */ udt->tag_parent->tag, + /* alignment */ align, + /* boundary */ USB_PAGE_SIZE, + /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, + /* highaddr */ BUS_SPACE_MAXADDR, + /* filter */ NULL, + /* filterarg */ NULL, + /* maxsize */ size, + /* nsegments */ (align == 1) ? + (2 + (size / USB_PAGE_SIZE)) : 1, + /* maxsegsz */ (align == 1) ? + USB_PAGE_SIZE : size, + /* flags */ 0, + /* lockfn */ &usb2_dma_lock_cb, + /* lockarg */ NULL, + &tag)) { + tag = NULL; + } + udt->tag = tag; + return; +} + +/*------------------------------------------------------------------------* + * usb2_dma_tag_free - free a DMA tag + *------------------------------------------------------------------------*/ +static void +usb2_dma_tag_destroy(struct usb2_dma_tag *udt) +{ + bus_dma_tag_destroy(udt->tag); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_alloc_mem_cb - BUS-DMA callback function + *------------------------------------------------------------------------*/ +static void +usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, + int nseg, int error) +{ + usb2_pc_common_mem_cb(arg, segs, nseg, error, 0); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_load_mem_cb - BUS-DMA callback function + *------------------------------------------------------------------------*/ +static void +usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, + int nseg, int error) +{ + usb2_pc_common_mem_cb(arg, segs, nseg, error, 1); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_common_mem_cb - BUS-DMA callback function + *------------------------------------------------------------------------*/ +static void +usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, + int nseg, int error, uint8_t isload) +{ + struct usb2_dma_parent_tag *uptag; + struct usb2_page_cache *pc; + struct usb2_page *pg; + uint32_t rem; + uint8_t owned; + + pc = arg; + uptag = pc->tag_parent; + + /* + * XXX There is sometimes recursive locking here. + * XXX We should try to find a better solution. + * XXX Until further the "owned" variable does + * XXX the trick. + */ + + if (error) { + goto done; + } + pg = pc->page_start; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + rem = segs->ds_addr & (USB_PAGE_SIZE - 1); + pc->page_offset_buf = rem; + pc->page_offset_end += rem; + nseg--; + + while (nseg > 0) { + nseg--; + segs++; + pg++; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + } + +done: + owned = mtx_owned(uptag->mtx); + if (!owned) + mtx_lock(uptag->mtx); + + uptag->dma_error = (error ? 1 : 0); + if (isload) { + (uptag->func) (uptag); + } else { + usb2_cv_broadcast(uptag->cv); + } + if (!owned) + mtx_unlock(uptag->mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_alloc_mem - allocate DMA'able memory + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, + uint32_t size, uint32_t align) +{ + struct usb2_dma_parent_tag *uptag; + struct usb2_dma_tag *utag; + bus_dmamap_t map; + void *ptr; + int err; + + uptag = pc->tag_parent; + + if (align != 1) { + /* + * The alignment must be greater or equal to the + * "size" else the object can be split between two + * memory pages and we get a problem! + */ + while (align < size) { + align *= 2; + if (align == 0) { + goto error; + } + } +#if 1 + /* + * XXX BUS-DMA workaround - FIXME later: + * + * We assume that that the aligment at this point of + * the code is greater than or equal to the size and + * less than two times the size, so that if we double + * the size, the size will be greater than the + * alignment. + * + * The bus-dma system has a check for "alignment" + * being less than "size". If that check fails we end + * up using contigmalloc which is page based even for + * small allocations. Try to avoid that to save + * memory, hence we sometimes to a large number of + * small allocations! + */ + if (size <= (USB_PAGE_SIZE / 2)) { + size *= 2; + } +#endif + } + /* get the correct DMA tag */ + utag = usb2_dma_tag_find(uptag, size, align); + if (utag == NULL) { + goto error; + } + /* allocate memory */ + if (bus_dmamem_alloc( + utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { + goto error; + } + /* setup page cache */ + pc->buffer = ptr; + pc->page_start = pg; + pc->page_offset_buf = 0; + pc->page_offset_end = size; + pc->map = map; + pc->tag = utag->tag; + pc->ismultiseg = (align == 1); + + mtx_lock(uptag->mtx); + + /* load memory into DMA */ + err = bus_dmamap_load( + utag->tag, map, ptr, size, &usb2_pc_alloc_mem_cb, + pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); + + if (err == EINPROGRESS) { + usb2_cv_wait(uptag->cv, uptag->mtx); + err = 0; + } + mtx_unlock(uptag->mtx); + + if (err || uptag->dma_error) { + bus_dmamem_free(utag->tag, ptr, map); + goto error; + } + bzero(ptr, size); + + usb2_pc_cpu_flush(pc); + + return (0); + +error: + /* reset most of the page cache */ + pc->buffer = NULL; + pc->page_start = NULL; + pc->page_offset_buf = 0; + pc->page_offset_end = 0; + pc->map = NULL; + pc->tag = NULL; + return (1); +} + +/*------------------------------------------------------------------------* + * usb2_pc_free_mem - free DMA memory + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_pc_free_mem(struct usb2_page_cache *pc) +{ + if (pc && pc->buffer) { + + bus_dmamap_unload(pc->tag, pc->map); + + bus_dmamem_free(pc->tag, pc->buffer, pc->map); + + pc->buffer = NULL; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_load_mem - load virtual memory into DMA + * + * Return values: + * 0: Success + * Else: Error + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync) +{ + /* setup page cache */ + pc->page_offset_buf = 0; + pc->page_offset_end = size; + pc->ismultiseg = 1; + + mtx_assert(pc->tag_parent->mtx, MA_OWNED); + + if (size > 0) { + if (sync) { + struct usb2_dma_parent_tag *uptag; + int err; + + uptag = pc->tag_parent; + + /* + * Try to load memory into DMA. + */ + err = bus_dmamap_load( + pc->tag, pc->map, pc->buffer, size, + &usb2_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); + if (err == EINPROGRESS) { + usb2_cv_wait(uptag->cv, uptag->mtx); + err = 0; + } + if (err || uptag->dma_error) { + return (1); + } + } else { + + /* + * Try to load memory into DMA. The callback + * will be called in all cases: + */ + if (bus_dmamap_load( + pc->tag, pc->map, pc->buffer, size, + &usb2_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { + } + } + } else { + if (!sync) { + /* + * Call callback so that refcount is decremented + * properly: + */ + pc->tag_parent->dma_error = 0; + (pc->tag_parent->func) (pc->tag_parent); + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_pc_cpu_invalidate - invalidate CPU cache + *------------------------------------------------------------------------*/ +void +usb2_pc_cpu_invalidate(struct usb2_page_cache *pc) +{ + bus_dmamap_sync(pc->tag, pc->map, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_cpu_flush - flush CPU cache + *------------------------------------------------------------------------*/ +void +usb2_pc_cpu_flush(struct usb2_page_cache *pc) +{ + bus_dmamap_sync(pc->tag, pc->map, + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_dmamap_create - create a DMA map + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size) +{ + struct usb2_xfer_root *info; + struct usb2_dma_tag *utag; + + /* get info */ + info = pc->tag_parent->info; + + /* sanity check */ + if (info == NULL) { + goto error; + } + utag = usb2_dma_tag_find(pc->tag_parent, size, 1); + if (utag == NULL) { + goto error; + } + /* create DMA map */ + if (bus_dmamap_create(utag->tag, 0, &pc->map)) { + goto error; + } + pc->tag = utag->tag; + return 0; /* success */ + +error: + pc->map = NULL; + pc->tag = NULL; + return 1; /* failure */ +} + +/*------------------------------------------------------------------------* + * usb2_pc_dmamap_destroy + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_pc_dmamap_destroy(struct usb2_page_cache *pc) +{ + if (pc && pc->tag) { + bus_dmamap_destroy(pc->tag, pc->map); + pc->tag = NULL; + pc->map = NULL; + } + return; +} + +#endif + +#ifdef __NetBSD__ + +/*------------------------------------------------------------------------* + * usb2_dma_tag_create - allocate a DMA tag + * + * NOTE: If the "align" parameter has a value of 1 the DMA-tag will + * allow multi-segment mappings. Else all mappings are single-segment. + *------------------------------------------------------------------------*/ +static void +usb2_dma_tag_create(struct usb2_dma_tag *udt, + uint32_t size, uint32_t align) +{ + uint32_t nseg; + + if (align == 1) { + nseg = (2 + (size / USB_PAGE_SIZE)); + } else { + nseg = 1; + } + + udt->p_seg = malloc(nseg * sizeof(*(udt->p_seg)), + M_USB, M_WAITOK | M_ZERO); + + if (udt->p_seg == NULL) { + return; + } + udt->tag = udt->tag_parent->tag; + udt->n_seg = nseg; + return; +} + +/*------------------------------------------------------------------------* + * usb2_dma_tag_free - free a DMA tag + *------------------------------------------------------------------------*/ +static void +usb2_dma_tag_destroy(struct usb2_dma_tag *udt) +{ + free(udt->p_seg, M_USB); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_common_mem_cb - BUS-DMA callback function + *------------------------------------------------------------------------*/ +static void +usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs, + int nseg, int error, uint8_t isload, uint8_t dolock) +{ + struct usb2_dma_parent_tag *uptag; + struct usb2_page *pg; + uint32_t rem; + uint8_t ext_seg; /* extend last segment */ + + uptag = pc->tag_parent; + + if (error) { + goto done; + } + pg = pc->page_start; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + rem = segs->ds_addr & (USB_PAGE_SIZE - 1); + pc->page_offset_buf = rem; + pc->page_offset_end += rem; + if (nseg < ((pc->page_offset_end + + (USB_PAGE_SIZE - 1)) / USB_PAGE_SIZE)) { + ext_seg = 1; + } else { + ext_seg = 0; + } + nseg--; + + while (nseg > 0) { + nseg--; + segs++; + pg++; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + } + + /* + * XXX The segments we get from BUS-DMA are not aligned, + * XXX so we need to extend the last segment if we are + * XXX unaligned and cross the segment boundary! + */ + if (ext_seg && pc->ismultiseg) { + (pg + 1)->physaddr = pg->physaddr + USB_PAGE_SIZE; + } +done: + if (dolock) + mtx_lock(uptag->mtx); + + uptag->dma_error = (error ? 1 : 0); + if (isload) { + (uptag->func) (uptag); + } + if (dolock) + mtx_unlock(uptag->mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_alloc_mem - allocate DMA'able memory + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, + uint32_t size, uint32_t align) +{ + struct usb2_dma_parent_tag *uptag; + struct usb2_dma_tag *utag; + caddr_t ptr = NULL; + bus_dmamap_t map; + int seg_count; + + uptag = pc->tag_parent; + + if (align != 1) { + /* + * The alignment must be greater or equal to the + * "size" else the object can be split between two + * memory pages and we get a problem! + */ + while (align < size) { + align *= 2; + if (align == 0) { + goto done_5; + } + } + } + /* get the correct DMA tag */ + utag = usb2_dma_tag_find(pc->tag_parent, size, align); + if (utag == NULL) { + goto done_5; + } + if (bus_dmamem_alloc(utag->tag, size, align, 0, utag->p_seg, + utag->n_seg, &seg_count, BUS_DMA_WAITOK)) { + goto done_4; + } + if (bus_dmamem_map(utag->tag, utag->p_seg, seg_count, size, + &ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT)) { + goto done_3; + } + if (bus_dmamap_create(utag->tag, size, utag->n_seg, (align == 1) ? + USB_PAGE_SIZE : size, 0, BUS_DMA_WAITOK, &map)) { + goto done_2; + } + if (bus_dmamap_load(utag->tag, map, ptr, size, NULL, + BUS_DMA_WAITOK)) { + goto done_1; + } + pc->p_seg = malloc(seg_count * sizeof(*(pc->p_seg)), + M_USB, M_WAITOK | M_ZERO); + if (pc->p_seg == NULL) { + goto done_0; + } + /* store number if actual segments used */ + pc->n_seg = seg_count; + + /* make a copy of the segments */ + bcopy(utag->p_seg, pc->p_seg, + seg_count * sizeof(*(pc->p_seg))); + + /* setup page cache */ + pc->buffer = ptr; + pc->page_start = pg; + pc->page_offset_buf = 0; + pc->page_offset_end = size; + pc->map = map; + pc->tag = utag->tag; + pc->ismultiseg = (align == 1); + + usb2_pc_common_mem_cb(pc, utag->p_seg, seg_count, 0, 0, 1); + + bzero(ptr, size); + + usb2_pc_cpu_flush(pc); + + return (0); + +done_0: + bus_dmamap_unload(utag->tag, map); +done_1: + bus_dmamap_destroy(utag->tag, map); +done_2: + bus_dmamem_unmap(utag->tag, ptr, size); +done_3: + bus_dmamem_free(utag->tag, utag->p_seg, seg_count); +done_4: + /* utag is destroyed later */ +done_5: + /* reset most of the page cache */ + pc->buffer = NULL; + pc->page_start = NULL; + pc->page_offset_buf = 0; + pc->page_offset_end = 0; + pc->map = NULL; + pc->tag = NULL; + pc->n_seg = 0; + pc->p_seg = NULL; + return (1); +} + +/*------------------------------------------------------------------------* + * usb2_pc_free_mem - free DMA memory + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_pc_free_mem(struct usb2_page_cache *pc) +{ + if (pc && pc->buffer) { + bus_dmamap_unload(pc->tag, pc->map); + bus_dmamap_destroy(pc->tag, pc->map); + bus_dmamem_unmap(pc->tag, pc->buffer, + pc->page_offset_end - pc->page_offset_buf); + bus_dmamem_free(pc->tag, pc->p_seg, pc->n_seg); + free(pc->p_seg, M_USB); + pc->buffer = NULL; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_load_mem - load virtual memory into DMA + * + * Return values: + * 0: Success + * Else: Error + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync) +{ + int error; + + /* setup page cache */ + pc->page_offset_buf = 0; + pc->page_offset_end = size; + pc->ismultiseg = 1; + + if (size > 0) { + + /* try to load memory into DMA using using no wait option */ + if (bus_dmamap_load(pc->tag, pc->map, pc->buffer, + size, NULL, BUS_DMA_NOWAIT)) { + error = ENOMEM; + } else { + error = 0; + } + + usb2_pc_common_mem_cb(pc, pc->map->dm_segs, + pc->map->dm_nsegs, error, !sync); + + if (error) { + return (1); + } + } else { + if (!sync) { + /* + * Call callback so that refcount is decremented + * properly: + */ + pc->tag_parent->dma_error = 0; + (pc->tag_parent->func) (pc->tag_parent); + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_pc_cpu_invalidate - invalidate CPU cache + *------------------------------------------------------------------------*/ +void +usb2_pc_cpu_invalidate(struct usb2_page_cache *pc) +{ + uint32_t len; + + len = pc->page_offset_end - pc->page_offset_buf; + + bus_dmamap_sync(pc->tag, pc->map, 0, len, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_cpu_flush - flush CPU cache + *------------------------------------------------------------------------*/ +void +usb2_pc_cpu_flush(struct usb2_page_cache *pc) +{ + uint32_t len; + + len = pc->page_offset_end - pc->page_offset_buf; + + bus_dmamap_sync(pc->tag, pc->map, 0, len, + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pc_dmamap_create - create a DMA map + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size) +{ + struct usb2_xfer_root *info; + struct usb2_dma_tag *utag; + + /* get info */ + info = pc->tag_parent->info; + + /* sanity check */ + if (info == NULL) { + goto error; + } + utag = usb2_dma_tag_find(pc->tag_parent, size, 1); + if (utag == NULL) { + goto error; + } + if (bus_dmamap_create(utag->tag, size, utag->n_seg, + USB_PAGE_SIZE, 0, BUS_DMA_WAITOK, &pc->map)) { + goto error; + } + pc->tag = utag->tag; + pc->p_seg = utag->p_seg; + pc->n_seg = utag->n_seg; + return 0; /* success */ + +error: + pc->map = NULL; + pc->tag = NULL; + pc->p_seg = NULL; + pc->n_seg = 0; + return 1; /* failure */ +} + +/*------------------------------------------------------------------------* + * usb2_pc_dmamap_destroy + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_pc_dmamap_destroy(struct usb2_page_cache *pc) +{ + if (pc && pc->tag) { + bus_dmamap_destroy(pc->tag, pc->map); + pc->tag = NULL; + pc->map = NULL; + } + return; +} + +#endif + +/*------------------------------------------------------------------------* + * usb2_dma_tag_find - factored out code + *------------------------------------------------------------------------*/ +struct usb2_dma_tag * +usb2_dma_tag_find(struct usb2_dma_parent_tag *udpt, + uint32_t size, uint32_t align) +{ + struct usb2_dma_tag *udt; + uint8_t nudt; + + USB_ASSERT(align > 0, ("Invalid parameter align = 0!\n")); + USB_ASSERT(size > 0, ("Invalid parameter size = 0!\n")); + + udt = udpt->utag_first; + nudt = udpt->utag_max; + + while (nudt--) { + + if (udt->align == 0) { + usb2_dma_tag_create(udt, size, align); + if (udt->tag == NULL) { + return (NULL); + } + udt->align = align; + udt->size = size; + return (udt); + } + if ((udt->align == align) && (udt->size == size)) { + return (udt); + } + udt++; + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_dma_tag_setup - initialise USB DMA tags + *------------------------------------------------------------------------*/ +void +usb2_dma_tag_setup(struct usb2_dma_parent_tag *udpt, + struct usb2_dma_tag *udt, bus_dma_tag_t dmat, + struct mtx *mtx, usb2_dma_callback_t *func, + struct usb2_xfer_root *info, uint8_t ndmabits, + uint8_t nudt) +{ + bzero(udpt, sizeof(*udpt)); + + /* sanity checking */ + if ((nudt == 0) || + (ndmabits == 0) || + (mtx == NULL)) { + /* something is corrupt */ + return; + } +#ifdef __FreeBSD__ + /* initialise condition variable */ + usb2_cv_init(udpt->cv, "USB DMA CV"); +#endif + + /* store some information */ + udpt->mtx = mtx; + udpt->info = info; + udpt->func = func; + udpt->tag = dmat; + udpt->utag_first = udt; + udpt->utag_max = nudt; + udpt->dma_bits = ndmabits; + + while (nudt--) { + bzero(udt, sizeof(*udt)); + udt->tag_parent = udpt; + udt++; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_tag_unsetup - factored out code + *------------------------------------------------------------------------*/ +void +usb2_dma_tag_unsetup(struct usb2_dma_parent_tag *udpt) +{ + struct usb2_dma_tag *udt; + uint8_t nudt; + + udt = udpt->utag_first; + nudt = udpt->utag_max; + + while (nudt--) { + + if (udt->align) { + /* destroy the USB DMA tag */ + usb2_dma_tag_destroy(udt); + udt->align = 0; + } + udt++; + } + + if (udpt->utag_max) { +#ifdef __FreeBSD__ + /* destroy the condition variable */ + usb2_cv_destroy(udpt->cv); +#endif + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bdma_work_loop + * + * This function handles loading of virtual buffers into DMA and is + * only called when "dma_refcount" is zero. + *------------------------------------------------------------------------*/ +void +usb2_bdma_work_loop(struct usb2_xfer_queue *pq) +{ + struct usb2_xfer_root *info; + struct usb2_xfer *xfer; + uint32_t nframes; + + xfer = pq->curr; + info = xfer->usb2_root; + + mtx_assert(info->priv_mtx, MA_OWNED); + + if (xfer->error) { + /* some error happened */ + mtx_lock(xfer->usb2_mtx); + usb2_transfer_done(xfer, 0); + mtx_unlock(xfer->usb2_mtx); + return; + } + if (!xfer->flags_int.bdma_setup) { + struct usb2_page *pg; + uint32_t frlength_0; + uint8_t isread; + + xfer->flags_int.bdma_setup = 1; + + /* reset BUS-DMA load state */ + + info->dma_error = 0; + + if (xfer->flags_int.isochronous_xfr) { + /* only one frame buffer */ + nframes = 1; + frlength_0 = xfer->sumlen; + } else { + /* can be multiple frame buffers */ + nframes = xfer->nframes; + frlength_0 = xfer->frlengths[0]; + } + + /* + * Set DMA direction first. This is needed to + * select the correct cache invalidate and cache + * flush operations. + */ + isread = USB_GET_DATA_ISREAD(xfer); + pg = xfer->dma_page_ptr; + + if (xfer->flags_int.control_xfr && + xfer->flags_int.control_hdr) { + /* special case */ + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + /* The device controller writes to memory */ + xfer->frbuffers[0].isread = 1; + } else { + /* The host controller reads from memory */ + xfer->frbuffers[0].isread = 0; + } + } else { + /* default case */ + xfer->frbuffers[0].isread = isread; + } + + /* + * Setup the "page_start" pointer which points to an array of + * USB pages where information about the physical address of a + * page will be stored. Also initialise the "isread" field of + * the USB page caches. + */ + xfer->frbuffers[0].page_start = pg; + + info->dma_nframes = nframes; + info->dma_currframe = 0; + info->dma_frlength_0 = frlength_0; + + pg += (frlength_0 / USB_PAGE_SIZE); + pg += 2; + + while (--nframes > 0) { + xfer->frbuffers[nframes].isread = isread; + xfer->frbuffers[nframes].page_start = pg; + + pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); + pg += 2; + } + + } + if (info->dma_error) { + mtx_lock(xfer->usb2_mtx); + usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); + mtx_unlock(xfer->usb2_mtx); + return; + } + if (info->dma_currframe != info->dma_nframes) { + + if (info->dma_currframe == 0) { + /* special case */ + usb2_pc_load_mem(xfer->frbuffers, + info->dma_frlength_0, 0); + } else { + /* default case */ + nframes = info->dma_currframe; + usb2_pc_load_mem(xfer->frbuffers + nframes, + xfer->frlengths[nframes], 0); + } + + /* advance frame index */ + info->dma_currframe++; + + return; + } + /* go ahead */ + usb2_bdma_pre_sync(xfer); + + /* start loading next USB transfer, if any */ + usb2_command_wrapper(pq, NULL); + + /* finally start the hardware */ + usb2_pipe_enter(xfer); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_bdma_done_event + * + * This function is called when the BUS-DMA has loaded virtual memory + * into DMA, if any. + *------------------------------------------------------------------------*/ +void +usb2_bdma_done_event(struct usb2_dma_parent_tag *udpt) +{ + struct usb2_xfer_root *info; + + info = udpt->info; + + mtx_assert(info->priv_mtx, MA_OWNED); + + /* copy error */ + info->dma_error = udpt->dma_error; + + /* enter workloop again */ + usb2_command_wrapper(&info->dma_q, + info->dma_q.curr); + return; +} + +/*------------------------------------------------------------------------* + * usb2_bdma_pre_sync + * + * This function handles DMA synchronisation that must be done before + * an USB transfer is started. + *------------------------------------------------------------------------*/ +void +usb2_bdma_pre_sync(struct usb2_xfer *xfer) +{ + struct usb2_page_cache *pc; + uint32_t nframes; + + if (xfer->flags_int.isochronous_xfr) { + /* only one frame buffer */ + nframes = 1; + } else { + /* can be multiple frame buffers */ + nframes = xfer->nframes; + } + + pc = xfer->frbuffers; + + while (nframes--) { + + if (pc->page_offset_buf != pc->page_offset_end) { + if (pc->isread) { + usb2_pc_cpu_invalidate(pc); + } else { + usb2_pc_cpu_flush(pc); + } + } + pc++; + } + + return; +} + +/*------------------------------------------------------------------------* + * usb2_bdma_post_sync + * + * This function handles DMA synchronisation that must be done after + * an USB transfer is complete. + *------------------------------------------------------------------------*/ +void +usb2_bdma_post_sync(struct usb2_xfer *xfer) +{ + struct usb2_page_cache *pc; + uint32_t nframes; + + if (xfer->flags_int.isochronous_xfr) { + /* only one frame buffer */ + nframes = 1; + } else { + /* can be multiple frame buffers */ + nframes = xfer->nframes; + } + + pc = xfer->frbuffers; + + while (nframes--) { + + if (pc->page_offset_buf != pc->page_offset_end) { + if (pc->isread) { + usb2_pc_cpu_invalidate(pc); + } + } + pc++; + } + return; +} diff --git a/sys/dev/usb2/core/usb2_busdma.h b/sys/dev/usb2/core/usb2_busdma.h new file mode 100644 index 000000000000..0b035995e624 --- /dev/null +++ b/sys/dev/usb2/core/usb2_busdma.h @@ -0,0 +1,169 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_BUSDMA_H_ +#define _USB2_BUSDMA_H_ + +#include +#include + +#include + +/* defines */ + +#define USB_PAGE_SIZE PAGE_SIZE /* use system PAGE_SIZE */ + +#ifdef __FreeBSD__ +#if (__FreeBSD_version >= 700020) +#define USB_GET_DMA_TAG(dev) bus_get_dma_tag(dev) +#else +#define USB_GET_DMA_TAG(dev) NULL /* XXX */ +#endif +#endif + +/* structure prototypes */ + +struct usb2_xfer_root; +struct usb2_dma_parent_tag; + +/* + * The following typedef defines the USB DMA load done callback. + */ + +typedef void (usb2_dma_callback_t)(struct usb2_dma_parent_tag *udpt); + +/* + * The following structure defines physical and non kernel virtual + * address of a memory page having size USB_PAGE_SIZE. + */ +struct usb2_page { + bus_size_t physaddr; + void *buffer; /* non Kernel Virtual Address */ +}; + +/* + * The following structure is used when needing the kernel virtual + * pointer and the physical address belonging to an offset in an USB + * page cache. + */ +struct usb2_page_search { + void *buffer; + bus_size_t physaddr; + uint32_t length; +}; + +/* + * The following structure is used to keep information about a DMA + * memory allocation. + */ +struct usb2_page_cache { + +#ifdef __FreeBSD__ + bus_dma_tag_t tag; + bus_dmamap_t map; +#endif +#ifdef __NetBSD__ + bus_dma_tag_t tag; + bus_dmamap_t map; + bus_dma_segment_t *p_seg; +#endif + struct usb2_page *page_start; + struct usb2_dma_parent_tag *tag_parent; /* always set */ + void *buffer; /* virtual buffer pointer */ +#ifdef __NetBSD__ + int n_seg; +#endif + uint32_t page_offset_buf; + uint32_t page_offset_end; + uint8_t isread:1; /* set if we are currently reading + * from the memory. Else write. */ + uint8_t ismultiseg:1; /* set if we can have multiple + * segments */ +}; + +/* + * The following structure describes the parent USB DMA tag. + */ +struct usb2_dma_parent_tag { +#ifdef __FreeBSD__ + struct cv cv[1]; /* internal condition variable */ +#endif + + bus_dma_tag_t tag; /* always set */ + + struct mtx *mtx; /* private mutex, always set */ + struct usb2_xfer_root *info; /* used by the callback function */ + usb2_dma_callback_t *func; /* load complete callback function */ + struct usb2_dma_tag *utag_first;/* pointer to first USB DMA tag */ + + uint8_t dma_error; /* set if DMA load operation failed */ + uint8_t dma_bits; /* number of DMA address lines */ + uint8_t utag_max; /* number of USB DMA tags */ +}; + +/* + * The following structure describes an USB DMA tag. + */ +struct usb2_dma_tag { +#ifdef __NetBSD__ + bus_dma_segment_t *p_seg; +#endif + struct usb2_dma_parent_tag *tag_parent; + bus_dma_tag_t tag; + + uint32_t align; + uint32_t size; +#ifdef __NetBSD__ + uint32_t n_seg; +#endif +}; + +/* function prototypes */ + +int usb2_uiomove(struct usb2_page_cache *pc, struct uio *uio, uint32_t pc_offset, uint32_t len); +struct usb2_dma_tag *usb2_dma_tag_find(struct usb2_dma_parent_tag *udpt, uint32_t size, uint32_t align); +uint8_t usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align); +uint8_t usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size); +uint8_t usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync); +void usb2_bdma_done_event(struct usb2_dma_parent_tag *udpt); +void usb2_bdma_post_sync(struct usb2_xfer *xfer); +void usb2_bdma_pre_sync(struct usb2_xfer *xfer); +void usb2_bdma_work_loop(struct usb2_xfer_queue *pq); +void usb2_bzero(struct usb2_page_cache *cache, uint32_t offset, uint32_t len); +void usb2_copy_in(struct usb2_page_cache *cache, uint32_t offset, const void *ptr, uint32_t len); +int usb2_copy_in_user(struct usb2_page_cache *cache, uint32_t offset, const void *ptr, uint32_t len); +void usb2_copy_out(struct usb2_page_cache *cache, uint32_t offset, void *ptr, uint32_t len); +int usb2_copy_out_user(struct usb2_page_cache *cache, uint32_t offset, void *ptr, uint32_t len); +void usb2_dma_tag_setup(struct usb2_dma_parent_tag *udpt, struct usb2_dma_tag *udt, bus_dma_tag_t dmat, struct mtx *mtx, usb2_dma_callback_t *func, struct usb2_xfer_root *info, uint8_t ndmabits, uint8_t nudt); +void usb2_dma_tag_unsetup(struct usb2_dma_parent_tag *udpt); +void usb2_get_page(struct usb2_page_cache *pc, uint32_t offset, struct usb2_page_search *res); +void usb2_m_copy_in(struct usb2_page_cache *cache, uint32_t dst_offset, struct mbuf *m, uint32_t src_offset, uint32_t src_len); +void usb2_pc_cpu_flush(struct usb2_page_cache *pc); +void usb2_pc_cpu_invalidate(struct usb2_page_cache *pc); +void usb2_pc_dmamap_destroy(struct usb2_page_cache *pc); +void usb2_pc_free_mem(struct usb2_page_cache *pc); + +#endif /* _USB2_BUSDMA_H_ */ diff --git a/sys/dev/usb2/core/usb2_compat_linux.c b/sys/dev/usb2/core/usb2_compat_linux.c new file mode 100644 index 000000000000..7b0212d11891 --- /dev/null +++ b/sys/dev/usb2/core/usb2_compat_linux.c @@ -0,0 +1,1659 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved. + * Copyright (c) 2007 Hans Petter Selasky. 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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usb_linux_softc { + LIST_ENTRY(usb_linux_softc) sc_attached_list; + + device_t sc_fbsd_dev; + struct usb2_device *sc_fbsd_udev; + struct usb_interface *sc_ui; + struct usb_driver *sc_udrv; +}; + +/* prototypes */ +static device_probe_t usb_linux_probe; +static device_attach_t usb_linux_attach; +static device_detach_t usb_linux_detach; +static device_suspend_t usb_linux_suspend; +static device_resume_t usb_linux_resume; +static device_shutdown_t usb_linux_shutdown; + +static usb2_callback_t usb_linux_isoc_callback; +static usb2_callback_t usb_linux_non_isoc_callback; + +static usb_complete_t usb_linux_wait_complete; + +static uint16_t usb_max_isoc_frames(struct usb_device *dev); +static int usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen); +static const struct usb_device_id *usb_linux_lookup_id(const struct usb_device_id *id, struct usb2_attach_arg *uaa); +static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *sc); +static struct usb_device *usb_linux_create_usb_device(struct usb2_device *udev, device_t dev); +static void usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface); +static void usb_linux_complete(struct usb2_xfer *xfer); +static int usb_unlink_urb_sub(struct urb *urb, uint8_t drain); + +/*------------------------------------------------------------------------* + * FreeBSD USB interface + *------------------------------------------------------------------------*/ + +static LIST_HEAD(, usb_linux_softc) usb_linux_attached_list; +static LIST_HEAD(, usb_driver) usb_linux_driver_list; + +static device_method_t usb_linux_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, usb_linux_probe), + DEVMETHOD(device_attach, usb_linux_attach), + DEVMETHOD(device_detach, usb_linux_detach), + DEVMETHOD(device_suspend, usb_linux_suspend), + DEVMETHOD(device_resume, usb_linux_resume), + DEVMETHOD(device_shutdown, usb_linux_shutdown), + + {0, 0} +}; + +static driver_t usb_linux_driver = { + .name = "usb_linux", + .methods = usb_linux_methods, + .size = sizeof(struct usb_linux_softc), +}; + +static devclass_t usb_linux_devclass; + +DRIVER_MODULE(usb_linux, ushub, usb_linux_driver, usb_linux_devclass, NULL, 0); + +/*------------------------------------------------------------------------* + * usb_linux_lookup_id + * + * This functions takes an array of "struct usb_device_id" and tries + * to match the entries with the information in "struct usb2_attach_arg". + * If it finds a match the matching entry will be returned. + * Else "NULL" will be returned. + *------------------------------------------------------------------------*/ +static const struct usb_device_id * +usb_linux_lookup_id(const struct usb_device_id *id, struct usb2_attach_arg *uaa) +{ + if (id == NULL) { + goto done; + } + /* + * Keep on matching array entries until we find one with + * "match_flags" equal to zero, which indicates the end of the + * array: + */ + for (; id->match_flags; id++) { + + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->idVendor != uaa->info.idVendor)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + (id->idProduct != uaa->info.idProduct)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > uaa->info.bcdDevice)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < uaa->info.bcdDevice)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != uaa->info.bDeviceClass)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass != uaa->info.bDeviceSubClass)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != uaa->info.bDeviceProtocol)) { + continue; + } + if ((uaa->info.bDeviceClass == 0xFF) && + !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL))) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (id->bInterfaceClass != uaa->info.bInterfaceClass)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && + (id->bInterfaceSubClass != uaa->info.bInterfaceSubClass)) { + continue; + } + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && + (id->bInterfaceProtocol != uaa->info.bInterfaceProtocol)) { + continue; + } + /* we found a match! */ + return (id); + } + +done: + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb_linux_probe + * + * This function is the FreeBSD probe callback. It is called from the + * FreeBSD USB stack through the "device_probe_and_attach()" function. + *------------------------------------------------------------------------*/ +static int +usb_linux_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb_driver *udrv; + int err = ENXIO; + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + mtx_lock(&Giant); + LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { + if (usb_linux_lookup_id(udrv->id_table, uaa)) { + err = 0; + break; + } + } + mtx_unlock(&Giant); + + return (err); +} + +/*------------------------------------------------------------------------* + * usb_linux_get_usb_driver + * + * This function returns the pointer to the "struct usb_driver" where + * the Linux USB device driver "struct usb_device_id" match was found. + * We apply a lock before reading out the pointer to avoid races. + *------------------------------------------------------------------------*/ +static struct usb_driver * +usb_linux_get_usb_driver(struct usb_linux_softc *sc) +{ + struct usb_driver *udrv; + + mtx_lock(&Giant); + udrv = sc->sc_udrv; + mtx_unlock(&Giant); + return (udrv); +} + +/*------------------------------------------------------------------------* + * usb_linux_attach + * + * This function is the FreeBSD attach callback. It is called from the + * FreeBSD USB stack through the "device_probe_and_attach()" function. + * This function is called when "usb_linux_probe()" returns zero. + *------------------------------------------------------------------------*/ +static int +usb_linux_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb_linux_softc *sc = device_get_softc(dev); + struct usb_driver *udrv; + struct usb_device *p_dev; + const struct usb_device_id *id = NULL; + + if (sc == NULL) { + return (ENOMEM); + } + mtx_lock(&Giant); + LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { + id = usb_linux_lookup_id(udrv->id_table, uaa); + if (id) + break; + } + mtx_unlock(&Giant); + + if (id == NULL) { + return (ENXIO); + } + /* + * Save some memory and only create the Linux compat structure when + * needed: + */ + p_dev = uaa->device->linux_dev; + if (p_dev == NULL) { + p_dev = usb_linux_create_usb_device(uaa->device, dev); + if (p_dev == NULL) { + return (ENOMEM); + } + uaa->device->linux_dev = p_dev; + } + device_set_usb2_desc(dev); + + sc->sc_fbsd_udev = uaa->device; + sc->sc_fbsd_dev = dev; + sc->sc_udrv = udrv; + sc->sc_ui = usb_ifnum_to_if(p_dev, uaa->info.bIfaceNum); + if (sc->sc_ui == NULL) { + return (EINVAL); + } + if (udrv->probe) { + if ((udrv->probe) (sc->sc_ui, id)) { + return (ENXIO); + } + } + mtx_lock(&Giant); + LIST_INSERT_HEAD(&usb_linux_attached_list, sc, sc_attached_list); + mtx_unlock(&Giant); + + /* success */ + return (0); +} + +/*------------------------------------------------------------------------* + * usb_linux_detach + * + * This function is the FreeBSD detach callback. It is called from the + * FreeBSD USB stack through the "device_detach()" function. + *------------------------------------------------------------------------*/ +static int +usb_linux_detach(device_t dev) +{ + struct usb_linux_softc *sc = device_get_softc(dev); + struct usb_driver *udrv = NULL; + + mtx_lock(&Giant); + if (sc->sc_attached_list.le_prev) { + LIST_REMOVE(sc, sc_attached_list); + sc->sc_attached_list.le_prev = NULL; + udrv = sc->sc_udrv; + sc->sc_udrv = NULL; + } + mtx_unlock(&Giant); + + if (udrv && udrv->disconnect) { + (udrv->disconnect) (sc->sc_ui); + } + /* + * Make sure that we free all FreeBSD USB transfers belonging to + * this Linux "usb_interface", hence they will most likely not be + * needed any more. + */ + usb_linux_cleanup_interface(sc->sc_fbsd_udev->linux_dev, sc->sc_ui); + return (0); +} + +/*------------------------------------------------------------------------* + * usb_linux_suspend + * + * This function is the FreeBSD suspend callback. Usually it does nothing. + *------------------------------------------------------------------------*/ +static int +usb_linux_suspend(device_t dev) +{ + struct usb_linux_softc *sc = device_get_softc(dev); + struct usb_driver *udrv = usb_linux_get_usb_driver(sc); + int err; + + if (udrv && udrv->suspend) { + err = (udrv->suspend) (sc->sc_ui, 0); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_linux_resume + * + * This function is the FreeBSD resume callback. Usually it does nothing. + *------------------------------------------------------------------------*/ +static int +usb_linux_resume(device_t dev) +{ + struct usb_linux_softc *sc = device_get_softc(dev); + struct usb_driver *udrv = usb_linux_get_usb_driver(sc); + int err; + + if (udrv && udrv->resume) { + err = (udrv->resume) (sc->sc_ui); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_linux_shutdown + * + * This function is the FreeBSD shutdown callback. Usually it does nothing. + *------------------------------------------------------------------------*/ +static int +usb_linux_shutdown(device_t dev) +{ + struct usb_linux_softc *sc = device_get_softc(dev); + struct usb_driver *udrv = usb_linux_get_usb_driver(sc); + + if (udrv && udrv->shutdown) { + (udrv->shutdown) (sc->sc_ui); + } + return (0); +} + +/*------------------------------------------------------------------------* + * Linux emulation layer + *------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------* + * usb_max_isoc_frames + * + * The following function returns the maximum number of isochronous + * frames that we support per URB. It is not part of the Linux USB API. + *------------------------------------------------------------------------*/ +static uint16_t +usb_max_isoc_frames(struct usb_device *dev) +{ + return ((usb2_get_speed(dev->bsd_udev) == USB_SPEED_HIGH) ? + USB_MAX_HIGH_SPEED_ISOC_FRAMES : USB_MAX_FULL_SPEED_ISOC_FRAMES); +} + +/*------------------------------------------------------------------------* + * usb_submit_urb + * + * This function is used to queue an URB after that it has been + * initialized. If it returns non-zero, it means that the URB was not + * queued. + *------------------------------------------------------------------------*/ +int +usb_submit_urb(struct urb *urb, uint16_t mem_flags) +{ + struct usb_host_endpoint *uhe; + + if (urb == NULL) { + return (-EINVAL); + } + mtx_assert(&Giant, MA_OWNED); + + if (urb->pipe == NULL) { + return (-EINVAL); + } + uhe = urb->pipe; + + /* + * Check that we have got a FreeBSD USB transfer that will dequeue + * the URB structure and do the real transfer. If there are no USB + * transfers, then we return an error. + */ + if (uhe->bsd_xfer[0] || + uhe->bsd_xfer[1]) { + /* we are ready! */ + + TAILQ_INSERT_HEAD(&uhe->bsd_urb_list, urb, bsd_urb_list); + + urb->status = -EINPROGRESS; + + usb2_transfer_start(uhe->bsd_xfer[0]); + usb2_transfer_start(uhe->bsd_xfer[1]); + } else { + /* no pipes have been setup yet! */ + urb->status = -EINVAL; + return (-EINVAL); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_unlink_urb + * + * This function is used to stop an URB after that it is been + * submitted, but before the "complete" callback has been called. On + *------------------------------------------------------------------------*/ +int +usb_unlink_urb(struct urb *urb) +{ + return (usb_unlink_urb_sub(urb, 0)); +} + +static void +usb_unlink_bsd(struct usb2_xfer *xfer, + struct urb *urb, uint8_t drain) +{ + if (xfer && + usb2_transfer_pending(xfer) && + (xfer->priv_fifo == (void *)urb)) { + if (drain) { + mtx_unlock(&Giant); + usb2_transfer_drain(xfer); + mtx_lock(&Giant); + } else { + usb2_transfer_stop(xfer); + } + usb2_transfer_start(xfer); + } + return; +} + +static int +usb_unlink_urb_sub(struct urb *urb, uint8_t drain) +{ + struct usb_host_endpoint *uhe; + uint16_t x; + + if (urb == NULL) { + return (-EINVAL); + } + mtx_assert(&Giant, MA_OWNED); + + if (urb->pipe == NULL) { + return (-EINVAL); + } + uhe = urb->pipe; + + if (urb->bsd_urb_list.tqe_prev) { + + /* not started yet, just remove it from the queue */ + TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); + urb->bsd_urb_list.tqe_prev = NULL; + urb->status = -ECONNRESET; + urb->actual_length = 0; + + for (x = 0; x < urb->number_of_packets; x++) { + urb->iso_frame_desc[x].actual_length = 0; + } + + if (urb->complete) { + (urb->complete) (urb); + } + } else { + + /* + * If the URB is not on the URB list, then check if one of + * the FreeBSD USB transfer are processing the current URB. + * If so, re-start that transfer, which will lead to the + * termination of that URB: + */ + usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain); + usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_clear_halt + * + * This function must always be used to clear the stall. Stall is when + * an USB endpoint returns a stall message to the USB host controller. + * Until the stall is cleared, no data can be transferred. + *------------------------------------------------------------------------*/ +int +usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe) +{ + struct usb2_config cfg[1]; + struct usb2_pipe *pipe; + uint8_t type; + uint8_t addr; + + if (uhe == NULL) + return (-EINVAL); + + type = uhe->desc.bmAttributes & UE_XFERTYPE; + addr = uhe->desc.bEndpointAddress; + + bzero(cfg, sizeof(cfg)); + + cfg[0].type = type; + cfg[0].endpoint = addr & UE_ADDR; + cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); + + pipe = usb2_get_pipe(dev->bsd_udev, uhe->bsd_iface_index, cfg); + if (pipe == NULL) + return (-EINVAL); + + usb2_clear_data_toggle(dev->bsd_udev, pipe); + + return (usb_control_msg(dev, &dev->ep0, + UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT, + UF_ENDPOINT_HALT, addr, NULL, 0, 1000)); +} + +/*------------------------------------------------------------------------* + * usb_start_wait_urb + * + * This is an internal function that is used to perform synchronous + * Linux USB transfers. + *------------------------------------------------------------------------*/ +static int +usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen) +{ + int err; + + /* you must have a timeout! */ + if (timeout == 0) { + timeout = 1; + } + urb->complete = &usb_linux_wait_complete; + urb->timeout = timeout; + urb->transfer_flags |= URB_WAIT_WAKEUP; + urb->transfer_flags &= ~URB_IS_SLEEPING; + + err = usb_submit_urb(urb, 0); + if (err) + goto done; + + /* + * the URB might have completed before we get here, so check that by + * using some flags! + */ + while (urb->transfer_flags & URB_WAIT_WAKEUP) { + urb->transfer_flags |= URB_IS_SLEEPING; + usb2_cv_wait(&urb->cv_wait, &Giant); + urb->transfer_flags &= ~URB_IS_SLEEPING; + } + + err = urb->status; + +done: + if (err) { + *p_actlen = 0; + } else { + *p_actlen = urb->actual_length; + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb_control_msg + * + * The following function performs a control transfer sequence one any + * control, bulk or interrupt endpoint, specified by "uhe". A control + * transfer means that you transfer an 8-byte header first followed by + * a data-phase as indicated by the 8-byte header. The "timeout" is + * given in milliseconds. + * + * Return values: + * 0: Success + * < 0: Failure + * > 0: Acutal length + *------------------------------------------------------------------------*/ +int +usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe, + uint8_t request, uint8_t requesttype, + uint16_t value, uint16_t index, void *data, + uint16_t size, uint32_t timeout) +{ + struct usb2_device_request req; + struct urb *urb; + int err; + uint16_t actlen; + uint8_t type; + uint8_t addr; + + req.bmRequestType = requesttype; + req.bRequest = request; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, size); + + if (uhe == NULL) { + return (-EINVAL); + } + type = (uhe->desc.bmAttributes & UE_XFERTYPE); + addr = (uhe->desc.bEndpointAddress & UE_ADDR); + + if (type != UE_CONTROL) { + return (-EINVAL); + } + if (addr == 0) { + /* + * The FreeBSD USB stack supports standard control + * transfers on control endpoint zero: + */ + err = usb2_do_request_flags(dev->bsd_udev, + &Giant, &req, data, USB_SHORT_XFER_OK, + &actlen, timeout); + if (err) { + err = -EPIPE; + } else { + err = actlen; + } + return (err); + } + if (dev->bsd_udev->flags.usb2_mode != USB_MODE_HOST) { + /* not supported */ + return (-EINVAL); + } + err = usb_setup_endpoint(dev, uhe, 1 /* dummy */ ); + + /* + * NOTE: we need to allocate real memory here so that we don't + * transfer data to/from the stack! + * + * 0xFFFF is a FreeBSD specific magic value. + */ + urb = usb_alloc_urb(0xFFFF, size); + if (urb == NULL) + return (-ENOMEM); + + urb->dev = dev; + urb->pipe = uhe; + + bcopy(&req, urb->setup_packet, sizeof(req)); + + if (size && (!(req.bmRequestType & UT_READ))) { + /* move the data to a real buffer */ + bcopy(data, USB_ADD_BYTES(urb->setup_packet, + sizeof(req)), size); + } + err = usb_start_wait_urb(urb, timeout, &actlen); + + if (req.bmRequestType & UT_READ) { + if (actlen) { + bcopy(USB_ADD_BYTES(urb->setup_packet, + sizeof(req)), data, actlen); + } + } + usb_free_urb(urb); + + if (err == 0) { + err = actlen; + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb_set_interface + * + * The following function will select which alternate setting of an + * USB interface you plan to use. By default alternate setting with + * index zero is selected. Note that "iface_no" is not the interface + * index, but rather the value of "bInterfaceNumber". + *------------------------------------------------------------------------*/ +int +usb_set_interface(struct usb_device *dev, uint8_t iface_no, uint8_t alt_index) +{ + struct usb_interface *p_ui = usb_ifnum_to_if(dev, iface_no); + int err; + + if (p_ui == NULL) + return (-EINVAL); + if (alt_index >= p_ui->num_altsetting) + return (-EINVAL); + usb_linux_cleanup_interface(dev, p_ui); + err = -usb2_set_alt_interface_index(dev->bsd_udev, + p_ui->bsd_iface_index, alt_index); + if (err == 0) { + p_ui->cur_altsetting = p_ui->altsetting + alt_index; + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb_setup_endpoint + * + * The following function is an extension to the Linux USB API that + * allows you to set a maximum buffer size for a given USB endpoint. + * The maximum buffer size is per URB. If you don't call this function + * to set a maximum buffer size, the endpoint will not be functional. + * Note that for isochronous endpoints the maximum buffer size must be + * a non-zero dummy, hence this function will base the maximum buffer + * size on "wMaxPacketSize". + *------------------------------------------------------------------------*/ +int +usb_setup_endpoint(struct usb_device *dev, + struct usb_host_endpoint *uhe, uint32_t bufsize) +{ + struct usb2_config cfg[2]; + uint8_t type = uhe->desc.bmAttributes & UE_XFERTYPE; + uint8_t addr = uhe->desc.bEndpointAddress; + + if (uhe->fbsd_buf_size == bufsize) { + /* optimize */ + return (0); + } + usb2_transfer_unsetup(uhe->bsd_xfer, 2); + + uhe->fbsd_buf_size = bufsize; + + if (bufsize == 0) { + return (0); + } + bzero(cfg, sizeof(cfg)); + + if (type == UE_ISOCHRONOUS) { + + /* + * Isochronous transfers are special in that they don't fit + * into the BULK/INTR/CONTROL transfer model. + */ + + cfg[0].type = type; + cfg[0].endpoint = addr & UE_ADDR; + cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); + cfg[0].mh.callback = &usb_linux_isoc_callback; + cfg[0].mh.bufsize = 0; /* use wMaxPacketSize */ + cfg[0].mh.frames = usb_max_isoc_frames(dev); + cfg[0].mh.flags.proxy_buffer = 1; +#if 0 + /* + * The Linux USB API allows non back-to-back + * isochronous frames which we do not support. If the + * isochronous frames are not back-to-back we need to + * do a copy, and then we need a buffer for + * that. Enable this at your own risk. + */ + cfg[0].mh.flags.ext_buffer = 1; +#endif + cfg[0].mh.flags.short_xfer_ok = 1; + + bcopy(cfg, cfg + 1, sizeof(*cfg)); + + /* Allocate and setup two generic FreeBSD USB transfers */ + + if (usb2_transfer_setup(dev->bsd_udev, &uhe->bsd_iface_index, + uhe->bsd_xfer, cfg, 2, uhe, &Giant)) { + return (-EINVAL); + } + } else { + if (bufsize > (1 << 22)) { + /* limit buffer size */ + bufsize = (1 << 22); + } + /* Allocate and setup one generic FreeBSD USB transfer */ + + cfg[0].type = type; + cfg[0].endpoint = addr & UE_ADDR; + cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); + cfg[0].mh.callback = &usb_linux_non_isoc_callback; + cfg[0].mh.bufsize = bufsize; + cfg[0].mh.flags.ext_buffer = 1; /* enable zero-copy */ + cfg[0].mh.flags.proxy_buffer = 1; + cfg[0].mh.flags.short_xfer_ok = 1; + + if (usb2_transfer_setup(dev->bsd_udev, &uhe->bsd_iface_index, + uhe->bsd_xfer, cfg, 1, uhe, &Giant)) { + return (-EINVAL); + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_linux_create_usb_device + * + * The following function is used to build up a per USB device + * structure tree, that mimics the Linux one. The root structure + * is returned by this function. + *------------------------------------------------------------------------*/ +static struct usb_device * +usb_linux_create_usb_device(struct usb2_device *udev, device_t dev) +{ + struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev); + struct usb2_descriptor *desc; + struct usb2_interface_descriptor *id; + struct usb2_endpoint_descriptor *ed; + struct usb_device *p_ud = NULL; + struct usb_interface *p_ui = NULL; + struct usb_host_interface *p_uhi = NULL; + struct usb_host_endpoint *p_uhe = NULL; + uint32_t size; + uint16_t niface_total; + uint16_t nedesc; + uint16_t iface_no_curr; + uint16_t iface_index; + uint8_t pass; + uint8_t iface_no; + + /* + * We do two passes. One pass for computing necessary memory size + * and one pass to initialize all the allocated memory structures. + */ + for (pass = 0; pass < 2; pass++) { + + iface_no_curr = 0 - 1; + niface_total = 0; + iface_index = 0; + nedesc = 0; + desc = NULL; + + /* + * Iterate over all the USB descriptors. Use the USB config + * descriptor pointer provided by the FreeBSD USB stack. + */ + while ((desc = usb2_desc_foreach(cd, desc))) { + + /* + * Build up a tree according to the descriptors we + * find: + */ + switch (desc->bDescriptorType) { + case UDESC_DEVICE: + break; + + case UDESC_ENDPOINT: + ed = (void *)desc; + if ((ed->bLength < sizeof(*ed)) || + (iface_index == 0)) + break; + if (p_uhe) { + bcopy(ed, &p_uhe->desc, sizeof(p_uhe->desc)); + p_uhe->bsd_iface_index = iface_index - 1; + p_uhe++; + } + if (p_uhi) { + (p_uhi - 1)->desc.bNumEndpoints++; + } + nedesc++; + break; + + case UDESC_INTERFACE: + id = (void *)desc; + if (id->bLength < sizeof(*id)) + break; + if (p_uhi) { + bcopy(id, &p_uhi->desc, sizeof(p_uhi->desc)); + p_uhi->desc.bNumEndpoints = 0; + p_uhi->endpoint = p_uhe; + p_uhi->string = ""; + p_uhi->bsd_iface_index = iface_index; + p_uhi++; + } + iface_no = id->bInterfaceNumber; + niface_total++; + if (iface_no_curr != iface_no) { + if (p_ui) { + p_ui->altsetting = p_uhi - 1; + p_ui->cur_altsetting = p_uhi - 1; + p_ui->num_altsetting = 1; + p_ui->bsd_iface_index = iface_index; + p_ui->linux_udev = p_ud; + p_ui++; + } + iface_no_curr = iface_no; + iface_index++; + } else { + if (p_ui) { + (p_ui - 1)->num_altsetting++; + } + } + break; + + default: + break; + } + } + + if (pass == 0) { + + size = ((sizeof(*p_ud) * 1) + + (sizeof(*p_uhe) * nedesc) + + (sizeof(*p_ui) * iface_index) + + (sizeof(*p_uhi) * niface_total)); + + p_ud = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); + if (p_ud == NULL) { + goto done; + } + p_uhe = (void *)(p_ud + 1); + p_ui = (void *)(p_uhe + nedesc); + p_uhi = (void *)(p_ui + iface_index); + + p_ud->product = ""; + p_ud->manufacturer = ""; + p_ud->serial = ""; + p_ud->speed = usb2_get_speed(udev); + p_ud->bsd_udev = udev; + p_ud->bsd_iface_start = p_ui; + p_ud->bsd_iface_end = p_ui + iface_index; + p_ud->bsd_endpoint_start = p_uhe; + p_ud->bsd_endpoint_end = p_uhe + nedesc; + p_ud->devnum = device_get_unit(dev); + bcopy(&udev->ddesc, &p_ud->descriptor, + sizeof(p_ud->descriptor)); + bcopy(udev->default_pipe.edesc, &p_ud->ep0.desc, + sizeof(p_ud->ep0.desc)); + } + } +done: + return (p_ud); +} + +/*------------------------------------------------------------------------* + * usb_alloc_urb + * + * This function should always be used when you allocate an URB for + * use with the USB Linux stack. In case of an isochronous transfer + * you must specifiy the maximum number of "iso_packets" which you + * plan to transfer per URB. This function is always blocking, and + * "mem_flags" are not regarded like on Linux. + *------------------------------------------------------------------------*/ +struct urb * +usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags) +{ + struct urb *urb; + uint32_t size; + + if (iso_packets == 0xFFFF) { + /* + * FreeBSD specific magic value to ask for control transfer + * memory allocation: + */ + size = sizeof(*urb) + sizeof(struct usb2_device_request) + mem_flags; + } else { + size = sizeof(*urb) + (iso_packets * sizeof(urb->iso_frame_desc[0])); + } + + urb = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); + if (urb) { + + usb2_cv_init(&urb->cv_wait, "URBWAIT"); + if (iso_packets == 0xFFFF) { + urb->setup_packet = (void *)(urb + 1); + urb->transfer_buffer = (void *)(urb->setup_packet + + sizeof(struct usb2_device_request)); + } else { + urb->number_of_packets = iso_packets; + } + } + return (urb); +} + +/*------------------------------------------------------------------------* + * usb_find_host_endpoint + * + * The following function will return the Linux USB host endpoint + * structure that matches the given endpoint type and endpoint + * value. If no match is found, NULL is returned. This function is not + * part of the Linux USB API and is only used internally. + *------------------------------------------------------------------------*/ +struct usb_host_endpoint * +usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep) +{ + struct usb_host_endpoint *uhe; + struct usb_host_endpoint *uhe_end; + struct usb_host_interface *uhi; + struct usb_interface *ui; + uint8_t ea; + uint8_t at; + uint8_t mask; + + if (dev == NULL) { + return (NULL); + } + if (type == UE_CONTROL) { + mask = UE_ADDR; + } else { + mask = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR); + } + + ep &= mask; + + /* + * Iterate over all the interfaces searching the selected alternate + * setting only, and all belonging endpoints. + */ + for (ui = dev->bsd_iface_start; + ui != dev->bsd_iface_end; + ui++) { + uhi = ui->cur_altsetting; + if (uhi) { + uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; + for (uhe = uhi->endpoint; + uhe != uhe_end; + uhe++) { + ea = uhe->desc.bEndpointAddress; + at = uhe->desc.bmAttributes; + + if (((ea & mask) == ep) && + ((at & UE_XFERTYPE) == type)) { + return (uhe); + } + } + } + } + + if ((type == UE_CONTROL) && ((ep & UE_ADDR) == 0)) { + return (&dev->ep0); + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb_altnum_to_altsetting + * + * The following function returns a pointer to an alternate setting by + * index given a "usb_interface" pointer. If the alternate setting by + * index does not exist, NULL is returned. And alternate setting is a + * variant of an interface, but usually with slightly different + * characteristics. + *------------------------------------------------------------------------*/ +struct usb_host_interface * +usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index) +{ + if (alt_index >= intf->num_altsetting) { + return (NULL); + } + return (intf->altsetting + alt_index); +} + +/*------------------------------------------------------------------------* + * usb_ifnum_to_if + * + * The following function searches up an USB interface by + * "bInterfaceNumber". If no match is found, NULL is returned. + *------------------------------------------------------------------------*/ +struct usb_interface * +usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no) +{ + struct usb_interface *p_ui; + + for (p_ui = dev->bsd_iface_start; + p_ui != dev->bsd_iface_end; + p_ui++) { + if ((p_ui->num_altsetting > 0) && + (p_ui->altsetting->desc.bInterfaceNumber == iface_no)) { + return (p_ui); + } + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb_buffer_alloc + *------------------------------------------------------------------------*/ +void * +usb_buffer_alloc(struct usb_device *dev, uint32_t size, uint16_t mem_flags, uint8_t *dma_addr) +{ + return (malloc(size, M_USBDEV, M_WAITOK | M_ZERO)); +} + +/*------------------------------------------------------------------------* + * usb_get_intfdata + *------------------------------------------------------------------------*/ +void * +usb_get_intfdata(struct usb_interface *intf) +{ + return (intf->bsd_priv_sc); +} + +/*------------------------------------------------------------------------* + * usb_linux_register + * + * The following function is used by the "USB_DRIVER_EXPORT()" macro, + * and is used to register a Linux USB driver, so that its + * "usb_device_id" structures gets searched a probe time. This + * function is not part of the Linux USB API, and is for internal use + * only. + *------------------------------------------------------------------------*/ +void +usb_linux_register(void *arg) +{ + struct usb_driver *drv = arg; + + mtx_lock(&Giant); + LIST_INSERT_HEAD(&usb_linux_driver_list, drv, linux_driver_list); + mtx_unlock(&Giant); + + usb2_needs_explore_all(); + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_deregister + * + * The following function is used by the "USB_DRIVER_EXPORT()" macro, + * and is used to deregister a Linux USB driver. This function will + * ensure that all driver instances belonging to the Linux USB device + * driver in question, gets detached before the driver is + * unloaded. This function is not part of the Linux USB API, and is + * for internal use only. + *------------------------------------------------------------------------*/ +void +usb_linux_deregister(void *arg) +{ + struct usb_driver *drv = arg; + struct usb_linux_softc *sc; + +repeat: + mtx_lock(&Giant); + LIST_FOREACH(sc, &usb_linux_attached_list, sc_attached_list) { + if (sc->sc_udrv == drv) { + mtx_unlock(&Giant); + device_detach(sc->sc_fbsd_dev); + goto repeat; + } + } + LIST_REMOVE(drv, linux_driver_list); + mtx_unlock(&Giant); + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_free_device + * + * The following function is only used by the FreeBSD USB stack, to + * cleanup and free memory after that a Linux USB device was attached. + *------------------------------------------------------------------------*/ +void +usb_linux_free_device(struct usb_device *dev) +{ + struct usb_host_endpoint *uhe; + struct usb_host_endpoint *uhe_end; + int err; + + uhe = dev->bsd_endpoint_start; + uhe_end = dev->bsd_endpoint_end; + while (uhe != uhe_end) { + err = usb_setup_endpoint(dev, uhe, 0); + uhe++; + } + err = usb_setup_endpoint(dev, &dev->ep0, 0); + free(dev, M_USBDEV); + return; +} + +/*------------------------------------------------------------------------* + * usb_buffer_free + *------------------------------------------------------------------------*/ +void +usb_buffer_free(struct usb_device *dev, uint32_t size, + void *addr, uint8_t dma_addr) +{ + free(addr, M_USBDEV); + return; +} + +/*------------------------------------------------------------------------* + * usb_free_urb + *------------------------------------------------------------------------*/ +void +usb_free_urb(struct urb *urb) +{ + if (urb == NULL) { + return; + } + /* make sure that the current URB is not active */ + usb_kill_urb(urb); + + /* destroy condition variable */ + usb2_cv_destroy(&urb->cv_wait); + + /* just free it */ + free(urb, M_USBDEV); + return; +} + +/*------------------------------------------------------------------------* + * usb_init_urb + * + * The following function can be used to initialize a custom URB. It + * is not recommended to use this function. Use "usb_alloc_urb()" + * instead. + *------------------------------------------------------------------------*/ +void +usb_init_urb(struct urb *urb) +{ + if (urb == NULL) { + return; + } + bzero(urb, sizeof(*urb)); + return; +} + +/*------------------------------------------------------------------------* + * usb_kill_urb + *------------------------------------------------------------------------*/ +void +usb_kill_urb(struct urb *urb) +{ + if (usb_unlink_urb_sub(urb, 1)) { + /* ignore */ + } + return; +} + +/*------------------------------------------------------------------------* + * usb_set_intfdata + * + * The following function sets the per Linux USB interface private + * data pointer. It is used by most Linux USB device drivers. + *------------------------------------------------------------------------*/ +void +usb_set_intfdata(struct usb_interface *intf, void *data) +{ + intf->bsd_priv_sc = data; + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_cleanup_interface + * + * The following function will release all FreeBSD USB transfers + * associated with a Linux USB interface. It is for internal use only. + *------------------------------------------------------------------------*/ +static void +usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface) +{ + struct usb_host_interface *uhi; + struct usb_host_interface *uhi_end; + struct usb_host_endpoint *uhe; + struct usb_host_endpoint *uhe_end; + int err; + + uhi = iface->altsetting; + uhi_end = iface->altsetting + iface->num_altsetting; + while (uhi != uhi_end) { + uhe = uhi->endpoint; + uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; + while (uhe != uhe_end) { + err = usb_setup_endpoint(dev, uhe, 0); + uhe++; + } + uhi++; + } + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_wait_complete + * + * The following function is used by "usb_start_wait_urb()" to wake it + * up, when an USB transfer has finished. + *------------------------------------------------------------------------*/ +static void +usb_linux_wait_complete(struct urb *urb) +{ + if (urb->transfer_flags & URB_IS_SLEEPING) { + usb2_cv_signal(&urb->cv_wait); + } + urb->transfer_flags &= ~URB_WAIT_WAKEUP; + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_complete + *------------------------------------------------------------------------*/ +static void +usb_linux_complete(struct usb2_xfer *xfer) +{ + struct urb *urb; + + urb = xfer->priv_fifo; + xfer->priv_fifo = NULL; + if (urb->complete) { + (urb->complete) (urb); + } + return; +} + +/*------------------------------------------------------------------------* + * usb_linux_isoc_callback + * + * The following is the FreeBSD isochronous USB callback. Isochronous + * frames are USB packets transferred 1000 or 8000 times per second, + * depending on whether a full- or high- speed USB transfer is + * used. + *------------------------------------------------------------------------*/ +static void +usb_linux_isoc_callback(struct usb2_xfer *xfer) +{ + uint32_t max_frame = xfer->max_frame_size; + uint32_t offset; + uint16_t x; + struct urb *urb = xfer->priv_fifo; + struct usb_host_endpoint *uhe = xfer->priv_sc; + struct usb_iso_packet_descriptor *uipd; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (urb->bsd_isread) { + + /* copy in data with regard to the URB */ + + offset = 0; + + for (x = 0; x < urb->number_of_packets; x++) { + uipd = urb->iso_frame_desc + x; + uipd->actual_length = xfer->frlengths[x]; + uipd->status = 0; + if (!xfer->flags.ext_buffer) { + usb2_copy_out(xfer->frbuffers, offset, + USB_ADD_BYTES(urb->transfer_buffer, + uipd->offset), uipd->actual_length); + } + offset += max_frame; + } + } else { + for (x = 0; x < urb->number_of_packets; x++) { + uipd = urb->iso_frame_desc + x; + uipd->actual_length = xfer->frlengths[x]; + uipd->status = 0; + } + } + + urb->actual_length = xfer->actlen; + + /* check for short transfer */ + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + if (urb->transfer_flags & URB_SHORT_NOT_OK) { + urb->status = -EPIPE; /* XXX should be + * EREMOTEIO */ + } else { + urb->status = 0; + } + } else { + /* success */ + urb->status = 0; + } + + /* call callback */ + usb_linux_complete(xfer); + + case USB_ST_SETUP: +tr_setup: + + if (xfer->priv_fifo == NULL) { + + /* get next transfer */ + urb = TAILQ_FIRST(&uhe->bsd_urb_list); + if (urb == NULL) { + /* nothing to do */ + return; + } + TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); + urb->bsd_urb_list.tqe_prev = NULL; + + x = xfer->max_frame_count; + if (urb->number_of_packets > x) { + /* XXX simply truncate the transfer */ + urb->number_of_packets = x; + } + } else { + DPRINTF("Already got a transfer\n"); + + /* already got a transfer (should not happen) */ + urb = xfer->priv_fifo; + } + + urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0; + + if (!(urb->bsd_isread)) { + + /* copy out data with regard to the URB */ + + offset = 0; + + for (x = 0; x < urb->number_of_packets; x++) { + uipd = urb->iso_frame_desc + x; + xfer->frlengths[x] = uipd->length; + if (!xfer->flags.ext_buffer) { + usb2_copy_in(xfer->frbuffers, offset, + USB_ADD_BYTES(urb->transfer_buffer, + uipd->offset), uipd->length); + } + offset += uipd->length; + } + } else { + + /* + * compute the transfer length into the "offset" + * variable + */ + + offset = urb->number_of_packets * max_frame; + + /* setup "frlengths" array */ + + for (x = 0; x < urb->number_of_packets; x++) { + uipd = urb->iso_frame_desc + x; + xfer->frlengths[x] = max_frame; + } + } + + if (xfer->flags.ext_buffer) { + /* set virtual address to load */ + usb2_set_frame_data(xfer, + urb->transfer_buffer, 0); + } + xfer->priv_fifo = urb; + xfer->flags.force_short_xfer = 0; + xfer->timeout = urb->timeout; + xfer->nframes = urb->number_of_packets; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + urb->status = -ECONNRESET; + } else { + urb->status = -EPIPE; /* stalled */ + } + + /* Set zero for "actual_length" */ + urb->actual_length = 0; + + /* Set zero for "actual_length" */ + for (x = 0; x < urb->number_of_packets; x++) { + urb->iso_frame_desc[x].actual_length = 0; + } + + /* call callback */ + usb_linux_complete(xfer); + + if (xfer->error == USB_ERR_CANCELLED) { + /* we need to return in this case */ + return; + } + goto tr_setup; + + } +} + +/*------------------------------------------------------------------------* + * usb_linux_non_isoc_callback + * + * The following is the FreeBSD BULK/INTERRUPT and CONTROL USB + * callback. It dequeues Linux USB stack compatible URB's, transforms + * the URB fields into a FreeBSD USB transfer, and defragments the USB + * transfer as required. When the transfer is complete the "complete" + * callback is called. + *------------------------------------------------------------------------*/ +static void +usb_linux_non_isoc_callback(struct usb2_xfer *xfer) +{ + enum { + REQ_SIZE = sizeof(struct usb2_device_request) + }; + struct urb *urb = xfer->priv_fifo; + struct usb_host_endpoint *uhe = xfer->priv_sc; + uint8_t *ptr; + uint32_t max_bulk = xfer->max_data_length; + uint8_t data_frame = xfer->flags_int.control_xfr ? 1 : 0; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->flags_int.control_xfr) { + + /* don't transfer the setup packet again: */ + + xfer->frlengths[0] = 0; + } + if (urb->bsd_isread && (!xfer->flags.ext_buffer)) { + /* copy in data with regard to the URB */ + usb2_copy_out(xfer->frbuffers + data_frame, 0, + urb->bsd_data_ptr, xfer->frlengths[data_frame]); + } + urb->bsd_length_rem -= xfer->frlengths[data_frame]; + urb->bsd_data_ptr += xfer->frlengths[data_frame]; + urb->actual_length += xfer->frlengths[data_frame]; + + /* check for short transfer */ + if (xfer->actlen < xfer->sumlen) { + urb->bsd_length_rem = 0; + + /* short transfer */ + if (urb->transfer_flags & URB_SHORT_NOT_OK) { + urb->status = -EPIPE; + } else { + urb->status = 0; + } + } else { + /* check remainder */ + if (urb->bsd_length_rem > 0) { + goto setup_bulk; + } + /* success */ + urb->status = 0; + } + + /* call callback */ + usb_linux_complete(xfer); + + case USB_ST_SETUP: +tr_setup: + /* get next transfer */ + urb = TAILQ_FIRST(&uhe->bsd_urb_list); + if (urb == NULL) { + /* nothing to do */ + return; + } + TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); + urb->bsd_urb_list.tqe_prev = NULL; + + xfer->priv_fifo = urb; + xfer->flags.force_short_xfer = 0; + xfer->timeout = urb->timeout; + + if (xfer->flags_int.control_xfr) { + + /* + * USB control transfers need special handling. + * First copy in the header, then copy in data! + */ + if (!xfer->flags.ext_buffer) { + usb2_copy_in(xfer->frbuffers, 0, + urb->setup_packet, REQ_SIZE); + } else { + /* set virtual address to load */ + usb2_set_frame_data(xfer, + urb->setup_packet, 0); + } + + xfer->frlengths[0] = REQ_SIZE; + + ptr = urb->setup_packet; + + /* setup data transfer direction and length */ + urb->bsd_isread = (ptr[0] & UT_READ) ? 1 : 0; + urb->bsd_length_rem = ptr[6] | (ptr[7] << 8); + + } else { + + /* setup data transfer direction */ + + urb->bsd_length_rem = urb->transfer_buffer_length; + urb->bsd_isread = (uhe->desc.bEndpointAddress & + UE_DIR_IN) ? 1 : 0; + } + + urb->bsd_data_ptr = urb->transfer_buffer; + urb->actual_length = 0; + +setup_bulk: + if (max_bulk > urb->bsd_length_rem) { + max_bulk = urb->bsd_length_rem; + } + /* check if we need to force a short transfer */ + + if ((max_bulk == urb->bsd_length_rem) && + (urb->transfer_flags & URB_ZERO_PACKET) && + (!xfer->flags_int.control_xfr)) { + xfer->flags.force_short_xfer = 1; + } + /* check if we need to copy in data */ + + if (xfer->flags.ext_buffer) { + /* set virtual address to load */ + usb2_set_frame_data(xfer, urb->bsd_data_ptr, + data_frame); + } else if (!urb->bsd_isread) { + /* copy out data with regard to the URB */ + usb2_copy_in(xfer->frbuffers + data_frame, 0, + urb->bsd_data_ptr, max_bulk); + } + xfer->frlengths[data_frame] = max_bulk; + if (xfer->flags_int.control_xfr) { + if (max_bulk > 0) { + xfer->nframes = 2; + } else { + xfer->nframes = 1; + } + } else { + xfer->nframes = 1; + } + usb2_start_hardware(xfer); + return; + + default: + if (xfer->error == USB_ERR_CANCELLED) { + urb->status = -ECONNRESET; + } else { + urb->status = -EPIPE; + } + + /* Set zero for "actual_length" */ + urb->actual_length = 0; + + /* call callback */ + usb_linux_complete(xfer); + + if (xfer->error == USB_ERR_CANCELLED) { + /* we need to return in this case */ + return; + } + goto tr_setup; + } +} diff --git a/sys/dev/usb2/core/usb2_compat_linux.h b/sys/dev/usb2/core/usb2_compat_linux.h new file mode 100644 index 000000000000..b8ddaf4fe93f --- /dev/null +++ b/sys/dev/usb2/core/usb2_compat_linux.h @@ -0,0 +1,465 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved. + * Copyright (c) 2007 Hans Petter Selasky. 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. + */ + +#ifndef _USB_COMPAT_LINUX_H +#define _USB_COMPAT_LINUX_H + +struct usb_device; +struct usb_interface; +struct usb_driver; +struct urb; + +typedef void *pm_message_t; +typedef void (usb_complete_t)(struct urb *); + +#define USB_MAX_FULL_SPEED_ISOC_FRAMES (60 * 1) +#define USB_MAX_HIGH_SPEED_ISOC_FRAMES (60 * 8) + +/* + * Linux compatible USB device drivers put their device information + * into the "usb_device_id" structure using the "USB_DEVICE()" macro. + * The "MODULE_DEVICE_TABLE()" macro can be used to export this + * information to userland. + */ +struct usb_device_id { + /* which fields to match against */ + uint16_t match_flags; +#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 +#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 +#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 +#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 +#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 +#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 +#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 +#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 +#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 +#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 + + /* Used for product specific matches; the BCD range is inclusive */ + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice_lo; + uint16_t bcdDevice_hi; + + /* Used for device class matches */ + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + + /* Used for interface class matches */ + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + + /* Hook for driver specific information */ + unsigned long driver_info; +}; + +#define USB_DEVICE_ID_MATCH_DEVICE \ + (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT) + +#define USB_DEVICE(vend,prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = (vend), \ + .idProduct = (prod) + +/* The "usb_driver" structure holds the Linux USB device driver + * callbacks, and a pointer to device ID's which this entry should + * match against. Usually this entry is exposed to the USB emulation + * layer using the "USB_DRIVER_EXPORT()" macro, which is defined + * below. + */ +struct usb_driver { + const char *name; + + int (*probe) (struct usb_interface *intf, + const struct usb_device_id *id); + + void (*disconnect) (struct usb_interface *intf); + + int (*ioctl) (struct usb_interface *intf, unsigned int code, + void *buf); + + int (*suspend) (struct usb_interface *intf, pm_message_t message); + int (*resume) (struct usb_interface *intf); + + const struct usb_device_id *id_table; + + void (*shutdown) (struct usb_interface *intf); + + LIST_ENTRY(usb_driver) linux_driver_list; +}; + +#define USB_DRIVER_EXPORT(id,p_usb_drv) \ + SYSINIT(id,SI_SUB_KLD,SI_ORDER_FIRST,usb_linux_register,p_usb_drv); \ + SYSUNINIT(id,SI_SUB_KLD,SI_ORDER_ANY,usb_linux_deregister,p_usb_drv) + +/* + * The following structure is the same as "usb_device_descriptor_t" + * except that 16-bit values are "uint16_t" and not an array of "uint8_t". + * It is used by Linux USB device drivers. + */ +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __packed; + +/* + * The following structure is the same as + * "usb_interface_descriptor_t". It is used by + * Linux USB device drivers. + */ +struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __packed; + +/* + * The following structure is the same as "usb_endpoint_descriptor_t" + * except that 16-bit values are "uint16_t" and not an array of "uint8_t". + * It is used by Linux USB device drivers. + */ +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + + /* extension for audio endpoints only: */ + uint8_t bRefresh; + uint8_t bSynchAddress; +} __packed; + +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + +/* CONTROL REQUEST SUPPORT */ + +/* + * Definition of direction mask for + * "bEndpointAddress" and "bmRequestType": + */ +#define USB_DIR_MASK 0x80 +#define USB_DIR_OUT 0x00 /* write to USB device */ +#define USB_DIR_IN 0x80 /* read from USB device */ + +/* + * Definition of type mask for + * "bmRequestType": + */ +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * Definition of receiver mask for + * "bmRequestType": + */ +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * Definition of standard request values for + * "bRequest": + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */ +#define USB_REQ_GET_ENCRYPTION 0x0E +#define USB_REQ_SET_HANDSHAKE 0x0F +#define USB_REQ_GET_HANDSHAKE 0x10 +#define USB_REQ_SET_CONNECTION 0x11 +#define USB_REQ_SET_SECURITY_DATA 0x12 +#define USB_REQ_GET_SECURITY_DATA 0x13 +#define USB_REQ_SET_WUSB_DATA 0x14 +#define USB_REQ_LOOPBACK_DATA_WRITE 0x15 +#define USB_REQ_LOOPBACK_DATA_READ 0x16 +#define USB_REQ_SET_INTERFACE_DS 0x17 + +/* + * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and + * are read as a bit array returned by USB_REQ_GET_STATUS. (So there + * are at most sixteen features of each type.) + */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ +#define USB_DEVICE_BATTERY 2 /* (wireless) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ +#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless) */ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ +#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ + +#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ + +#define PIPE_ISOCHRONOUS 0x01 /* UE_ISOCHRONOUS */ +#define PIPE_INTERRUPT 0x03 /* UE_INTERRUPT */ +#define PIPE_CONTROL 0x00 /* UE_CONTROL */ +#define PIPE_BULK 0x02 /* UE_BULK */ + +/* Whenever Linux references an USB endpoint: + * a) to initialize "urb->pipe" + * b) second argument passed to "usb_control_msg()" + * + * Then it uses one of the following macros. The "endpoint" argument + * is the physical endpoint value masked by 0xF. The "dev" argument + * is a pointer to "struct usb_device". + */ +#define usb_sndctrlpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_OUT) + +#define usb_rcvctrlpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_IN) + +#define usb_sndisocpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_OUT) + +#define usb_rcvisocpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_IN) + +#define usb_sndbulkpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_OUT) + +#define usb_rcvbulkpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_IN) + +#define usb_sndintpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_OUT) + +#define usb_rcvintpipe(dev,endpoint) \ + usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_IN) + +/* The following four structures makes up a tree, where we have the + * leaf structure, "usb_host_endpoint", first, and the root structure, + * "usb_device", last. The four structures below mirror the structure + * of the USB descriptors belonging to an USB configuration. Please + * refer to the USB specification for a definition of "endpoints" and + * "interfaces". + */ +struct usb_host_endpoint { + struct usb_endpoint_descriptor desc; + + TAILQ_HEAD(, urb) bsd_urb_list; + + struct usb2_xfer *bsd_xfer[2]; + + uint8_t *extra; /* Extra descriptors */ + + uint32_t fbsd_buf_size; + + uint16_t extralen; + + uint8_t bsd_iface_index; +} __aligned(USB_HOST_ALIGN); + +struct usb_host_interface { + struct usb_interface_descriptor desc; + + /* the following array has size "desc.bNumEndpoint" */ + struct usb_host_endpoint *endpoint; + + const char *string; /* iInterface string, if present */ + uint8_t *extra; /* Extra descriptors */ + + uint16_t extralen; + + uint8_t bsd_iface_index; +} __aligned(USB_HOST_ALIGN); + +struct usb_interface { + /* array of alternate settings for this interface */ + struct usb_host_interface *altsetting; + struct usb_host_interface *cur_altsetting; + struct usb_device *linux_udev; + void *bsd_priv_sc; /* device specific information */ + + uint8_t num_altsetting; /* number of alternate settings */ + uint8_t bsd_iface_index; +} __aligned(USB_HOST_ALIGN); + +struct usb_device { + struct usb_device_descriptor descriptor; + struct usb_host_endpoint ep0; + + struct usb2_device *bsd_udev; + struct usb_interface *bsd_iface_start; + struct usb_interface *bsd_iface_end; + struct usb_host_endpoint *bsd_endpoint_start; + struct usb_host_endpoint *bsd_endpoint_end; + + /* static strings from the device */ + const char *product; /* iProduct string, if present */ + const char *manufacturer; /* iManufacturer string, if present */ + const char *serial; /* iSerialNumber string, if present */ + + uint16_t devnum; + + uint8_t speed; /* USB_SPEED_XXX */ +} __aligned(USB_HOST_ALIGN); + +/* + * The following structure is used to extend "struct urb" when we are + * dealing with an isochronous endpoint. It contains information about + * the data offset and data length of an isochronous packet. + * The "actual_length" field is updated before the "complete" + * callback in the "urb" structure is called. + */ +struct usb_iso_packet_descriptor { + uint32_t offset; /* depreciated buffer offset (the + * packets are usually back to back) */ + uint16_t length; /* expected length */ + uint16_t actual_length; + uint16_t status; +}; + +/* + * The following structure holds various information about an USB + * transfer. This structure is used for all kinds of USB transfers. + * + * URB is short for USB Request Block. + */ +struct urb { + TAILQ_ENTRY(urb) bsd_urb_list; + struct cv cv_wait; + + struct usb_device *dev; /* (in) pointer to associated device */ + struct usb_host_endpoint *pipe; /* (in) pipe pointer */ + uint8_t *setup_packet; /* (in) setup packet (control only) */ + uint8_t *bsd_data_ptr; + void *transfer_buffer; /* (in) associated data buffer */ + void *context; /* (in) context for completion */ + usb_complete_t *complete; /* (in) completion routine */ + + uint32_t transfer_buffer_length;/* (in) data buffer length */ + uint32_t actual_length; /* (return) actual transfer length */ + uint32_t bsd_length_rem; + uint32_t timeout; /* FreeBSD specific */ + + uint16_t transfer_flags; /* (in) */ +#define URB_SHORT_NOT_OK 0x0001 /* report short transfers like errors */ +#define URB_ISO_ASAP 0x0002 /* ignore "start_frame" field */ +#define URB_ZERO_PACKET 0x0004 /* the USB transfer ends with a short + * packet */ +#define URB_NO_TRANSFER_DMA_MAP 0x0008 /* "transfer_dma" is valid on submit */ +#define URB_WAIT_WAKEUP 0x0010 /* custom flags */ +#define URB_IS_SLEEPING 0x0020 /* custom flags */ + + uint16_t start_frame; /* (modify) start frame (ISO) */ + uint16_t number_of_packets; /* (in) number of ISO packets */ + uint16_t interval; /* (modify) transfer interval + * (INT/ISO) */ + uint16_t error_count; /* (return) number of ISO errors */ + int16_t status; /* (return) status */ + + uint8_t setup_dma; /* (in) not used on FreeBSD */ + uint8_t transfer_dma; /* (in) not used on FreeBSD */ + uint8_t bsd_isread; + + struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */ +}; + +/* various prototypes */ + +int usb_submit_urb(struct urb *urb, uint16_t mem_flags); +int usb_unlink_urb(struct urb *urb); +int usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe); +int usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *pipe, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size, uint32_t timeout); +int usb_set_interface(struct usb_device *dev, uint8_t ifnum, uint8_t alternate); +int usb_setup_endpoint(struct usb_device *dev, struct usb_host_endpoint *uhe, uint32_t bufsize); + +struct usb_host_endpoint *usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep); +struct urb *usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags); +struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index); +struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no); + +void *usb_buffer_alloc(struct usb_device *dev, uint32_t size, uint16_t mem_flags, uint8_t *dma_addr); +void *usb_get_intfdata(struct usb_interface *intf); + +void usb_buffer_free(struct usb_device *dev, uint32_t size, void *addr, uint8_t dma_addr); +void usb_free_urb(struct urb *urb); +void usb_init_urb(struct urb *urb); +void usb_kill_urb(struct urb *urb); +void usb_set_intfdata(struct usb_interface *intf, void *data); +void usb_linux_register(void *arg); +void usb_linux_deregister(void *arg); + +#define interface_to_usbdev(intf) (intf)->linux_udev +#define interface_to_bsddev(intf) (intf)->linux_udev->bsd_udev + +#endif /* _USB_COMPAT_LINUX_H */ diff --git a/sys/dev/usb2/core/usb2_config_td.c b/sys/dev/usb2/core/usb2_config_td.c new file mode 100644 index 000000000000..ac652fd256db --- /dev/null +++ b/sys/dev/usb2/core/usb2_config_td.c @@ -0,0 +1,320 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include + +static void usb2_config_td_sync_cb(struct usb2_config_td_softc *sc, struct usb2_config_td_cc *cc, uint16_t ref); + +static void +usb2_config_td_dispatch(struct usb2_proc_msg *pm) +{ + struct usb2_config_td_item *pi = (void *)pm; + struct usb2_config_td *ctd = pi->p_ctd; + + DPRINTF("\n"); + + (pi->command_func) (ctd->p_softc, (void *)(pi + 1), pi->command_ref); + + if (TAILQ_NEXT(pm, pm_qentry) == NULL) { + /* last command */ + if (ctd->p_end_of_commands) { + (ctd->p_end_of_commands) (ctd->p_softc); + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_config_td_setup + * + * NOTE: the structure pointed to by "ctd" must be zeroed before calling + * this function! + * + * Return values: + * 0: success + * Else: failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_config_td_setup(struct usb2_config_td *ctd, void *priv_sc, + struct mtx *priv_mtx, + usb2_config_td_end_of_commands_t *p_func_eoc, + uint16_t item_size, uint16_t item_count) +{ + struct usb2_config_td_item *pi; + uint16_t n; + + DPRINTF(" size=%u, count=%u \n", item_size, item_count); + + if (item_count >= 256) { + DPRINTFN(0, "too many items!\n"); + return (1); + } + ctd->p_softc = priv_sc; + ctd->p_end_of_commands = p_func_eoc; + ctd->msg_count = (2 * item_count); + ctd->msg_size = + (sizeof(struct usb2_config_td_item) + item_size); + ctd->p_msgs = + malloc(ctd->msg_size * ctd->msg_count, M_USBDEV, M_WAITOK | M_ZERO); + if (ctd->p_msgs == NULL) { + return (1); + } + if (usb2_proc_setup(&ctd->usb2_proc, priv_mtx, USB_PRI_MED)) { + free(ctd->p_msgs, M_USBDEV); + ctd->p_msgs = NULL; + return (1); + } + /* initialise messages */ + pi = USB_ADD_BYTES(ctd->p_msgs, 0); + for (n = 0; n != ctd->msg_count; n++) { + pi->hdr.pm_callback = &usb2_config_td_dispatch; + pi->p_ctd = ctd; + pi = USB_ADD_BYTES(pi, ctd->msg_size); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_config_td_drain + * + * This function will tear down an USB config thread, waiting for the + * currently executing command to return. + * + * NOTE: If the structure pointed to by "ctd" is all zero, + * this function does nothing. + *------------------------------------------------------------------------*/ +void +usb2_config_td_drain(struct usb2_config_td *ctd) +{ + DPRINTF("\n"); + if (ctd->p_msgs) { + usb2_proc_drain(&ctd->usb2_proc); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_config_td_unsetup + * + * NOTE: If the structure pointed to by "ctd" is all zero, + * this function does nothing. + *------------------------------------------------------------------------*/ +void +usb2_config_td_unsetup(struct usb2_config_td *ctd) +{ + DPRINTF("\n"); + + usb2_config_td_drain(ctd); + + if (ctd->p_msgs) { + usb2_proc_unsetup(&ctd->usb2_proc); + free(ctd->p_msgs, M_USBDEV); + ctd->p_msgs = NULL; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_config_td_queue_command + * + * This function will enter a command into the config thread queue for + * execution. The "command_sync" field was previously used to indicate + * the queue count which is now fixed at two elements. If the + * "command_sync" field is equal to "USB2_CONFIG_TD_SYNC" the command + * will be executed synchronously from the config thread. The + * "command_ref" argument is the reference count for the current + * command which is passed on to the "command_post_func" + * function. This parameter can be used to make a command + * unique. "command_pre_func" is called from this function when we + * have the final queue element. "command_post_func" is called from + * the USB config thread when the command reaches the beginning of the + * USB config thread queue. This function must be called locked. + *------------------------------------------------------------------------*/ +void +usb2_config_td_queue_command(struct usb2_config_td *ctd, + usb2_config_td_command_t *command_pre_func, + usb2_config_td_command_t *command_post_func, + uint16_t command_sync, + uint16_t command_ref) +{ + struct usb2_config_td_item *pi; + struct usb2_config_td_item *pi_0; + struct usb2_config_td_item *pi_1; + uint16_t n; + + if (usb2_config_td_is_gone(ctd)) { + DPRINTF("gone\n"); + /* nothing more to do */ + return; + } + DPRINTF("\n"); + + pi = USB_ADD_BYTES(ctd->p_msgs, 0); + for (n = 0;; n += 2) { + if (n == ctd->msg_count) { + /* should not happen */ + panic("%s:%d: out of memory!\n", + __FUNCTION__, __LINE__); + return; + } + if (pi->command_func == NULL) { + /* reserve our entry */ + pi->command_func = command_post_func; + pi->command_ref = command_ref; + pi_0 = pi; + pi = USB_ADD_BYTES(pi, ctd->msg_size); + pi->command_func = command_post_func; + pi->command_ref = command_ref; + pi_1 = pi; + break; + } + if ((pi->command_func == command_post_func) && + (pi->command_ref == command_ref)) { + /* found an entry */ + pi_0 = pi; + pi = USB_ADD_BYTES(pi, ctd->msg_size); + pi_1 = pi; + break; + } + pi = USB_ADD_BYTES(pi, (2 * ctd->msg_size)); + } + + /* + * We have two message structures. One of them will get + * queued: + */ + pi = usb2_proc_msignal(&ctd->usb2_proc, pi_0, pi_1); + + /* + * The job of the post-command function is to finish the command in + * a separate context to allow calls to sleeping functions + * basically. Queue the post command before calling the pre command. + * That way commands queued by the pre command will be queued after + * the current command. + */ + + /* + * The job of the pre-command function is to copy the needed + * configuration to the provided structure and to execute other + * commands that must happen immediately + */ + if (command_pre_func) { + (command_pre_func) (ctd->p_softc, (void *)(pi + 1), command_ref); + } + if (command_sync == USB2_CONFIG_TD_SYNC) { + usb2_proc_mwait(&ctd->usb2_proc, pi_0, pi_1); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_config_td_is_gone + * + * Return values: + * 0: config thread is running + * Else: config thread is gone + *------------------------------------------------------------------------*/ +uint8_t +usb2_config_td_is_gone(struct usb2_config_td *ctd) +{ + return (usb2_proc_is_gone(&ctd->usb2_proc)); +} + +/*------------------------------------------------------------------------* + * usb2_config_td_sleep + * + * NOTE: this function can only be called from the config thread + * + * Return values: + * 0: normal delay + * Else: config thread is gone + *------------------------------------------------------------------------*/ +uint8_t +usb2_config_td_sleep(struct usb2_config_td *ctd, uint32_t timeout) +{ + uint8_t is_gone = usb2_config_td_is_gone(ctd); + + if (is_gone) { + goto done; + } + if (timeout == 0) { + /* + * Zero means no timeout, so avoid that by setting + * timeout to one: + */ + timeout = 1; + } + mtx_unlock(ctd->usb2_proc.up_mtx); + + if (pause("USBWAIT", timeout)) { + /* ignore */ + } + mtx_lock(ctd->usb2_proc.up_mtx); + + is_gone = usb2_config_td_is_gone(ctd); +done: + return (is_gone); +} + +/*------------------------------------------------------------------------* + * usb2_config_td_sync + * + * This function will wait until all commands have been executed on + * the config thread. This function must be called locked and can + * sleep. + * + * Return values: + * 0: success + * Else: config thread is gone + *------------------------------------------------------------------------*/ +uint8_t +usb2_config_td_sync(struct usb2_config_td *ctd) +{ + if (usb2_config_td_is_gone(ctd)) { + return (1); + } + usb2_config_td_queue_command(ctd, NULL, + &usb2_config_td_sync_cb, USB2_CONFIG_TD_SYNC, 0); + + if (usb2_config_td_is_gone(ctd)) { + return (1); + } + return (0); +} + +static void +usb2_config_td_sync_cb(struct usb2_config_td_softc *sc, + struct usb2_config_td_cc *cc, uint16_t ref) +{ + return; +} diff --git a/sys/dev/usb2/core/usb2_config_td.h b/sys/dev/usb2/core/usb2_config_td.h new file mode 100644 index 000000000000..1f7d378b0bf0 --- /dev/null +++ b/sys/dev/usb2/core/usb2_config_td.h @@ -0,0 +1,71 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_CONFIG_TD_H_ +#define _USB2_CONFIG_TD_H_ + +struct usb2_config_td_softc; +struct usb2_config_td_cc; + +#define USB2_CONFIG_TD_SYNC 0xFFFF /* magic value */ + +typedef void (usb2_config_td_command_t)(struct usb2_config_td_softc *sc, struct usb2_config_td_cc *cc, uint16_t reference); +typedef void (usb2_config_td_end_of_commands_t)(struct usb2_config_td_softc *sc); + +/* + * The following structure defines a command that should be executed + * using the USB config thread system. + */ +struct usb2_config_td_item { + struct usb2_proc_msg hdr; + struct usb2_config_td *p_ctd; + usb2_config_td_command_t *command_func; + uint16_t command_ref; +} __aligned(USB_HOST_ALIGN); + +/* + * The following structure defines an USB config thread. + */ +struct usb2_config_td { + struct usb2_process usb2_proc; + struct usb2_config_td_softc *p_softc; + usb2_config_td_end_of_commands_t *p_end_of_commands; + void *p_msgs; + uint16_t msg_size; + uint16_t msg_count; +}; + +/* prototypes */ + +uint8_t usb2_config_td_setup(struct usb2_config_td *ctd, void *priv_sc, struct mtx *priv_mtx, usb2_config_td_end_of_commands_t *p_func_eoc, uint16_t item_size, uint16_t item_count); +void usb2_config_td_drain(struct usb2_config_td *ctd); +void usb2_config_td_unsetup(struct usb2_config_td *ctd); +void usb2_config_td_queue_command(struct usb2_config_td *ctd, usb2_config_td_command_t *pre_func, usb2_config_td_command_t *post_func, uint16_t command_sync, uint16_t command_ref); +uint8_t usb2_config_td_is_gone(struct usb2_config_td *ctd); +uint8_t usb2_config_td_sleep(struct usb2_config_td *ctd, uint32_t timeout); +uint8_t usb2_config_td_sync(struct usb2_config_td *ctd); + +#endif /* _USB2_CONFIG_TD_H_ */ diff --git a/sys/dev/usb2/core/usb2_core.c b/sys/dev/usb2/core/usb2_core.c new file mode 100644 index 000000000000..b96f53c71225 --- /dev/null +++ b/sys/dev/usb2/core/usb2_core.c @@ -0,0 +1,40 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +/* + * USB specifications and other documentation can be found at + * http://www.usb.org/developers/docs/ and + * http://www.usb.org/developers/devclass_docs/ + */ + +#include +#include + +MALLOC_DEFINE(M_USB, "USB", "USB"); +MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); +MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller"); + +MODULE_VERSION(usb2_core, 1); diff --git a/sys/dev/usb2/core/usb2_core.h b/sys/dev/usb2/core/usb2_core.h new file mode 100644 index 000000000000..c0cec7a1725e --- /dev/null +++ b/sys/dev/usb2/core/usb2_core.h @@ -0,0 +1,448 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +/* + * Including this file is mandatory for all USB related c-files in the + * kernel. + */ + +#ifndef _USB2_CORE_H_ +#define _USB2_CORE_H_ + +/* Default USB configuration */ + +#ifndef USB_NO_POLL +#define USB_NO_POLL 0 +#endif + +#ifndef USB_USE_CONDVAR +#define USB_USE_CONDVAR 0 +#endif + +#ifndef USB_TD_GET_PROC +#define USB_TD_GET_PROC(td) (td)->td_proc +#endif + +#ifndef USB_PROC_GET_GID +#define USB_PROC_GET_GID(td) (td)->p_pgid +#endif + +#ifndef USB_VNOPS_FO_CLOSE +#define USB_VNOPS_FO_CLOSE(fp, td, perr) do { \ + (td)->td_fpop = (fp); \ + *(perr) = vnops.fo_close(fp, td); \ + (td)->td_fpop = NULL; \ +} while (0) +#endif + +#ifndef USB_VNOPS_FO_STAT +#define USB_VNOPS_FO_STAT(fp, sb, cred, td) \ + vnops.fo_stat(fp, sb, cred, td) +#endif + +#ifndef USB_VNOPS_FO_TRUNCATE +#define USB_VNOPS_FO_TRUNCATE(fp, length, cred, td) \ + vnops.fo_truncate(fp, length, cred, td) +#endif + +/* Include files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "usb2_if.h" +#include "opt_usb.h" +#include "opt_bus.h" + +#define USB_STACK_VERSION 2000 /* 2.0 */ + +#define USB_HOST_ALIGN 8 /* bytes, must be power of two */ + +#define USB_ROOT_HUB_ADDR 1 /* value */ + +#define USB_ISOC_TIME_MAX 128 /* ms */ +#define USB_FS_ISOC_UFRAME_MAX 4 /* exclusive unit */ + +#if (USB_FS_ISOC_UFRAME_MAX > 6) +#error "USB_FS_ISOC_UFRAME_MAX cannot be set higher than 6" +#endif + +#define USB_MAX_FS_ISOC_FRAMES_PER_XFER (120) /* units */ +#define USB_MAX_HS_ISOC_FRAMES_PER_XFER (8*120) /* units */ + +#define USB_MAX_IPACKET 8 /* maximum size of the initial USB + * data packet */ +#ifndef USB_VERBOSE +#define USB_VERBOSE 1 +#endif + +#define USB_HUB_MAX_DEPTH 5 + +/* USB transfer states */ + +#define USB_ST_SETUP 0 +#define USB_ST_TRANSFERRED 1 +#define USB_ST_ERROR 2 + +/* + * The following macro will return the current state of an USB + * transfer like defined by the "USB_ST_XXX" enums. + */ +#define USB_GET_STATE(xfer) ((xfer)->usb2_state) + +/* + * The following macro will tell if an USB transfer is currently + * receiving or transferring data. + */ +#define USB_GET_DATA_ISREAD(xfer) (((xfer)->flags_int.usb2_mode == \ + USB_MODE_DEVICE) ? ((xfer->endpoint & UE_DIR_IN) ? 0 : 1) : \ + ((xfer->endpoint & UE_DIR_IN) ? 1 : 0)) + +/* + * The following macros are used used to convert milliseconds into + * HZ. We use 1024 instead of 1000 milliseconds per second to save a + * full division. + */ +#define USB_MS_HZ 1024 + +#define USB_MS_TO_TICKS(ms) \ + (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) + +/* macros */ + +#define usb2_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) +#define usb2_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d) +#define usb2_callout_stop(c) callout_stop(&(c)->co) +#define usb2_callout_drain(c) callout_drain(&(c)->co) +#define usb2_callout_pending(c) callout_pending(&(c)->co) + +/* structure prototypes */ + +struct file; +struct usb2_bus; +struct usb2_device; +struct usb2_page; +struct usb2_page_cache; +struct usb2_xfer; +struct usb2_xfer_root; + +/* typedefs */ + +typedef uint8_t usb2_error_t; + +typedef void (usb2_callback_t)(struct usb2_xfer *); + +/* structures */ + +/* + * This structure contains permissions. + */ + +struct usb2_perm { + uint32_t uid; + uint32_t gid; + uint16_t mode; +}; + +/* + * Common queue structure for USB transfers. + */ +struct usb2_xfer_queue { + TAILQ_HEAD(, usb2_xfer) head; + struct usb2_xfer *curr; /* current USB transfer processed */ + void (*command) (struct usb2_xfer_queue *pq); + uint8_t recurse_1:1; + uint8_t recurse_2:1; +}; + +/* + * The following is a wrapper for the callout structure to ease + * porting the code to other platforms. + */ +struct usb2_callout { + struct callout co; +}; + +/* + * The following structure defines a set of USB transfer flags. + */ +struct usb2_xfer_flags { + uint8_t force_short_xfer:1; /* force a short transmit transfer + * last */ + uint8_t short_xfer_ok:1; /* allow short receive transfers */ + uint8_t short_frames_ok:1; /* allow short frames */ + uint8_t pipe_bof:1; /* block pipe on failure */ + uint8_t proxy_buffer:1; /* makes buffer size a factor of + * "max_frame_size" */ + uint8_t ext_buffer:1; /* uses external DMA buffer */ + uint8_t manual_status:1; /* non automatic status stage on + * control transfers */ + uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can + * be ignored */ + uint8_t stall_pipe:1; /* set if the endpoint belonging to + * this USB transfer should be stalled + * before starting this transfer! */ +}; + +/* + * The following structure defines a set of internal USB transfer + * flags. + */ +struct usb2_xfer_flags_int { + uint16_t control_rem; /* remainder in bytes */ + + uint8_t open:1; /* set if USB pipe has been opened */ + uint8_t transferring:1; /* set if an USB transfer is in + * progress */ + uint8_t did_dma_delay:1; /* set if we waited for HW DMA */ + uint8_t did_close:1; /* set if we closed the USB transfer */ + uint8_t draining:1; /* set if we are draining an USB + * transfer */ + uint8_t started:1; /* keeps track of started or stopped */ + uint8_t bandwidth_reclaimed:1; + uint8_t control_xfr:1; /* set if control transfer */ + uint8_t control_hdr:1; /* set if control header should be + * sent */ + uint8_t control_act:1; /* set if control transfer is active */ + + uint8_t short_frames_ok:1; /* filtered version */ + uint8_t short_xfer_ok:1; /* filtered version */ + uint8_t bdma_enable:1; /* filtered version (only set if + * hardware supports DMA) */ + uint8_t bdma_no_post_sync:1; /* set if the USB callback wrapper + * should not do the BUS-DMA post sync + * operation */ + uint8_t bdma_setup:1; /* set if BUS-DMA has been setup */ + uint8_t isochronous_xfr:1; /* set if isochronous transfer */ + uint8_t usb2_mode:1; /* shadow copy of "udev->usb2_mode" */ + uint8_t curr_dma_set:1; /* used by USB HC/DC driver */ + uint8_t can_cancel_immed:1; /* set if USB transfer can be + * cancelled immediately */ +}; + +/* + * The following structure defines the symmetric part of an USB config + * structure. + */ +struct usb2_config_sub { + usb2_callback_t *callback; /* USB transfer callback */ + uint32_t bufsize; /* total pipe buffer size in bytes */ + uint32_t frames; /* maximum number of USB frames */ + uint16_t interval; /* interval in milliseconds */ +#define USB_DEFAULT_INTERVAL 0 + uint16_t timeout; /* transfer timeout in milliseconds */ + struct usb2_xfer_flags flags; /* transfer flags */ +}; + +/* + * The following structure define an USB configuration, that basically + * is used when setting up an USB transfer. + */ +struct usb2_config { + struct usb2_config_sub mh; /* parameters for USB_MODE_HOST */ + struct usb2_config_sub md; /* parameters for USB_MODE_DEVICE */ + uint8_t type; /* pipe type */ + uint8_t endpoint; /* pipe number */ + uint8_t direction; /* pipe direction */ + uint8_t ep_index; /* pipe index match to use */ + uint8_t if_index; /* "ifaces" index to use */ +}; + +/* + * The following structure defines an USB transfer. + */ +struct usb2_xfer { + struct usb2_callout timeout_handle; + TAILQ_ENTRY(usb2_xfer) wait_entry; /* used at various places */ + + struct usb2_page_cache *buf_fixup; /* fixup buffer(s) */ + struct usb2_xfer_queue *wait_queue; /* pointer to queue that we + * are waiting on */ + struct usb2_page *dma_page_ptr; + struct usb2_pipe *pipe; /* our USB pipe */ + struct usb2_device *udev; + struct mtx *priv_mtx; /* cannot be changed during operation */ + struct mtx *usb2_mtx; /* used by HC driver */ + struct usb2_xfer_root *usb2_root; /* used by HC driver */ + void *usb2_sc; /* used by HC driver */ + void *qh_start[2]; /* used by HC driver */ + void *td_start[2]; /* used by HC driver */ + void *td_transfer_first; /* used by HC driver */ + void *td_transfer_last; /* used by HC driver */ + void *td_transfer_cache; /* used by HC driver */ + void *priv_sc; /* device driver data pointer 1 */ + void *priv_fifo; /* device driver data pointer 2 */ + void *local_buffer; + uint32_t *frlengths; + struct usb2_page_cache *frbuffers; + usb2_callback_t *callback; + + uint32_t max_usb2_frame_size; + uint32_t max_data_length; + uint32_t sumlen; /* sum of all lengths in bytes */ + uint32_t actlen; /* actual length in bytes */ + uint32_t timeout; /* milliseconds */ +#define USB_NO_TIMEOUT 0 +#define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ + + uint32_t max_frame_count; /* initial value of "nframes" after + * setup */ + uint32_t nframes; /* number of USB frames to transfer */ + uint32_t aframes; /* actual number of USB frames + * transferred */ + + uint16_t max_packet_size; + uint16_t max_frame_size; + uint16_t qh_pos; + uint16_t isoc_time_complete; /* in ms */ + uint16_t interval; /* milliseconds */ + + uint8_t address; /* physical USB address */ + uint8_t endpoint; /* physical USB endpoint */ + uint8_t max_packet_count; + uint8_t usb2_smask; + uint8_t usb2_cmask; + uint8_t usb2_uframe; + uint8_t usb2_state; + + usb2_error_t error; + + struct usb2_xfer_flags flags; + struct usb2_xfer_flags_int flags_int; +}; + +/* + * The following structure keeps information that is used to match + * against an array of "usb2_device_id" elements. + */ +struct usb2_lookup_info { + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t bIfaceIndex; + uint8_t bIfaceNum; + uint8_t bConfigIndex; + uint8_t bConfigNum; +}; + +/* Structure used by probe and attach */ + +struct usb2_attach_arg { + struct usb2_lookup_info info; + device_t temp_dev; /* for internal use */ + const void *driver_info; /* for internal use */ + struct usb2_device *device; /* current device */ + struct usb2_interface *iface; /* current interface */ + uint8_t usb2_mode; /* see USB_MODE_XXX */ + uint8_t port; + uint8_t use_generic; /* hint for generic drivers */ +}; + +/* Structure used when referring an USB device */ + +struct usb2_location { + struct usb2_bus *bus; + struct usb2_device *udev; + struct usb2_interface *iface; + struct usb2_fifo *rxfifo; + struct usb2_fifo *txfifo; + uint32_t devloc; /* original devloc */ + uint16_t bus_index; + uint8_t dev_index; + uint8_t iface_index; + uint8_t ep_index; + uint8_t is_read; + uint8_t is_write; + uint8_t is_uref; +}; + +/* external variables */ + +MALLOC_DECLARE(M_USB); +MALLOC_DECLARE(M_USBDEV); +MALLOC_DECLARE(M_USBHC); + +extern struct mtx usb2_ref_lock; + +/* typedefs */ + +typedef struct malloc_type *usb2_malloc_type; + +/* prototypes */ + +const char *usb2_errstr(usb2_error_t error); +struct usb2_config_descriptor *usb2_get_config_descriptor(struct usb2_device *udev); +struct usb2_device_descriptor *usb2_get_device_descriptor(struct usb2_device *udev); +struct usb2_interface *usb2_get_iface(struct usb2_device *udev, uint8_t iface_index); +struct usb2_interface_descriptor *usb2_get_interface_descriptor(struct usb2_interface *iface); +uint8_t usb2_clear_stall_callback(struct usb2_xfer *xfer1, struct usb2_xfer *xfer2); +uint8_t usb2_get_interface_altindex(struct usb2_interface *iface); +usb2_error_t usb2_set_alt_interface_index(struct usb2_device *udev, uint8_t iface_index, uint8_t alt_index); +uint8_t usb2_get_speed(struct usb2_device *udev); +usb2_error_t usb2_transfer_setup(struct usb2_device *udev, const uint8_t *ifaces, struct usb2_xfer **pxfer, const struct usb2_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); +void usb2_set_frame_data(struct usb2_xfer *xfer, void *ptr, uint32_t frindex); +void usb2_set_frame_offset(struct usb2_xfer *xfer, uint32_t offset, uint32_t frindex); +void usb2_start_hardware(struct usb2_xfer *xfer); +void usb2_transfer_clear_stall(struct usb2_xfer *xfer); +void usb2_transfer_drain(struct usb2_xfer *xfer); +void usb2_transfer_set_stall(struct usb2_xfer *xfer); +void usb2_transfer_start(struct usb2_xfer *xfer); +void usb2_transfer_stop(struct usb2_xfer *xfer); +void usb2_transfer_unsetup(struct usb2_xfer **pxfer, uint16_t n_setup); +usb2_error_t usb2_ref_device(struct file *fp, struct usb2_location *ploc, uint32_t devloc); +void usb2_unref_device(struct usb2_location *ploc); +void usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index, uint8_t parent_index); +void usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index, uint32_t uid, uint32_t gid, uint16_t mode); +uint8_t usb2_get_bus_index(struct usb2_device *udev); +uint8_t usb2_get_device_index(struct usb2_device *udev); + +#endif /* _USB2_CORE_H_ */ diff --git a/sys/dev/usb2/core/usb2_debug.c b/sys/dev/usb2/core/usb2_debug.c new file mode 100644 index 000000000000..7969662387c7 --- /dev/null +++ b/sys/dev/usb2/core/usb2_debug.c @@ -0,0 +1,153 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +#include +#include +#include +#include + +/* + * Define this unconditionally in case a kernel module is loaded that + * has been compiled with debugging options. + */ +int usb2_debug = 0; + +SYSCTL_NODE(_hw, OID_AUTO, usb2, CTLFLAG_RW, 0, "USB debugging"); +SYSCTL_INT(_hw_usb2, OID_AUTO, debug, CTLFLAG_RW, + &usb2_debug, 0, "Debug level"); + +/*------------------------------------------------------------------------* + * usb2_dump_iface + * + * This function dumps information about an USB interface. + *------------------------------------------------------------------------*/ +void +usb2_dump_iface(struct usb2_interface *iface) +{ + printf("usb2_dump_iface: iface=%p\n", iface); + if (iface == NULL) { + return; + } + printf(" iface=%p idesc=%p altindex=%d\n", + iface, iface->idesc, iface->alt_index); + return; +} + +/*------------------------------------------------------------------------* + * usb2_dump_device + * + * This function dumps information about an USB device. + *------------------------------------------------------------------------*/ +void +usb2_dump_device(struct usb2_device *udev) +{ + printf("usb2_dump_device: dev=%p\n", udev); + if (udev == NULL) { + return; + } + printf(" bus=%p \n" + " address=%d config=%d depth=%d speed=%d self_powered=%d\n" + " power=%d langid=%d\n", + udev->bus, + udev->address, udev->curr_config_no, udev->depth, udev->speed, + udev->flags.self_powered, udev->power, udev->langid); + return; +} + +/*------------------------------------------------------------------------* + * usb2_dump_queue + * + * This function dumps the USB transfer that are queued up on an USB pipe. + *------------------------------------------------------------------------*/ +void +usb2_dump_queue(struct usb2_pipe *pipe) +{ + struct usb2_xfer *xfer; + + printf("usb2_dump_queue: pipe=%p xfer: ", pipe); + TAILQ_FOREACH(xfer, &pipe->pipe_q.head, wait_entry) { + printf(" %p", xfer); + } + printf("\n"); + return; +} + +/*------------------------------------------------------------------------* + * usb2_dump_pipe + * + * This function dumps information about an USB pipe. + *------------------------------------------------------------------------*/ +void +usb2_dump_pipe(struct usb2_pipe *pipe) +{ + if (pipe) { + printf("usb2_dump_pipe: pipe=%p", pipe); + + printf(" edesc=%p isoc_next=%d toggle_next=%d", + pipe->edesc, pipe->isoc_next, pipe->toggle_next); + + if (pipe->edesc) { + printf(" bEndpointAddress=0x%02x", + pipe->edesc->bEndpointAddress); + } + printf("\n"); + usb2_dump_queue(pipe); + } else { + printf("usb2_dump_pipe: pipe=NULL\n"); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_dump_xfer + * + * This function dumps information about an USB transfer. + *------------------------------------------------------------------------*/ +void +usb2_dump_xfer(struct usb2_xfer *xfer) +{ + printf("usb2_dump_xfer: xfer=%p\n", xfer); + if (xfer == NULL) { + return; + } + if (xfer->pipe == NULL) { + printf("xfer %p: pipe=NULL\n", + xfer); + return; + } + printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d " + "pipe=%p ep=0x%02x attr=0x%02x\n", + xfer, xfer->udev, + UGETW(xfer->udev->ddesc.idVendor), + UGETW(xfer->udev->ddesc.idProduct), + xfer->udev->address, xfer->pipe, + xfer->pipe->edesc->bEndpointAddress, + xfer->pipe->edesc->bmAttributes); + return; +} diff --git a/sys/dev/usb2/core/usb2_debug.h b/sys/dev/usb2/core/usb2_debug.h new file mode 100644 index 000000000000..92dcbd5b9c2c --- /dev/null +++ b/sys/dev/usb2/core/usb2_debug.h @@ -0,0 +1,70 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 file contains various factored out debug macros. */ + +#ifndef _USB2_DEBUG_H_ +#define _USB2_DEBUG_H_ + +/* Declare parent SYSCTL USB node. */ +SYSCTL_DECL(_hw_usb2); + +/* Declare global USB debug variable. */ +extern int usb2_debug; + +/* Force debugging until further */ +#ifndef USB_DEBUG +#define USB_DEBUG 1 +#endif + +/* Check if USB debugging is enabled. */ +#ifdef USB_DEBUG_VAR +#if (USB_DEBUG != 0) +#define DPRINTFN(n,fmt,...) do { \ + if ((USB_DEBUG_VAR) >= (n)) { \ + printf("%s:%u: " fmt, \ + __FUNCTION__, __LINE__,## __VA_ARGS__); \ + } \ +} while (0) +#define DPRINTF(...) DPRINTFN(1, __VA_ARGS__) +#else +#define DPRINTF(...) do { } while (0) +#define DPRINTFN(...) do { } while (0) +#endif +#endif + +struct usb2_interface; +struct usb2_device; +struct usb2_pipe; +struct usb2_xfer; + +void usb2_dump_iface(struct usb2_interface *iface); +void usb2_dump_device(struct usb2_device *udev); +void usb2_dump_queue(struct usb2_pipe *pipe); +void usb2_dump_pipe(struct usb2_pipe *pipe); +void usb2_dump_xfer(struct usb2_xfer *xfer); + +#endif /* _USB2_DEBUG_H_ */ diff --git a/sys/dev/usb2/core/usb2_dev.c b/sys/dev/usb2/core/usb2_dev.c new file mode 100644 index 000000000000..1afe83e1f9ee --- /dev/null +++ b/sys/dev/usb2/core/usb2_dev.c @@ -0,0 +1,2786 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2006-2008 Hans Petter Selasky. 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. + * + * + * usb2_dev.c - An abstraction layer for creating devices under /dev/... + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_fifo_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#if USB_DEBUG +static int usb2_fifo_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); +SYSCTL_INT(_hw_usb2_dev, OID_AUTO, debug, CTLFLAG_RW, + &usb2_fifo_debug, 0, "Debug Level"); +#endif + +#if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ + ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) +#define USB_UCRED struct ucred *ucred, +#else +#define USB_UCRED +#endif + +/* prototypes */ + +static uint32_t usb2_path_convert_one(const char **pp); +static uint32_t usb2_path_convert(const char *path); +static int usb2_check_access(int fflags, struct usb2_perm *puser); +static int usb2_fifo_open(struct usb2_fifo *f, struct file *fp, struct thread *td, int fflags); +static void usb2_fifo_close(struct usb2_fifo *f, struct thread *td, int fflags); +static void usb2_dev_init(void *arg); +static void usb2_dev_init_post(void *arg); +static void usb2_dev_uninit(void *arg); +static int usb2_fifo_uiomove(struct usb2_fifo *f, void *cp, int n, struct uio *uio); +static void usb2_fifo_check_methods(struct usb2_fifo_methods *pm); +static void usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev); +static struct usb2_fifo *usb2_fifo_alloc(void); +static struct usb2_pipe *usb2_dev_get_pipe(struct usb2_device *udev, uint8_t iface_index, uint8_t ep_index, uint8_t dir); + +static d_fdopen_t usb2_fdopen; +static d_close_t usb2_close; +static d_ioctl_t usb2_ioctl; + +static fo_rdwr_t usb2_read_f; +static fo_rdwr_t usb2_write_f; + +#if __FreeBSD_version > 800009 +static fo_truncate_t usb2_truncate_f; + +#endif +static fo_ioctl_t usb2_ioctl_f; +static fo_poll_t usb2_poll_f; +static fo_kqfilter_t usb2_kqfilter_f; +static fo_stat_t usb2_stat_f; +static fo_close_t usb2_close_f; + +static usb2_fifo_open_t usb2_fifo_dummy_open; +static usb2_fifo_close_t usb2_fifo_dummy_close; +static usb2_fifo_ioctl_t usb2_fifo_dummy_ioctl; +static usb2_fifo_cmd_t usb2_fifo_dummy_cmd; + +static struct usb2_perm usb2_perm = { + .uid = UID_ROOT, + .gid = GID_OPERATOR, + .mode = 0660, +}; + +static struct cdevsw usb2_devsw = { + .d_version = D_VERSION, + .d_fdopen = usb2_fdopen, + .d_close = usb2_close, + .d_ioctl = usb2_ioctl, + .d_name = "usb", + .d_flags = D_TRACKCLOSE, +}; + +static struct fileops usb2_ops_f = { + .fo_read = usb2_read_f, + .fo_write = usb2_write_f, +#if __FreeBSD_version > 800009 + .fo_truncate = usb2_truncate_f, +#endif + .fo_ioctl = usb2_ioctl_f, + .fo_poll = usb2_poll_f, + .fo_kqfilter = usb2_kqfilter_f, + .fo_stat = usb2_stat_f, + .fo_close = usb2_close_f, + .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE +}; + +static const dev_clone_fn usb2_clone_ptr = &usb2_clone; +static struct cdev *usb2_dev; +static uint32_t usb2_last_devloc = 0 - 1; +static eventhandler_tag usb2_clone_tag; +static void *usb2_old_f_data; +static struct fileops *usb2_old_f_ops; +static TAILQ_HEAD(, usb2_symlink) usb2_sym_head; +static struct sx usb2_sym_lock; + +struct mtx usb2_ref_lock; + +static uint32_t +usb2_path_convert_one(const char **pp) +{ + const char *ptr; + uint32_t temp = 0; + + ptr = *pp; + + while ((*ptr >= '0') && (*ptr <= '9')) { + temp *= 10; + temp += (*ptr - '0'); + if (temp >= 1000000) { + /* catch overflow early */ + return (0 - 1); + } + ptr++; + } + + if (*ptr == '.') { + /* skip dot */ + ptr++; + } + *pp = ptr; + + return (temp); +} + +/*------------------------------------------------------------------------* + * usb2_path_convert + * + * Path format: "/dev/usb..." + * + * Returns: Path converted into numerical format. + *------------------------------------------------------------------------*/ +static uint32_t +usb2_path_convert(const char *path) +{ + uint32_t temp; + uint32_t devloc; + + devloc = 0; + + temp = usb2_path_convert_one(&path); + + if (temp >= USB_BUS_MAX) { + return (0 - 1); + } + devloc += temp; + + temp = usb2_path_convert_one(&path); + + if (temp >= USB_DEV_MAX) { + return (0 - 1); + } + devloc += (temp * USB_BUS_MAX); + + temp = usb2_path_convert_one(&path); + + if (temp >= USB_IFACE_MAX) { + return (0 - 1); + } + devloc += (temp * USB_DEV_MAX * USB_BUS_MAX); + + temp = usb2_path_convert_one(&path); + + if (temp >= ((USB_FIFO_MAX / 2) + (USB_EP_MAX / 2))) { + return (0 - 1); + } + devloc += (temp * USB_IFACE_MAX * USB_DEV_MAX * USB_BUS_MAX); + + return (devloc); +} + +/*------------------------------------------------------------------------* + * usb2_set_iface_perm + * + * This function will set the interface permissions. + *------------------------------------------------------------------------*/ +void +usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index, + uint32_t uid, uint32_t gid, uint16_t mode) +{ + struct usb2_interface *iface; + + iface = usb2_get_iface(udev, iface_index); + if (iface && iface->idesc) { + mtx_lock(&usb2_ref_lock); + iface->perm.uid = uid; + iface->perm.gid = gid; + iface->perm.mode = mode; + mtx_unlock(&usb2_ref_lock); + + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_perm + * + * This function will set the permissions at the given level. + * + * Return values: + * 0: Success. + * Else: Failure. + *------------------------------------------------------------------------*/ +static int +usb2_set_perm(struct usb2_dev_perm *psrc, uint8_t level) +{ + struct usb2_location loc; + struct usb2_perm *pdst; + uint32_t devloc; + int error; + + /* check if the current thread can change USB permissions. */ + error = priv_check(curthread, PRIV_ROOT); + if (error) { + return (error); + } + /* range check device location */ + if ((psrc->bus_index >= USB_BUS_MAX) || + (psrc->dev_index >= USB_DEV_MAX) || + (psrc->iface_index >= USB_IFACE_MAX)) { + return (EINVAL); + } + if (level == 1) + devloc = USB_BUS_MAX; /* use root-HUB to access bus */ + else + devloc = 0; + switch (level) { + case 3: + devloc += psrc->iface_index * + USB_DEV_MAX * USB_BUS_MAX; + /* FALLTHROUGH */ + case 2: + devloc += psrc->dev_index * + USB_BUS_MAX; + /* FALLTHROUGH */ + case 1: + devloc += psrc->bus_index; + break; + default: + break; + } + + if ((level > 0) && (level < 4)) { + error = usb2_ref_device(NULL, &loc, devloc); + if (error) { + return (error); + } + } + switch (level) { + case 3: + if (loc.iface == NULL) { + usb2_unref_device(&loc); + return (EINVAL); + } + pdst = &loc.iface->perm; + break; + case 2: + pdst = &loc.udev->perm; + break; + case 1: + pdst = &loc.bus->perm; + break; + default: + pdst = &usb2_perm; + break; + } + + /* all permissions are protected by "usb2_ref_lock" */ + mtx_lock(&usb2_ref_lock); + pdst->uid = psrc->user_id; + pdst->gid = psrc->group_id; + pdst->mode = psrc->mode; + mtx_unlock(&usb2_ref_lock); + + if ((level > 0) && (level < 4)) { + usb2_unref_device(&loc); + } + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_get_perm + * + * This function will get the permissions at the given level. + * + * Return values: + * 0: Success. + * Else: Failure. + *------------------------------------------------------------------------*/ +static int +usb2_get_perm(struct usb2_dev_perm *pdst, uint8_t level) +{ + struct usb2_location loc; + struct usb2_perm *psrc; + uint32_t devloc; + int error; + + if ((pdst->bus_index >= USB_BUS_MAX) || + (pdst->dev_index >= USB_DEV_MAX) || + (pdst->iface_index >= USB_IFACE_MAX)) { + return (EINVAL); + } + if (level == 1) + devloc = USB_BUS_MAX; /* use root-HUB to access bus */ + else + devloc = 0; + switch (level) { + case 3: + devloc += pdst->iface_index * + USB_DEV_MAX * USB_BUS_MAX; + /* FALLTHROUGH */ + case 2: + devloc += pdst->dev_index * + USB_BUS_MAX; + /* FALLTHROUGH */ + case 1: + devloc += pdst->bus_index; + break; + default: + break; + } + + if ((level > 0) && (level < 4)) { + error = usb2_ref_device(NULL, &loc, devloc); + if (error) { + return (error); + } + } + switch (level) { + case 3: + if (loc.iface == NULL) { + usb2_unref_device(&loc); + return (EINVAL); + } + psrc = &loc.iface->perm; + break; + case 2: + psrc = &loc.udev->perm; + break; + case 1: + psrc = &loc.bus->perm; + break; + default: + psrc = &usb2_perm; + break; + } + + /* all permissions are protected by "usb2_ref_lock" */ + mtx_lock(&usb2_ref_lock); + if (psrc->mode != 0) { + pdst->user_id = psrc->uid; + pdst->group_id = psrc->gid; + pdst->mode = psrc->mode; + } else { + /* access entry at this level and location is not active */ + pdst->user_id = 0; + pdst->group_id = 0; + pdst->mode = 0; + } + mtx_unlock(&usb2_ref_lock); + + if ((level > 0) && (level < 4)) { + usb2_unref_device(&loc); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_check_access + * + * This function will verify the given access information. + * + * Return values: + * 0: Access granted. + * Else: No access granted. + *------------------------------------------------------------------------*/ +static int +usb2_check_access(int fflags, struct usb2_perm *puser) +{ + mode_t accmode; + + if ((fflags & (FWRITE | FREAD)) && (puser->mode != 0)) { + /* continue */ + } else { + return (EPERM); /* no access */ + } + + accmode = 0; + if (fflags & FWRITE) + accmode |= VWRITE; + if (fflags & FREAD) + accmode |= VREAD; + + return (vaccess(VCHR, puser->mode, puser->uid, + puser->gid, accmode, curthread->td_ucred, NULL)); +} + +/*------------------------------------------------------------------------* + * usb2_ref_device + * + * This function is used to atomically refer an USB device by its + * device location. If this function returns success the USB device + * will not dissappear until the USB device is unreferenced. + * + * Return values: + * 0: Success, refcount incremented on the given USB device. + * Else: Failure. + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_ref_device(struct file *fp, struct usb2_location *ploc, uint32_t devloc) +{ + struct usb2_fifo **ppf; + struct usb2_fifo *f; + int fflags; + uint8_t need_uref; + + if (fp) { + /* check if we need uref hint */ + need_uref = devloc ? 0 : 1; + /* get devloc - already verified */ + devloc = USB_P2U(fp->f_data); + /* get file flags */ + fflags = fp->f_flag; + /* only ref FIFO */ + ploc->is_uref = 0; + /* devloc should be valid */ + } else { + /* we need uref */ + need_uref = 1; + /* only ref device */ + fflags = 0; + /* search for FIFO */ + ploc->is_uref = 1; + /* check "devloc" */ + if (devloc >= (USB_BUS_MAX * USB_DEV_MAX * + USB_IFACE_MAX * ((USB_EP_MAX / 2) + (USB_FIFO_MAX / 2)))) { + return (USB_ERR_INVAL); + } + } + + /* store device location */ + ploc->devloc = devloc; + ploc->bus_index = devloc % USB_BUS_MAX; + ploc->dev_index = (devloc / USB_BUS_MAX) % USB_DEV_MAX; + ploc->iface_index = (devloc / (USB_BUS_MAX * + USB_DEV_MAX)) % USB_IFACE_MAX; + ploc->ep_index = (devloc / (USB_BUS_MAX * USB_DEV_MAX * + USB_IFACE_MAX)); + + mtx_lock(&usb2_ref_lock); + ploc->bus = devclass_get_softc(usb2_devclass_ptr, ploc->bus_index); + if (ploc->bus == NULL) { + DPRINTFN(2, "no bus at %u\n", ploc->bus_index); + goto error; + } + if (ploc->dev_index >= ploc->bus->devices_max) { + DPRINTFN(2, "invalid dev index, %u\n", ploc->dev_index); + goto error; + } + ploc->udev = ploc->bus->devices[ploc->dev_index]; + if (ploc->udev == NULL) { + DPRINTFN(2, "no device at %u\n", ploc->dev_index); + goto error; + } + if (ploc->udev->refcount == USB_DEV_REF_MAX) { + DPRINTFN(2, "no dev ref\n"); + goto error; + } + ploc->iface = usb2_get_iface(ploc->udev, ploc->iface_index); + if (ploc->ep_index != 0) { + /* non control endpoint - we need an interface */ + if (ploc->iface == NULL) { + DPRINTFN(2, "no iface\n"); + goto error; + } + if (ploc->iface->idesc == NULL) { + DPRINTFN(2, "no idesc\n"); + goto error; + } + } + /* check if we are doing an open */ + if (fp == NULL) { + /* set defaults */ + ploc->txfifo = NULL; + ploc->rxfifo = NULL; + ploc->is_write = 0; + ploc->is_read = 0; + } else { + /* check for write */ + if (fflags & FWRITE) { + ppf = ploc->udev->fifo; + f = ppf[ploc->ep_index + USB_FIFO_TX]; + ploc->txfifo = f; + ploc->is_write = 1; /* ref */ + if ((f == NULL) || + (f->refcount == USB_FIFO_REF_MAX) || + (f->curr_file != fp)) { + goto error; + } + } else { + ploc->txfifo = NULL; + ploc->is_write = 0; /* no ref */ + } + + /* check for read */ + if (fflags & FREAD) { + ppf = ploc->udev->fifo; + f = ppf[ploc->ep_index + USB_FIFO_RX]; + ploc->rxfifo = f; + ploc->is_read = 1; /* ref */ + if ((f == NULL) || + (f->refcount == USB_FIFO_REF_MAX) || + (f->curr_file != fp)) { + goto error; + } + } else { + ploc->rxfifo = NULL; + ploc->is_read = 0; /* no ref */ + } + } + + /* when everything is OK we increment the refcounts */ + if (ploc->is_write) { + DPRINTFN(2, "ref write\n"); + ploc->txfifo->refcount++; + if (ploc->txfifo->flag_no_uref == 0) { + /* we need extra locking */ + ploc->is_uref = 1; + } + } + if (ploc->is_read) { + DPRINTFN(2, "ref read\n"); + ploc->rxfifo->refcount++; + if (ploc->rxfifo->flag_no_uref == 0) { + /* we need extra locking */ + ploc->is_uref = 1; + } + } + if (ploc->is_uref) { + if (need_uref) { + DPRINTFN(2, "ref udev - needed\n"); + ploc->udev->refcount++; + } else { + DPRINTFN(2, "ref udev - not needed\n"); + ploc->is_uref = 0; + } + } + mtx_unlock(&usb2_ref_lock); + + if (ploc->is_uref) { + /* + * We are about to alter the bus-state. Apply the + * required locks. + */ + sx_xlock(ploc->udev->default_sx + 1); + mtx_lock(&Giant); /* XXX */ + } + return (0); + +error: + mtx_unlock(&usb2_ref_lock); + DPRINTFN(2, "fail\n"); + return (USB_ERR_INVAL); +} + +/*------------------------------------------------------------------------* + * usb2_unref_device + * + * This function will release the reference count by one unit for the + * given USB device. + *------------------------------------------------------------------------*/ +void +usb2_unref_device(struct usb2_location *ploc) +{ + if (ploc->is_uref) { + mtx_unlock(&Giant); /* XXX */ + sx_unlock(ploc->udev->default_sx + 1); + } + mtx_lock(&usb2_ref_lock); + if (ploc->is_read) { + if (--(ploc->rxfifo->refcount) == 0) { + usb2_cv_signal(&ploc->rxfifo->cv_drain); + } + } + if (ploc->is_write) { + if (--(ploc->txfifo->refcount) == 0) { + usb2_cv_signal(&ploc->txfifo->cv_drain); + } + } + if (ploc->is_uref) { + if (--(ploc->udev->refcount) == 0) { + usb2_cv_signal(ploc->udev->default_cv + 1); + } + } + mtx_unlock(&usb2_ref_lock); + return; +} + +static struct usb2_fifo * +usb2_fifo_alloc(void) +{ + struct usb2_fifo *f; + + f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); + if (f) { + usb2_cv_init(&f->cv_io, "FIFO-IO"); + usb2_cv_init(&f->cv_drain, "FIFO-DRAIN"); + f->refcount = 1; + } + return (f); +} + +/*------------------------------------------------------------------------* + * usb2_fifo_create + *------------------------------------------------------------------------*/ +static int +usb2_fifo_create(struct usb2_location *ploc, uint32_t *pdevloc, int fflags) +{ + struct usb2_device *udev = ploc->udev; + struct usb2_fifo *f; + struct usb2_pipe *pipe; + uint8_t iface_index = ploc->iface_index; + uint8_t dev_ep_index = ploc->ep_index; + uint8_t n; + uint8_t is_tx; + uint8_t is_rx; + uint8_t no_null; + uint8_t is_busy; + + is_tx = (fflags & FWRITE) ? 1 : 0; + is_rx = (fflags & FREAD) ? 1 : 0; + no_null = 1; + is_busy = 0; + + /* search for a free FIFO slot */ + + for (n = 0;; n += 2) { + + if (n == USB_FIFO_MAX) { + if (no_null) { + no_null = 0; + n = 0; + } else { + /* end of FIFOs reached */ + return (ENOMEM); + } + } + /* Check for TX FIFO */ + if (is_tx) { + f = udev->fifo[n + USB_FIFO_TX]; + if (f != NULL) { + if (f->dev_ep_index != dev_ep_index) { + /* wrong endpoint index */ + continue; + } + if ((dev_ep_index != 0) && + (f->iface_index != iface_index)) { + /* wrong interface index */ + continue; + } + if (f->curr_file != NULL) { + /* FIFO is opened */ + is_busy = 1; + continue; + } + } else if (no_null) { + continue; + } + } + /* Check for RX FIFO */ + if (is_rx) { + f = udev->fifo[n + USB_FIFO_RX]; + if (f != NULL) { + if (f->dev_ep_index != dev_ep_index) { + /* wrong endpoint index */ + continue; + } + if ((dev_ep_index != 0) && + (f->iface_index != iface_index)) { + /* wrong interface index */ + continue; + } + if (f->curr_file != NULL) { + /* FIFO is opened */ + is_busy = 1; + continue; + } + } else if (no_null) { + continue; + } + } + break; + } + + if (no_null == 0) { + if (dev_ep_index >= (USB_EP_MAX / 2)) { + /* we don't create any endpoints in this range */ + return (is_busy ? EBUSY : EINVAL); + } + } + /* Check TX FIFO */ + if (is_tx && + (udev->fifo[n + USB_FIFO_TX] == NULL)) { + pipe = usb2_dev_get_pipe(udev, + iface_index, dev_ep_index, USB_FIFO_TX); + if (pipe == NULL) { + return (EINVAL); + } + f = usb2_fifo_alloc(); + if (f == NULL) { + return (ENOMEM); + } + /* update some fields */ + f->fifo_index = n + USB_FIFO_TX; + f->dev_ep_index = dev_ep_index; + f->priv_mtx = udev->default_mtx; + f->priv_sc0 = pipe; + f->methods = &usb2_ugen_methods; + f->iface_index = iface_index; + f->udev = udev; + if (dev_ep_index != 0) { + f->flag_no_uref = 1; + } + mtx_lock(&usb2_ref_lock); + udev->fifo[n + USB_FIFO_TX] = f; + mtx_unlock(&usb2_ref_lock); + } + /* Check RX FIFO */ + if (is_rx && + (udev->fifo[n + USB_FIFO_RX] == NULL)) { + + pipe = usb2_dev_get_pipe(udev, + iface_index, dev_ep_index, USB_FIFO_RX); + if (pipe == NULL) { + return (EINVAL); + } + f = usb2_fifo_alloc(); + if (f == NULL) { + return (ENOMEM); + } + /* update some fields */ + f->fifo_index = n + USB_FIFO_RX; + f->dev_ep_index = dev_ep_index; + f->priv_mtx = udev->default_mtx; + f->priv_sc0 = pipe; + f->methods = &usb2_ugen_methods; + f->iface_index = iface_index; + f->udev = udev; + if (dev_ep_index != 0) { + f->flag_no_uref = 1; + } + mtx_lock(&usb2_ref_lock); + udev->fifo[n + USB_FIFO_RX] = f; + mtx_unlock(&usb2_ref_lock); + } + if (is_tx) { + ploc->txfifo = udev->fifo[n + USB_FIFO_TX]; + } + if (is_rx) { + ploc->rxfifo = udev->fifo[n + USB_FIFO_RX]; + } + /* replace endpoint index by FIFO index */ + + (*pdevloc) %= (USB_BUS_MAX * USB_DEV_MAX * USB_IFACE_MAX); + (*pdevloc) += (USB_BUS_MAX * USB_DEV_MAX * USB_IFACE_MAX) * n; + + /* complete */ + + return (0); +} + +void +usb2_fifo_free(struct usb2_fifo *f) +{ + uint8_t n; + + if (f == NULL) { + /* be NULL safe */ + return; + } + /* destroy symlink devices, if any */ + for (n = 0; n != 2; n++) { + if (f->symlink[n]) { + usb2_free_symlink(f->symlink[n]); + f->symlink[n] = NULL; + } + } + mtx_lock(&usb2_ref_lock); + + /* delink ourselves to stop calls from userland */ + if ((f->fifo_index < USB_FIFO_MAX) && + (f->udev != NULL) && + (f->udev->fifo[f->fifo_index] == f)) { + f->udev->fifo[f->fifo_index] = NULL; + } else { + DPRINTFN(0, "USB FIFO %p has not been linked!\n", f); + } + + /* decrease refcount */ + f->refcount--; + /* prevent any write flush */ + f->flag_iserror = 1; + /* need to wait until all callers have exited */ + while (f->refcount != 0) { + mtx_unlock(&usb2_ref_lock); /* avoid LOR */ + mtx_lock(f->priv_mtx); + /* get I/O thread out of any sleep state */ + if (f->flag_sleeping) { + f->flag_sleeping = 0; + usb2_cv_broadcast(&f->cv_io); + } + mtx_unlock(f->priv_mtx); + mtx_lock(&usb2_ref_lock); + + /* wait for sync */ + usb2_cv_wait(&f->cv_drain, &usb2_ref_lock); + } + mtx_unlock(&usb2_ref_lock); + + /* take care of closing the device here, if any */ + usb2_fifo_close(f, curthread, 0); + + usb2_cv_destroy(&f->cv_io); + usb2_cv_destroy(&f->cv_drain); + + free(f, M_USBDEV); + return; +} + +static struct usb2_pipe * +usb2_dev_get_pipe(struct usb2_device *udev, + uint8_t iface_index, uint8_t ep_index, uint8_t dir) +{ + struct usb2_pipe *pipe; + uint8_t ep_dir; + + if (ep_index == 0) { + pipe = &udev->default_pipe; + } else { + if (dir == USB_FIFO_RX) { + if (udev->flags.usb2_mode == USB_MODE_HOST) { + ep_dir = UE_DIR_IN; + } else { + ep_dir = UE_DIR_OUT; + } + } else { + if (udev->flags.usb2_mode == USB_MODE_HOST) { + ep_dir = UE_DIR_OUT; + } else { + ep_dir = UE_DIR_IN; + } + } + pipe = usb2_get_pipe_by_addr(udev, ep_index | ep_dir); + } + + if (pipe == NULL) { + /* if the pipe does not exist then return */ + return (NULL); + } + if (pipe->edesc == NULL) { + /* invalid pipe */ + return (NULL); + } + if (ep_index != 0) { + if (pipe->iface_index != iface_index) { + /* + * Permissions violation - trying to access a + * pipe that does not belong to the interface. + */ + return (NULL); + } + } + return (pipe); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_fifo_open + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static int +usb2_fifo_open(struct usb2_fifo *f, struct file *fp, struct thread *td, + int fflags) +{ + int err; + + if (f == NULL) { + /* no FIFO there */ + DPRINTFN(2, "no FIFO\n"); + return (ENXIO); + } + /* remove FWRITE and FREAD flags */ + fflags &= ~(FWRITE | FREAD); + + /* set correct file flags */ + if ((f->fifo_index & 1) == USB_FIFO_TX) { + fflags |= FWRITE; + } else { + fflags |= FREAD; + } + + /* check if we are already opened */ + /* we don't need any locks when checking this variable */ + if (f->curr_file) { + err = EBUSY; + goto done; + } + /* call open method */ + err = (f->methods->f_open) (f, fflags, td); + if (err) { + goto done; + } + mtx_lock(f->priv_mtx); + + /* reset sleep flag */ + f->flag_sleeping = 0; + + /* reset error flag */ + f->flag_iserror = 0; + + /* reset complete flag */ + f->flag_iscomplete = 0; + + /* reset select flag */ + f->flag_isselect = 0; + + /* reset flushing flag */ + f->flag_flushing = 0; + + /* reset ASYNC proc flag */ + f->async_p = NULL; + + /* set which file we belong to */ + mtx_lock(&usb2_ref_lock); + f->curr_file = fp; + mtx_unlock(&usb2_ref_lock); + + /* reset queue */ + usb2_fifo_reset(f); + + mtx_unlock(f->priv_mtx); +done: + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_fifo_reset + *------------------------------------------------------------------------*/ +void +usb2_fifo_reset(struct usb2_fifo *f) +{ + struct usb2_mbuf *m; + + if (f == NULL) { + return; + } + while (1) { + USB_IF_DEQUEUE(&f->used_q, m); + if (m) { + USB_IF_ENQUEUE(&f->free_q, m); + } else { + break; + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_fifo_close + *------------------------------------------------------------------------*/ +static void +usb2_fifo_close(struct usb2_fifo *f, struct thread *td, int fflags) +{ + int err; + + /* check if we are not opened */ + if (!f->curr_file) { + /* nothing to do - already closed */ + return; + } + mtx_lock(f->priv_mtx); + + /* clear current file flag */ + f->curr_file = NULL; + + /* check if we are selected */ + if (f->flag_isselect) { + selwakeup(&f->selinfo); + f->flag_isselect = 0; + } + /* check if a thread wants SIGIO */ + if (f->async_p != NULL) { + PROC_LOCK(f->async_p); + psignal(f->async_p, SIGIO); + PROC_UNLOCK(f->async_p); + f->async_p = NULL; + } + /* remove FWRITE and FREAD flags */ + fflags &= ~(FWRITE | FREAD); + + /* flush written data, if any */ + if ((f->fifo_index & 1) == USB_FIFO_TX) { + + if (!f->flag_iserror) { + + /* set flushing flag */ + f->flag_flushing = 1; + + /* start write transfer, if not already started */ + (f->methods->f_start_write) (f); + + /* check if flushed already */ + while (f->flag_flushing && + (!f->flag_iserror)) { + /* wait until all data has been written */ + f->flag_sleeping = 1; + err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx); + if (err) { + DPRINTF("signal received\n"); + break; + } + } + } + fflags |= FWRITE; + + /* stop write transfer, if not already stopped */ + (f->methods->f_stop_write) (f); + } else { + fflags |= FREAD; + + /* stop write transfer, if not already stopped */ + (f->methods->f_stop_read) (f); + } + + /* check if we are sleeping */ + if (f->flag_sleeping) { + DPRINTFN(2, "Sleeping at close!\n"); + } + mtx_unlock(f->priv_mtx); + + /* call close method */ + (f->methods->f_close) (f, fflags, td); + + DPRINTF("closed\n"); + return; +} + +/*------------------------------------------------------------------------* + * usb2_check_thread_perm + * + * Returns: + * 0: Has permission. + * Else: No permission. + *------------------------------------------------------------------------*/ +int +usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, + int fflags, uint8_t iface_index, uint8_t ep_index) +{ + struct usb2_interface *iface; + int err; + + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (EINVAL); + } + if (iface->idesc == NULL) { + return (EINVAL); + } + /* scan down the permissions tree */ + if ((ep_index != 0) && iface && + (usb2_check_access(fflags, &iface->perm) == 0)) { + /* we got access through the interface */ + err = 0; + } else if (udev && + (usb2_check_access(fflags, &udev->perm) == 0)) { + /* we got access through the device */ + err = 0; + } else if (udev->bus && + (usb2_check_access(fflags, &udev->bus->perm) == 0)) { + /* we got access through the USB bus */ + err = 0; + } else if (usb2_check_access(fflags, &usb2_perm) == 0) { + /* we got general access */ + err = 0; + } else { + /* no access */ + err = EPERM; + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_fdopen - cdev callback + *------------------------------------------------------------------------*/ +static int +usb2_fdopen(struct cdev *dev, int xxx_oflags, struct thread *td, + struct file *fp) +{ + struct usb2_location loc; + uint32_t devloc; + int err; + int fflags; + + DPRINTFN(2, "oflags=0x%08x\n", xxx_oflags); + + devloc = usb2_last_devloc; + usb2_last_devloc = (0 - 1); /* reset "usb2_last_devloc" */ + + if (fp == NULL) { + DPRINTFN(2, "fp == NULL\n"); + return (ENXIO); + } + if (usb2_old_f_data != fp->f_data) { + if (usb2_old_f_data != NULL) { + DPRINTFN(0, "File data mismatch!\n"); + return (ENXIO); + } + usb2_old_f_data = fp->f_data; + } + if (usb2_old_f_ops != fp->f_ops) { + if (usb2_old_f_ops != NULL) { + DPRINTFN(0, "File ops mismatch!\n"); + return (ENXIO); + } + usb2_old_f_ops = fp->f_ops; + } + fflags = fp->f_flag; + DPRINTFN(2, "fflags=0x%08x\n", fflags); + + if (!(fflags & (FREAD | FWRITE))) { + /* should not happen */ + return (EPERM); + } + if (devloc == (uint32_t)(0 - 2)) { + /* tried to open "/dev/usb" */ + return (0); + } else if (devloc == (uint32_t)(0 - 1)) { + /* tried to open "/dev/usb " */ + DPRINTFN(2, "no devloc\n"); + return (ENXIO); + } + err = usb2_ref_device(NULL, &loc, devloc); + if (err) { + DPRINTFN(2, "cannot ref device\n"); + return (ENXIO); + } + err = usb2_check_thread_perm(loc.udev, td, fflags, + loc.iface_index, loc.ep_index); + + /* check for error */ + if (err) { + usb2_unref_device(&loc); + return (err); + } + /* create FIFOs, if any */ + err = usb2_fifo_create(&loc, &devloc, fflags); + /* check for error */ + if (err) { + usb2_unref_device(&loc); + return (err); + } + if (fflags & FREAD) { + err = usb2_fifo_open(loc.rxfifo, fp, td, fflags); + if (err) { + DPRINTFN(2, "read open failed\n"); + usb2_unref_device(&loc); + return (err); + } + } + if (fflags & FWRITE) { + err = usb2_fifo_open(loc.txfifo, fp, td, fflags); + if (err) { + DPRINTFN(2, "write open failed\n"); + if (fflags & FREAD) { + usb2_fifo_close(loc.rxfifo, td, + fflags); + } + usb2_unref_device(&loc); + return (err); + } + } + /* + * Take over the file so that we get all the callbacks + * directly and don't have to create another device: + */ + finit(fp, fp->f_flag, DTYPE_VNODE, + ((uint8_t *)0) + devloc, &usb2_ops_f); + + usb2_unref_device(&loc); + + DPRINTFN(2, "error=%d\n", err); + + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_close - cdev callback + *------------------------------------------------------------------------*/ +static int +usb2_close(struct cdev *dev, int flag, int mode, struct thread *p) +{ + DPRINTF("\n"); + return (0); /* nothing to do */ +} + +/*------------------------------------------------------------------------* + * usb2_close - cdev callback + *------------------------------------------------------------------------*/ +static int +usb2_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td) +{ + union { + struct usb2_read_dir *urd; + struct usb2_dev_perm *udp; + void *data; + } u; + int err; + + u.data = data; + + switch (cmd) { + case USB_READ_DIR: + err = usb2_read_symlink(u.urd->urd_data, + u.urd->urd_startentry, u.urd->urd_maxlen); + break; + case USB_SET_IFACE_PERM: + err = usb2_set_perm(u.udp, 3); + break; + case USB_SET_DEVICE_PERM: + err = usb2_set_perm(u.udp, 2); + break; + case USB_SET_BUS_PERM: + err = usb2_set_perm(u.udp, 1); + break; + case USB_SET_ROOT_PERM: + err = usb2_set_perm(u.udp, 0); + break; + case USB_GET_IFACE_PERM: + err = usb2_get_perm(u.udp, 3); + break; + case USB_GET_DEVICE_PERM: + err = usb2_get_perm(u.udp, 2); + break; + case USB_GET_BUS_PERM: + err = usb2_get_perm(u.udp, 1); + break; + case USB_GET_ROOT_PERM: + err = usb2_get_perm(u.udp, 0); + break; + case USB_DEV_QUIRK_GET: + case USB_QUIRK_NAME_GET: + case USB_DEV_QUIRK_ADD: + case USB_DEV_QUIRK_REMOVE: + err = usb2_quirk_ioctl_p(cmd, data, fflag, td); + break; + default: + err = ENOTTY; + break; + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_clone - cdev callback + * + * This function is the kernel clone callback for "/dev/usbX.Y". + * + * NOTE: This function assumes that the clone and device open + * operation is atomic. + *------------------------------------------------------------------------*/ +static void +usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev) +{ + enum { + USB_DNAME_LEN = sizeof(USB_DEVICE_NAME) - 1, + USB_GNAME_LEN = sizeof(USB_GENERIC_NAME) - 1, + }; + + if (*dev) { + /* someone else has created a device */ + return; + } + /* reset device location */ + usb2_last_devloc = (uint32_t)(0 - 1); + + /* + * Check if we are matching "usb", "ugen" or an internal + * symbolic link: + */ + if ((namelen >= USB_DNAME_LEN) && + (bcmp(name, USB_DEVICE_NAME, USB_DNAME_LEN) == 0)) { + if (namelen == USB_DNAME_LEN) { + /* USB management device location */ + usb2_last_devloc = (uint32_t)(0 - 2); + } else { + /* USB endpoint */ + usb2_last_devloc = + usb2_path_convert(name + USB_DNAME_LEN); + } + } else if ((namelen >= USB_GNAME_LEN) && + (bcmp(name, USB_GENERIC_NAME, USB_GNAME_LEN) == 0)) { + if (namelen == USB_GNAME_LEN) { + /* USB management device location */ + usb2_last_devloc = (uint32_t)(0 - 2); + } else { + /* USB endpoint */ + usb2_last_devloc = + usb2_path_convert(name + USB_GNAME_LEN); + } + } + if (usb2_last_devloc == (uint32_t)(0 - 1)) { + /* Search for symbolic link */ + usb2_last_devloc = + usb2_lookup_symlink(name, namelen); + } + if (usb2_last_devloc == (uint32_t)(0 - 1)) { + /* invalid location */ + return; + } + dev_ref(usb2_dev); + *dev = usb2_dev; + return; +} + +static void +usb2_dev_init(void *arg) +{ + mtx_init(&usb2_ref_lock, "USB ref mutex", NULL, MTX_DEF); + sx_init(&usb2_sym_lock, "USB sym mutex"); + TAILQ_INIT(&usb2_sym_head); + + /* check the UGEN methods */ + usb2_fifo_check_methods(&usb2_ugen_methods); + return; +} + +SYSINIT(usb2_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb2_dev_init, NULL); + +static void +usb2_dev_init_post(void *arg) +{ + /* + * Create a dummy device so that we are visible. This device + * should never be opened. Therefore a space character is + * appended after the USB device name. + * + * NOTE: The permissions of this device is 0777, because we + * check the permissions again in the open routine against the + * real USB permissions which are not 0777. Else USB access + * will be limited to one user and one group. + */ + usb2_dev = make_dev(&usb2_devsw, 0, UID_ROOT, GID_OPERATOR, + 0777, USB_DEVICE_NAME " "); + if (usb2_dev == NULL) { + DPRINTFN(0, "Could not create usb bus device!\n"); + } + usb2_clone_tag = EVENTHANDLER_REGISTER(dev_clone, usb2_clone_ptr, NULL, 1000); + if (usb2_clone_tag == NULL) { + DPRINTFN(0, "Registering clone handler failed!\n"); + } + return; +} + +SYSINIT(usb2_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb2_dev_init_post, NULL); + +static void +usb2_dev_uninit(void *arg) +{ + if (usb2_clone_tag) { + EVENTHANDLER_DEREGISTER(dev_clone, usb2_clone_tag); + usb2_clone_tag = NULL; + } + if (usb2_dev) { + destroy_dev(usb2_dev); + usb2_dev = NULL; + } + mtx_destroy(&usb2_ref_lock); + sx_destroy(&usb2_sym_lock); + return; +} + +SYSUNINIT(usb2_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_dev_uninit, NULL); + +static int +usb2_close_f(struct file *fp, struct thread *td) +{ + struct usb2_location loc; + int fflags; + int err; + + fflags = fp->f_flag; + + DPRINTFN(2, "fflags=%u\n", fflags); + + err = usb2_ref_device(fp, &loc, 0);; + + /* restore some file variables */ + fp->f_ops = usb2_old_f_ops; + fp->f_data = usb2_old_f_data; + + /* check for error */ + if (err) { + DPRINTFN(2, "could not ref\n"); + goto done; + } + if (fflags & FREAD) { + usb2_fifo_close(loc.rxfifo, td, fflags); + } + if (fflags & FWRITE) { + usb2_fifo_close(loc.txfifo, td, fflags); + } + usb2_unref_device(&loc); + +done: + /* call old close method */ + USB_VNOPS_FO_CLOSE(fp, td, &err); + + return (err); +} + +static int +usb2_ioctl_f_sub(struct usb2_fifo *f, u_long cmd, void *addr, + struct thread *td) +{ + int error = 0; + + switch (cmd) { + case FIODTYPE: + *(int *)addr = 0; /* character device */ + break; + + case FIONBIO: + /* handled by upper FS layer */ + break; + + case FIOASYNC: + if (*(int *)addr) { + if (f->async_p != NULL) { + error = EBUSY; + break; + } + f->async_p = USB_TD_GET_PROC(td); + } else { + f->async_p = NULL; + } + break; + + /* XXX this is not the most general solution */ + case TIOCSPGRP: + if (f->async_p == NULL) { + error = EINVAL; + break; + } + if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { + error = EPERM; + break; + } + break; + default: + return (ENOTTY); + } + return (error); +} + +static int +usb2_ioctl_f(struct file *fp, u_long cmd, void *addr, + struct ucred *cred, struct thread *td) +{ + struct usb2_location loc; + int fflags; + int err_rx; + int err_tx; + int err; + uint8_t is_common = 0; + + err = usb2_ref_device(fp, &loc, 0);; + if (err) { + return (ENXIO); + } + fflags = fp->f_flag; + + DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd); + + if (fflags & FREAD) { + if (fflags & FWRITE) { + /* + * Make sure that the IOCTL is not + * duplicated: + */ + is_common = 1; + } + err_rx = usb2_ioctl_f_sub(loc.rxfifo, cmd, addr, td); + if (err_rx == ENOTTY) { + err_rx = (loc.rxfifo->methods->f_ioctl) ( + loc.rxfifo, cmd, addr, + is_common ? fflags : (fflags & ~FWRITE), td); + } + } else { + err_rx = 0; + } + if (fflags & FWRITE) { + err_tx = usb2_ioctl_f_sub(loc.txfifo, cmd, addr, td); + if (err_tx == ENOTTY) { + if (is_common) + err_tx = 0; /* already handled this IOCTL */ + else + err_tx = (loc.txfifo->methods->f_ioctl) ( + loc.txfifo, cmd, addr, fflags & ~FREAD, td); + } + } else { + err_tx = 0; + } + + if (err_rx) { + err = err_rx; + } else if (err_tx) { + err = err_tx; + } else { + err = 0; /* no error */ + } + usb2_unref_device(&loc); + return (err); +} + +/* ARGSUSED */ +static int +usb2_kqfilter_f(struct file *fp, struct knote *kn) +{ + return (ENXIO); +} + +/* ARGSUSED */ +static int +usb2_poll_f(struct file *fp, int events, + struct ucred *cred, struct thread *td) +{ + struct usb2_location loc; + struct usb2_fifo *f; + struct usb2_mbuf *m; + int fflags; + int revents; + uint8_t usbfs_active = 0; + + revents = usb2_ref_device(fp, &loc, 1 /* no uref */ );; + if (revents) { + return (POLLHUP); + } + fflags = fp->f_flag; + + /* figure out if the USB File System is active */ + + if (fflags & FWRITE) { + f = loc.txfifo; + if (f->fs_ep_max != 0) { + usbfs_active = 1; + } + } + if (fflags & FREAD) { + f = loc.rxfifo; + if (f->fs_ep_max != 0) { + usbfs_active = 1; + } + } + /* Figure out who needs service */ + + if ((events & (POLLOUT | POLLWRNORM)) && + (fflags & FWRITE)) { + + f = loc.txfifo; + + mtx_lock(f->priv_mtx); + + if (!usbfs_active) { + if (f->flag_iserror) { + /* we got an error */ + m = (void *)1; + } else { + if (f->queue_data == NULL) { + /* + * start write transfer, if not + * already started + */ + (f->methods->f_start_write) (f); + } + /* check if any packets are available */ + USB_IF_POLL(&f->free_q, m); + } + } else { + if (f->flag_iscomplete) { + m = (void *)1; + } else { + m = NULL; + } + } + + if (m) { + revents |= events & (POLLOUT | POLLWRNORM); + } else { + f->flag_isselect = 1; + selrecord(td, &f->selinfo); + } + + mtx_unlock(f->priv_mtx); + } + if ((events & (POLLIN | POLLRDNORM)) && + (fflags & FREAD)) { + + f = loc.rxfifo; + + mtx_lock(f->priv_mtx); + + if (!usbfs_active) { + if (f->flag_iserror) { + /* we have and error */ + m = (void *)1; + } else { + if (f->queue_data == NULL) { + /* + * start read transfer, if not + * already started + */ + (f->methods->f_start_read) (f); + } + /* check if any packets are available */ + USB_IF_POLL(&f->used_q, m); + } + } else { + if (f->flag_iscomplete) { + m = (void *)1; + } else { + m = NULL; + } + } + + if (m) { + revents |= events & (POLLIN | POLLRDNORM); + } else { + f->flag_isselect = 1; + selrecord(td, &f->selinfo); + + /* start reading data */ + (f->methods->f_start_read) (f); + } + + mtx_unlock(f->priv_mtx); + } + usb2_unref_device(&loc); + return (revents); +} + +/* ARGSUSED */ +static int +usb2_read_f(struct file *fp, struct uio *uio, struct ucred *cred, + int flags, struct thread *td) +{ + struct usb2_location loc; + struct usb2_fifo *f; + struct usb2_mbuf *m; + int fflags; + int resid; + int io_len; + int err; + uint8_t tr_data = 0; + + DPRINTFN(2, "\n"); + + fflags = fp->f_flag & (O_NONBLOCK | O_DIRECT | FREAD | FWRITE); + if (fflags & O_DIRECT) + fflags |= IO_DIRECT; + + err = usb2_ref_device(fp, &loc, 1 /* no uref */ ); + if (err) { + return (ENXIO); + } + f = loc.rxfifo; + if (f == NULL) { + /* should not happen */ + return (EPERM); + } + resid = uio->uio_resid; + + if ((flags & FOF_OFFSET) == 0) + uio->uio_offset = fp->f_offset; + + mtx_lock(f->priv_mtx); + + if (f->flag_iserror) { + err = EIO; + goto done; + } + while (uio->uio_resid > 0) { + + if (f->fs_ep_max == 0) { + USB_IF_DEQUEUE(&f->used_q, m); + } else { + /* + * The queue is used for events that should be + * retrieved using the "USB_FS_COMPLETE" + * ioctl. + */ + m = NULL; + } + + if (m == NULL) { + + /* start read transfer, if not already started */ + + (f->methods->f_start_read) (f); + + if (fflags & O_NONBLOCK) { + if (tr_data) { + /* return length before error */ + break; + } + err = EWOULDBLOCK; + break; + } + DPRINTF("sleeping\n"); + + err = usb2_fifo_wait(f); + if (err) { + break; + } + continue; + } else { + tr_data = 1; + } + + io_len = MIN(m->cur_data_len, uio->uio_resid); + + DPRINTFN(2, "transfer %d bytes from %p\n", + io_len, m->cur_data_ptr); + + err = usb2_fifo_uiomove(f, + m->cur_data_ptr, io_len, uio); + + m->cur_data_len -= io_len; + m->cur_data_ptr += io_len; + + if (m->cur_data_len == 0) { + + uint8_t last_packet; + + last_packet = m->last_packet; + + USB_IF_ENQUEUE(&f->free_q, m); + + if (last_packet) { + /* keep framing */ + break; + } + } else { + USB_IF_PREPEND(&f->used_q, m); + } + + if (err) { + break; + } + } +done: + mtx_unlock(f->priv_mtx); + + usb2_unref_device(&loc); + + if ((flags & FOF_OFFSET) == 0) + fp->f_offset = uio->uio_offset; + fp->f_nextoff = uio->uio_offset; + return (err); +} + +static int +usb2_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread *td) +{ + return (USB_VNOPS_FO_STAT(fp, sb, cred, td)); +} + +#if __FreeBSD_version > 800009 +static int +usb2_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td) +{ + return (USB_VNOPS_FO_TRUNCATE(fp, length, cred, td)); +} + +#endif + +/* ARGSUSED */ +static int +usb2_write_f(struct file *fp, struct uio *uio, struct ucred *cred, + int flags, struct thread *td) +{ + struct usb2_location loc; + struct usb2_fifo *f; + struct usb2_mbuf *m; + int fflags; + int resid; + int io_len; + int err; + uint8_t tr_data = 0; + + DPRINTFN(2, "\n"); + + fflags = fp->f_flag & (O_NONBLOCK | O_DIRECT | + FREAD | FWRITE | O_FSYNC); + if (fflags & O_DIRECT) + fflags |= IO_DIRECT; + + err = usb2_ref_device(fp, &loc, 1 /* no uref */ ); + if (err) { + return (ENXIO); + } + f = loc.txfifo; + if (f == NULL) { + /* should not happen */ + usb2_unref_device(&loc); + return (EPERM); + } + resid = uio->uio_resid; + + if ((flags & FOF_OFFSET) == 0) + uio->uio_offset = fp->f_offset; + + mtx_lock(f->priv_mtx); + + if (f->flag_iserror) { + err = EIO; + goto done; + } + if ((f->queue_data == NULL) && (f->fs_ep_max == 0)) { + /* start write transfer, if not already started */ + (f->methods->f_start_write) (f); + } + /* we allow writing zero length data */ + do { + if (f->fs_ep_max == 0) { + USB_IF_DEQUEUE(&f->free_q, m); + } else { + /* + * The queue is used for events that should be + * retrieved using the "USB_FS_COMPLETE" + * ioctl. + */ + m = NULL; + } + + if (m == NULL) { + + if (fflags & O_NONBLOCK) { + if (tr_data) { + /* return length before error */ + break; + } + err = EWOULDBLOCK; + break; + } + DPRINTF("sleeping\n"); + + err = usb2_fifo_wait(f); + if (err) { + break; + } + continue; + } else { + tr_data = 1; + } + + USB_MBUF_RESET(m); + + io_len = MIN(m->cur_data_len, uio->uio_resid); + + m->cur_data_len = io_len; + + DPRINTFN(2, "transfer %d bytes to %p\n", + io_len, m->cur_data_ptr); + + err = usb2_fifo_uiomove(f, + m->cur_data_ptr, io_len, uio); + + if (err) { + USB_IF_ENQUEUE(&f->free_q, m); + break; + } else { + USB_IF_ENQUEUE(&f->used_q, m); + (f->methods->f_start_write) (f); + } + } while (uio->uio_resid > 0); +done: + mtx_unlock(f->priv_mtx); + + usb2_unref_device(&loc); + + if ((flags & FOF_OFFSET) == 0) + fp->f_offset = uio->uio_offset; + fp->f_nextoff = uio->uio_offset; + + return (err); +} + +static int +usb2_fifo_uiomove(struct usb2_fifo *f, void *cp, + int n, struct uio *uio) +{ + int error; + + mtx_unlock(f->priv_mtx); + + /* + * "uiomove()" can sleep so one needs to make a wrapper, + * exiting the mutex and checking things: + */ + error = uiomove(cp, n, uio); + + mtx_lock(f->priv_mtx); + + return (error); +} + +int +usb2_fifo_wait(struct usb2_fifo *f) +{ + int err; + + mtx_assert(f->priv_mtx, MA_OWNED); + + if (f->flag_iserror) { + /* we are gone */ + return (EIO); + } + f->flag_sleeping = 1; + + err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx); + + if (f->flag_iserror) { + /* we are gone */ + err = EIO; + } + return (err); +} + +void +usb2_fifo_signal(struct usb2_fifo *f) +{ + if (f->flag_sleeping) { + f->flag_sleeping = 0; + usb2_cv_broadcast(&f->cv_io); + } + return; +} + +void +usb2_fifo_wakeup(struct usb2_fifo *f) +{ + usb2_fifo_signal(f); + + if (f->flag_isselect) { + selwakeup(&f->selinfo); + f->flag_isselect = 0; + } + if (f->async_p != NULL) { + PROC_LOCK(f->async_p); + psignal(f->async_p, SIGIO); + PROC_UNLOCK(f->async_p); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_fifo_opened + * + * Returns: + * 0: FIFO not opened. + * Else: FIFO is opened. + *------------------------------------------------------------------------*/ +uint8_t +usb2_fifo_opened(struct usb2_fifo *f) +{ + uint8_t temp; + uint8_t do_unlock; + + if (f == NULL) { + return (0); /* be NULL safe */ + } + if (mtx_owned(f->priv_mtx)) { + do_unlock = 0; + } else { + do_unlock = 1; + mtx_lock(f->priv_mtx); + } + temp = f->curr_file ? 1 : 0; + if (do_unlock) { + mtx_unlock(f->priv_mtx); + } + return (temp); +} + + +static int +usb2_fifo_dummy_open(struct usb2_fifo *fifo, + int fflags, struct thread *td) +{ + return (0); +} + +static void +usb2_fifo_dummy_close(struct usb2_fifo *fifo, + int fflags, struct thread *td) +{ + return; +} + +static int +usb2_fifo_dummy_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, + int fflags, struct thread *td) +{ + return (ENOTTY); +} + +static void +usb2_fifo_dummy_cmd(struct usb2_fifo *fifo) +{ + fifo->flag_flushing = 0; /* not flushing */ + return; +} + +static void +usb2_fifo_check_methods(struct usb2_fifo_methods *pm) +{ + /* check that all callback functions are OK */ + + if (pm->f_open == NULL) + pm->f_open = &usb2_fifo_dummy_open; + + if (pm->f_close == NULL) + pm->f_close = &usb2_fifo_dummy_close; + + if (pm->f_ioctl == NULL) + pm->f_ioctl = &usb2_fifo_dummy_ioctl; + + if (pm->f_start_read == NULL) + pm->f_start_read = &usb2_fifo_dummy_cmd; + + if (pm->f_stop_read == NULL) + pm->f_stop_read = &usb2_fifo_dummy_cmd; + + if (pm->f_start_write == NULL) + pm->f_start_write = &usb2_fifo_dummy_cmd; + + if (pm->f_stop_write == NULL) + pm->f_stop_write = &usb2_fifo_dummy_cmd; + + return; +} + +/*------------------------------------------------------------------------* + * usb2_fifo_attach + * + * The following function will create a duplex FIFO. + * + * Return values: + * 0: Success. + * Else: Failure. + *------------------------------------------------------------------------*/ +int +usb2_fifo_attach(struct usb2_device *udev, void *priv_sc, + struct mtx *priv_mtx, struct usb2_fifo_methods *pm, + struct usb2_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, + uint8_t iface_index) +{ + struct usb2_fifo *f_tx; + struct usb2_fifo *f_rx; + char buf[32]; + char src[32]; + uint8_t n; + + f_sc->fp[USB_FIFO_TX] = NULL; + f_sc->fp[USB_FIFO_RX] = NULL; + + if (pm == NULL) + return (EINVAL); + + /* check the methods */ + usb2_fifo_check_methods(pm); + + if (priv_mtx == NULL) + priv_mtx = &Giant; + + /* search for a free FIFO slot */ + for (n = 0;; n += 2) { + + if (n == USB_FIFO_MAX) { + /* end of FIFOs reached */ + return (ENOMEM); + } + /* Check for TX FIFO */ + if (udev->fifo[n + USB_FIFO_TX] != NULL) { + continue; + } + /* Check for RX FIFO */ + if (udev->fifo[n + USB_FIFO_RX] != NULL) { + continue; + } + break; + } + + f_tx = usb2_fifo_alloc(); + f_rx = usb2_fifo_alloc(); + + if ((f_tx == NULL) || (f_rx == NULL)) { + usb2_fifo_free(f_tx); + usb2_fifo_free(f_rx); + return (ENOMEM); + } + /* initialise FIFO structures */ + + f_tx->fifo_index = n + USB_FIFO_TX; + f_tx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2); + f_tx->priv_mtx = priv_mtx; + f_tx->priv_sc0 = priv_sc; + f_tx->methods = pm; + f_tx->iface_index = iface_index; + f_tx->udev = udev; + f_tx->flag_no_uref = 1; + + f_rx->fifo_index = n + USB_FIFO_RX; + f_rx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2); + f_rx->priv_mtx = priv_mtx; + f_rx->priv_sc0 = priv_sc; + f_rx->methods = pm; + f_rx->iface_index = iface_index; + f_rx->udev = udev; + f_rx->flag_no_uref = 1; + + f_sc->fp[USB_FIFO_TX] = f_tx; + f_sc->fp[USB_FIFO_RX] = f_rx; + + mtx_lock(&usb2_ref_lock); + udev->fifo[f_tx->fifo_index] = f_tx; + udev->fifo[f_rx->fifo_index] = f_rx; + mtx_unlock(&usb2_ref_lock); + + if (snprintf(src, sizeof(src), + USB_DEVICE_NAME "%u.%u.%u.%u", + device_get_unit(udev->bus->bdev), + udev->device_index, + iface_index, + f_tx->dev_ep_index)) { + /* ignore */ + } + for (n = 0; n != 4; n++) { + + if (pm->basename[n] == NULL) { + continue; + } + if (subunit == 0xFFFF) { + if (snprintf(buf, sizeof(buf), + "%s%u%s", pm->basename[n], + unit, pm->postfix[n] ? + pm->postfix[n] : "")) { + /* ignore */ + } + } else { + if (snprintf(buf, sizeof(buf), + "%s%u.%u%s", pm->basename[n], + unit, subunit, pm->postfix[n] ? + pm->postfix[n] : "")) { + /* ignore */ + } + } + + /* + * Distribute the symbolic links into two FIFO structures: + */ + if (n & 1) { + f_rx->symlink[n / 2] = + usb2_alloc_symlink(src, "%s", buf); + } else { + f_tx->symlink[n / 2] = + usb2_alloc_symlink(src, "%s", buf); + } + printf("Symlink: %s -> %s\n", buf, src); + } + + DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_fifo_alloc_buffer + * + * Return values: + * 0: Success + * Else failure + *------------------------------------------------------------------------*/ +int +usb2_fifo_alloc_buffer(struct usb2_fifo *f, uint32_t bufsize, + uint16_t nbuf) +{ + usb2_fifo_free_buffer(f); + + /* allocate an endpoint */ + f->free_q.ifq_maxlen = nbuf; + f->used_q.ifq_maxlen = nbuf; + + f->queue_data = usb2_alloc_mbufs( + M_USBDEV, &f->free_q, bufsize, nbuf); + + if ((f->queue_data == NULL) && bufsize && nbuf) { + return (ENOMEM); + } + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_fifo_free_buffer + * + * This function will free the buffers associated with a FIFO. This + * function can be called multiple times in a row. + *------------------------------------------------------------------------*/ +void +usb2_fifo_free_buffer(struct usb2_fifo *f) +{ + if (f->queue_data) { + /* free old buffer */ + free(f->queue_data, M_USBDEV); + f->queue_data = NULL; + } + /* reset queues */ + + bzero(&f->free_q, sizeof(f->free_q)); + bzero(&f->used_q, sizeof(f->used_q)); + return; +} + +void +usb2_fifo_detach(struct usb2_fifo_sc *f_sc) +{ + if (f_sc == NULL) { + return; + } + usb2_fifo_free(f_sc->fp[USB_FIFO_TX]); + usb2_fifo_free(f_sc->fp[USB_FIFO_RX]); + + f_sc->fp[USB_FIFO_TX] = NULL; + f_sc->fp[USB_FIFO_RX] = NULL; + + DPRINTFN(2, "detached %p\n", f_sc); + + return; +} + +uint32_t +usb2_fifo_put_bytes_max(struct usb2_fifo *f) +{ + struct usb2_mbuf *m; + uint32_t len; + + USB_IF_POLL(&f->free_q, m); + + if (m) { + len = m->max_data_len; + } else { + len = 0; + } + return (len); +} + +/*------------------------------------------------------------------------* + * usb2_fifo_put_data + * + * what: + * 0 - normal operation + * 1 - set last packet flag to enforce framing + *------------------------------------------------------------------------*/ +void +usb2_fifo_put_data(struct usb2_fifo *f, struct usb2_page_cache *pc, + uint32_t offset, uint32_t len, uint8_t what) +{ + struct usb2_mbuf *m; + uint32_t io_len; + + while (len || (what == 1)) { + + USB_IF_DEQUEUE(&f->free_q, m); + + if (m) { + USB_MBUF_RESET(m); + + io_len = MIN(len, m->cur_data_len); + + usb2_copy_out(pc, offset, m->cur_data_ptr, io_len); + + m->cur_data_len = io_len; + offset += io_len; + len -= io_len; + + if ((len == 0) && (what == 1)) { + m->last_packet = 1; + } + USB_IF_ENQUEUE(&f->used_q, m); + + usb2_fifo_wakeup(f); + + if ((len == 0) || (what == 1)) { + break; + } + } else { + break; + } + } + return; +} + +void +usb2_fifo_put_data_linear(struct usb2_fifo *f, void *ptr, + uint32_t len, uint8_t what) +{ + struct usb2_mbuf *m; + uint32_t io_len; + + while (len || (what == 1)) { + + USB_IF_DEQUEUE(&f->free_q, m); + + if (m) { + USB_MBUF_RESET(m); + + io_len = MIN(len, m->cur_data_len); + + bcopy(ptr, m->cur_data_ptr, io_len); + + m->cur_data_len = io_len; + ptr = USB_ADD_BYTES(ptr, io_len); + len -= io_len; + + if ((len == 0) && (what == 1)) { + m->last_packet = 1; + } + USB_IF_ENQUEUE(&f->used_q, m); + + usb2_fifo_wakeup(f); + + if ((len == 0) || (what == 1)) { + break; + } + } else { + break; + } + } + return; +} + +uint8_t +usb2_fifo_put_data_buffer(struct usb2_fifo *f, void *ptr, uint32_t len) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&f->free_q, m); + + if (m) { + m->cur_data_len = len; + m->cur_data_ptr = ptr; + USB_IF_ENQUEUE(&f->used_q, m); + usb2_fifo_wakeup(f); + return (1); + } + return (0); +} + +void +usb2_fifo_put_data_error(struct usb2_fifo *f) +{ + f->flag_iserror = 1; + usb2_fifo_wakeup(f); + return; +} + +/*------------------------------------------------------------------------* + * usb2_fifo_get_data + * + * what: + * 0 - normal operation + * 1 - only get one "usb2_mbuf" + * + * returns: + * 0 - no more data + * 1 - data in buffer + *------------------------------------------------------------------------*/ +uint8_t +usb2_fifo_get_data(struct usb2_fifo *f, struct usb2_page_cache *pc, + uint32_t offset, uint32_t len, uint32_t *actlen, + uint8_t what) +{ + struct usb2_mbuf *m; + uint32_t io_len; + uint8_t tr_data = 0; + + actlen[0] = 0; + + while (1) { + + USB_IF_DEQUEUE(&f->used_q, m); + + if (m) { + + tr_data = 1; + + io_len = MIN(len, m->cur_data_len); + + usb2_copy_in(pc, offset, m->cur_data_ptr, io_len); + + len -= io_len; + offset += io_len; + actlen[0] += io_len; + m->cur_data_ptr += io_len; + m->cur_data_len -= io_len; + + if ((m->cur_data_len == 0) || (what == 1)) { + USB_IF_ENQUEUE(&f->free_q, m); + + usb2_fifo_wakeup(f); + + if (what == 1) { + break; + } + } else { + USB_IF_PREPEND(&f->used_q, m); + } + } else { + + if (tr_data) { + /* wait for data to be written out */ + break; + } + if (f->flag_flushing) { + f->flag_flushing = 0; + usb2_fifo_wakeup(f); + } + break; + } + if (len == 0) { + break; + } + } + return (tr_data); +} + +uint8_t +usb2_fifo_get_data_linear(struct usb2_fifo *f, void *ptr, + uint32_t len, uint32_t *actlen, uint8_t what) +{ + struct usb2_mbuf *m; + uint32_t io_len; + uint8_t tr_data = 0; + + actlen[0] = 0; + + while (1) { + + USB_IF_DEQUEUE(&f->used_q, m); + + if (m) { + + tr_data = 1; + + io_len = MIN(len, m->cur_data_len); + + bcopy(m->cur_data_ptr, ptr, io_len); + + len -= io_len; + ptr = USB_ADD_BYTES(ptr, io_len); + actlen[0] += io_len; + m->cur_data_ptr += io_len; + m->cur_data_len -= io_len; + + if ((m->cur_data_len == 0) || (what == 1)) { + USB_IF_ENQUEUE(&f->free_q, m); + + usb2_fifo_wakeup(f); + + if (what == 1) { + break; + } + } else { + USB_IF_PREPEND(&f->used_q, m); + } + } else { + + if (tr_data) { + /* wait for data to be written out */ + break; + } + if (f->flag_flushing) { + f->flag_flushing = 0; + usb2_fifo_wakeup(f); + } + break; + } + if (len == 0) { + break; + } + } + return (tr_data); +} + +uint8_t +usb2_fifo_get_data_buffer(struct usb2_fifo *f, void **pptr, uint32_t *plen) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&f->used_q, m); + + if (m) { + *plen = m->cur_data_len; + *pptr = m->cur_data_ptr; + + USB_IF_PREPEND(&f->used_q, m); + return (1); + } + return (0); +} + +void +usb2_fifo_get_data_next(struct usb2_fifo *f) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&f->used_q, m); + + if (m) { + USB_IF_ENQUEUE(&f->free_q, m); + usb2_fifo_wakeup(f); + } + return; +} + +void +usb2_fifo_get_data_error(struct usb2_fifo *f) +{ + f->flag_iserror = 1; + usb2_fifo_wakeup(f); + return; +} + +/*------------------------------------------------------------------------* + * usb2_alloc_symlink + * + * Return values: + * NULL: Failure + * Else: Pointer to symlink entry + *------------------------------------------------------------------------*/ +struct usb2_symlink * +usb2_alloc_symlink(const char *target, const char *fmt,...) +{ + struct usb2_symlink *ps; + va_list ap; + + ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); + if (ps == NULL) { + return (ps); + } + strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); + ps->dst_len = strlen(ps->dst_path); + + va_start(ap, fmt); + vsnrprintf(ps->src_path, + sizeof(ps->src_path), 32, fmt, ap); + va_end(ap); + ps->src_len = strlen(ps->src_path); + + sx_xlock(&usb2_sym_lock); + TAILQ_INSERT_TAIL(&usb2_sym_head, ps, sym_entry); + sx_unlock(&usb2_sym_lock); + return (ps); +} + +/*------------------------------------------------------------------------* + * usb2_free_symlink + *------------------------------------------------------------------------*/ +void +usb2_free_symlink(struct usb2_symlink *ps) +{ + if (ps == NULL) { + return; + } + sx_xlock(&usb2_sym_lock); + TAILQ_REMOVE(&usb2_sym_head, ps, sym_entry); + sx_unlock(&usb2_sym_lock); + + free(ps, M_USBDEV); + return; +} + +/*------------------------------------------------------------------------* + * usb2_lookup_symlink + * + * Return value: + * Numerical device location + *------------------------------------------------------------------------*/ +uint32_t +usb2_lookup_symlink(const char *src_ptr, uint8_t src_len) +{ + enum { + USB_DNAME_LEN = sizeof(USB_DEVICE_NAME) - 1, + }; + struct usb2_symlink *ps; + uint32_t temp; + + sx_xlock(&usb2_sym_lock); + + TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) { + + if (src_len != ps->src_len) + continue; + + if (memcmp(ps->src_path, src_ptr, src_len)) + continue; + + if (USB_DNAME_LEN > ps->dst_len) + continue; + + if (memcmp(ps->dst_path, USB_DEVICE_NAME, USB_DNAME_LEN)) + continue; + + temp = usb2_path_convert(ps->dst_path + USB_DNAME_LEN); + sx_unlock(&usb2_sym_lock); + + return (temp); + } + sx_unlock(&usb2_sym_lock); + return (0 - 1); +} + +/*------------------------------------------------------------------------* + * usb2_read_symlink + * + * Return value: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +int +usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) +{ + struct usb2_symlink *ps; + uint32_t temp; + uint32_t delta = 0; + uint8_t len; + int error = 0; + + sx_xlock(&usb2_sym_lock); + + TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) { + + /* + * Compute total length of source and destination symlink + * strings pluss one length byte and two NUL bytes: + */ + temp = ps->src_len + ps->dst_len + 3; + + if (temp > 255) { + /* + * Skip entry because this length cannot fit + * into one byte: + */ + continue; + } + if (startentry != 0) { + /* decrement read offset */ + startentry--; + continue; + } + if (temp > user_len) { + /* out of buffer space */ + break; + } + len = temp; + + /* copy out total length */ + + error = copyout(&len, + USB_ADD_BYTES(user_ptr, delta), 1); + if (error) { + break; + } + delta += 1; + + /* copy out source string */ + + error = copyout(ps->src_path, + USB_ADD_BYTES(user_ptr, delta), ps->src_len); + if (error) { + break; + } + len = 0; + delta += ps->src_len; + error = copyout(&len, + USB_ADD_BYTES(user_ptr, delta), 1); + if (error) { + break; + } + delta += 1; + + /* copy out destination string */ + + error = copyout(ps->dst_path, + USB_ADD_BYTES(user_ptr, delta), ps->dst_len); + if (error) { + break; + } + len = 0; + delta += ps->dst_len; + error = copyout(&len, + USB_ADD_BYTES(user_ptr, delta), 1); + if (error) { + break; + } + delta += 1; + + user_len -= temp; + } + + /* a zero length entry indicates the end */ + + if ((user_len != 0) && (error == 0)) { + + len = 0; + + error = copyout(&len, + USB_ADD_BYTES(user_ptr, delta), 1); + } + sx_unlock(&usb2_sym_lock); + return (error); +} diff --git a/sys/dev/usb2/core/usb2_dev.h b/sys/dev/usb2/core/usb2_dev.h new file mode 100644 index 000000000000..859dd4ebb728 --- /dev/null +++ b/sys/dev/usb2/core/usb2_dev.h @@ -0,0 +1,149 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_DEV_H_ +#define _USB2_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define USB_FIFO_TX 0 +#define USB_FIFO_RX 1 + +struct usb2_fifo; + +typedef int (usb2_fifo_open_t)(struct usb2_fifo *fifo, int fflags, struct thread *td); +typedef void (usb2_fifo_close_t)(struct usb2_fifo *fifo, int fflags, struct thread *td); +typedef int (usb2_fifo_ioctl_t)(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags, struct thread *td); +typedef void (usb2_fifo_cmd_t)(struct usb2_fifo *fifo); + +struct usb2_symlink { + TAILQ_ENTRY(usb2_symlink) sym_entry; + char src_path[32]; /* Source path - including terminating + * zero */ + char dst_path[32]; /* Destination path - including + * terminating zero */ + uint8_t src_len; /* String length */ + uint8_t dst_len; /* String length */ +}; + +/* + * Locking note for the following functions. All the + * "usb2_fifo_cmd_t" functions are called locked. The others are + * called unlocked. + */ +struct usb2_fifo_methods { + usb2_fifo_open_t *f_open; + usb2_fifo_close_t *f_close; + usb2_fifo_ioctl_t *f_ioctl; + usb2_fifo_cmd_t *f_start_read; + usb2_fifo_cmd_t *f_stop_read; + usb2_fifo_cmd_t *f_start_write; + usb2_fifo_cmd_t *f_stop_write; + const char *basename[4]; + const char *postfix[4]; +}; + +/* + * Most of the fields in the "usb2_fifo" structure are used by the + * generic USB access layer. + */ +struct usb2_fifo { + struct usb2_ifqueue free_q; + struct usb2_ifqueue used_q; + struct selinfo selinfo; + struct cv cv_io; + struct cv cv_drain; + struct usb2_fifo_methods *methods; + struct usb2_symlink *symlink[2];/* our symlinks */ + struct proc *async_p; /* process that wants SIGIO */ + struct usb2_fs_endpoint *fs_ep_ptr; + struct usb2_device *udev; + struct usb2_xfer *xfer[2]; + struct usb2_xfer **fs_xfer; + struct mtx *priv_mtx; /* client data */ + struct file *curr_file; /* set if FIFO is opened by a FILE */ + void *priv_sc0; /* client data */ + void *priv_sc1; /* client data */ + void *queue_data; + uint32_t timeout; /* timeout in milliseconds */ + uint32_t bufsize; /* BULK and INTERRUPT buffer size */ + uint16_t nframes; /* for isochronous mode */ + uint16_t dev_ep_index; /* our device endpoint index */ + uint8_t flag_no_uref; /* set if FIFO is not control endpoint */ + uint8_t flag_sleeping; /* set if FIFO is sleeping */ + uint8_t flag_iscomplete; /* set if a USB transfer is complete */ + uint8_t flag_iserror; /* set if FIFO error happened */ + uint8_t flag_isselect; /* set if FIFO is selected */ + uint8_t flag_flushing; /* set if FIFO is flushing data */ + uint8_t flag_short; /* set if short_ok or force_short + * transfer flags should be set */ + uint8_t flag_stall; /* set if clear stall should be run */ + uint8_t iface_index; /* set to the interface we belong to */ + uint8_t fifo_index; /* set to the FIFO index in "struct + * usb2_device" */ + uint8_t fs_ep_max; + uint8_t fifo_zlp; /* zero length packet count */ + uint8_t refcount; +#define USB_FIFO_REF_MAX 0xFF +}; + +struct usb2_fifo_sc { + struct usb2_fifo *fp[2]; +}; + +int usb2_fifo_wait(struct usb2_fifo *fifo); +void usb2_fifo_signal(struct usb2_fifo *fifo); +int usb2_fifo_alloc_buffer(struct usb2_fifo *f, uint32_t bufsize, uint16_t nbuf); +void usb2_fifo_free_buffer(struct usb2_fifo *f); +int usb2_fifo_attach(struct usb2_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb2_fifo_methods *pm, struct usb2_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, uint8_t iface_index); +void usb2_fifo_detach(struct usb2_fifo_sc *f_sc); +uint32_t usb2_fifo_put_bytes_max(struct usb2_fifo *fifo); +void usb2_fifo_put_data(struct usb2_fifo *fifo, struct usb2_page_cache *pc, uint32_t offset, uint32_t len, uint8_t what); +void usb2_fifo_put_data_linear(struct usb2_fifo *fifo, void *ptr, uint32_t len, uint8_t what); +uint8_t usb2_fifo_put_data_buffer(struct usb2_fifo *f, void *ptr, uint32_t len); +void usb2_fifo_put_data_error(struct usb2_fifo *fifo); +uint8_t usb2_fifo_get_data(struct usb2_fifo *fifo, struct usb2_page_cache *pc, uint32_t offset, uint32_t len, uint32_t *actlen, uint8_t what); +uint8_t usb2_fifo_get_data_linear(struct usb2_fifo *fifo, void *ptr, uint32_t len, uint32_t *actlen, uint8_t what); +uint8_t usb2_fifo_get_data_buffer(struct usb2_fifo *f, void **pptr, uint32_t *plen); +void usb2_fifo_get_data_next(struct usb2_fifo *f); +void usb2_fifo_get_data_error(struct usb2_fifo *fifo); +uint8_t usb2_fifo_opened(struct usb2_fifo *fifo); +void usb2_fifo_free(struct usb2_fifo *f); +void usb2_fifo_reset(struct usb2_fifo *f); +int usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, int fflags, uint8_t iface_index, uint8_t ep_index); +void usb2_fifo_wakeup(struct usb2_fifo *f); +struct usb2_symlink *usb2_alloc_symlink(const char *target, const char *fmt,...); +void usb2_free_symlink(struct usb2_symlink *ps); +uint32_t usb2_lookup_symlink(const char *src_ptr, uint8_t src_len); +int usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len); + +#endif /* _USB2_DEV_H_ */ diff --git a/sys/dev/usb2/core/usb2_device.c b/sys/dev/usb2/core/usb2_device.c new file mode 100644 index 000000000000..934886525f55 --- /dev/null +++ b/sys/dev/usb2/core/usb2_device.c @@ -0,0 +1,2110 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* function prototypes */ + +static void usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe); +static void usb2_free_pipe_data(struct usb2_device *udev, uint8_t iface_index, uint8_t iface_mask); +static void usb2_free_iface_data(struct usb2_device *udev); +static void usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev, uint8_t free_subdev); +static uint8_t usb2_probe_and_attach_sub(struct usb2_device *udev, struct usb2_attach_arg *uaa); +static void usb2_init_attach_arg(struct usb2_device *udev, struct usb2_attach_arg *uaa); +static void usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspend); +static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm); +static void usb2_check_strings(struct usb2_device *udev); +static usb2_error_t usb2_fill_iface_data(struct usb2_device *udev, uint8_t iface_index, uint8_t alt_index); +static void usb2_notify_addq(const char *type, struct usb2_device *udev); +static void usb2_fifo_free_wrap(struct usb2_device *udev, uint8_t iface_index, uint8_t free_all); + +/* static structures */ + +static const uint8_t usb2_hub_speed_combs[USB_SPEED_MAX][USB_SPEED_MAX] = { + /* HUB *//* subdevice */ + [USB_SPEED_HIGH][USB_SPEED_HIGH] = 1, + [USB_SPEED_HIGH][USB_SPEED_FULL] = 1, + [USB_SPEED_HIGH][USB_SPEED_LOW] = 1, + [USB_SPEED_FULL][USB_SPEED_FULL] = 1, + [USB_SPEED_FULL][USB_SPEED_LOW] = 1, + [USB_SPEED_LOW][USB_SPEED_LOW] = 1, +}; + +/* This variable is global to allow easy access to it: */ + +int usb2_template = 0; + +SYSCTL_INT(_hw_usb2, OID_AUTO, template, CTLFLAG_RW, + &usb2_template, 0, "Selected USB device side template"); + + +/*------------------------------------------------------------------------* + * usb2_get_pipe_by_addr + * + * This function searches for an USB pipe by endpoint address and + * direction. + * + * Returns: + * NULL: Failure + * Else: Success + *------------------------------------------------------------------------*/ +struct usb2_pipe * +usb2_get_pipe_by_addr(struct usb2_device *udev, uint8_t ea_val) +{ + struct usb2_pipe *pipe = udev->pipes; + struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX; + enum { + EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR), + }; + + /* + * According to the USB specification not all bits are used + * for the endpoint address. Keep defined bits only: + */ + ea_val &= EA_MASK; + + /* + * Iterate accross all the USB pipes searching for a match + * based on the endpoint address: + */ + for (; pipe != pipe_end; pipe++) { + + if (pipe->edesc == NULL) { + continue; + } + /* do the mask and check the value */ + if ((pipe->edesc->bEndpointAddress & EA_MASK) == ea_val) { + goto found; + } + } + + /* + * The default pipe is always present and is checked separately: + */ + if ((udev->default_pipe.edesc) && + ((udev->default_pipe.edesc->bEndpointAddress & EA_MASK) == ea_val)) { + pipe = &udev->default_pipe; + goto found; + } + return (NULL); + +found: + return (pipe); +} + +/*------------------------------------------------------------------------* + * usb2_get_pipe + * + * This function searches for an USB pipe based on the information + * given by the passed "struct usb2_config" pointer. + * + * Return values: + * NULL: No match. + * Else: Pointer to "struct usb2_pipe". + *------------------------------------------------------------------------*/ +struct usb2_pipe * +usb2_get_pipe(struct usb2_device *udev, uint8_t iface_index, + const struct usb2_config *setup) +{ + struct usb2_pipe *pipe = udev->pipes; + struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX; + uint8_t index = setup->ep_index; + uint8_t ea_mask; + uint8_t ea_val; + uint8_t type_mask; + uint8_t type_val; + + DPRINTFN(10, "udev=%p iface_index=%d address=0x%x " + "type=0x%x dir=0x%x index=%d\n", + udev, iface_index, setup->endpoint, + setup->type, setup->direction, setup->ep_index); + + /* setup expected endpoint direction mask and value */ + + if (setup->direction == UE_DIR_ANY) { + /* match any endpoint direction */ + ea_mask = 0; + ea_val = 0; + } else { + /* match the given endpoint direction */ + ea_mask = (UE_DIR_IN | UE_DIR_OUT); + ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT)); + } + + /* setup expected endpoint address */ + + if (setup->endpoint == UE_ADDR_ANY) { + /* match any endpoint address */ + } else { + /* match the given endpoint address */ + ea_mask |= UE_ADDR; + ea_val |= (setup->endpoint & UE_ADDR); + } + + /* setup expected endpoint type */ + + if (setup->type == UE_BULK_INTR) { + /* this will match BULK and INTERRUPT endpoints */ + type_mask = 2; + type_val = 2; + } else if (setup->type == UE_TYPE_ANY) { + /* match any endpoint type */ + type_mask = 0; + type_val = 0; + } else { + /* match the given endpoint type */ + type_mask = UE_XFERTYPE; + type_val = (setup->type & UE_XFERTYPE); + } + + /* + * Iterate accross all the USB pipes searching for a match + * based on the endpoint address. Note that we are searching + * the pipes from the beginning of the "udev->pipes" array. + */ + for (; pipe != pipe_end; pipe++) { + + if ((pipe->edesc == NULL) || + (pipe->iface_index != iface_index)) { + continue; + } + /* do the masks and check the values */ + + if (((pipe->edesc->bEndpointAddress & ea_mask) == ea_val) && + ((pipe->edesc->bmAttributes & type_mask) == type_val)) { + if (!index--) { + goto found; + } + } + } + + /* + * Match against default pipe last, so that "any pipe", "any + * address" and "any direction" returns the first pipe of the + * interface. "iface_index" and "direction" is ignored: + */ + if ((udev->default_pipe.edesc) && + ((udev->default_pipe.edesc->bEndpointAddress & ea_mask) == ea_val) && + ((udev->default_pipe.edesc->bmAttributes & type_mask) == type_val) && + (!index)) { + pipe = &udev->default_pipe; + goto found; + } + return (NULL); + +found: + return (pipe); +} + +/*------------------------------------------------------------------------* + * usb2_interface_count + * + * This function stores the number of USB interfaces excluding + * alternate settings, which the USB config descriptor reports into + * the unsigned 8-bit integer pointed to by "count". + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_interface_count(struct usb2_device *udev, uint8_t *count) +{ + if (udev->cdesc == NULL) { + *count = 0; + return (USB_ERR_NOT_CONFIGURED); + } + *count = udev->cdesc->bNumInterface; + return (USB_ERR_NORMAL_COMPLETION); +} + + +/*------------------------------------------------------------------------* + * usb2_fill_pipe_data + * + * This function will initialise the USB pipe structure pointed to by + * the "pipe" argument. + *------------------------------------------------------------------------*/ +static void +usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index, + struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) +{ + bzero(pipe, sizeof(*pipe)); + + (udev->bus->methods->pipe_init) (udev, edesc, pipe); + + if (pipe->methods == NULL) { + /* the pipe is invalid: just return */ + return; + } + /* initialise USB pipe structure */ + pipe->edesc = edesc; + pipe->iface_index = iface_index; + TAILQ_INIT(&pipe->pipe_q.head); + pipe->pipe_q.command = &usb2_pipe_start; + + /* clear stall, if any */ + if (udev->bus->methods->clear_stall) { + mtx_lock(&udev->bus->mtx); + (udev->bus->methods->clear_stall) (udev, pipe); + mtx_unlock(&udev->bus->mtx); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_free_pipe_data + * + * This function will free USB pipe data for the given interface + * index. Hence we do not have any dynamic allocations we simply clear + * "pipe->edesc" to indicate that the USB pipe structure can be + * reused. The pipes belonging to the given interface should not be in + * use when this function is called and no check is performed to + * prevent this. + *------------------------------------------------------------------------*/ +static void +usb2_free_pipe_data(struct usb2_device *udev, + uint8_t iface_index, uint8_t iface_mask) +{ + struct usb2_pipe *pipe = udev->pipes; + struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX; + + while (pipe != pipe_end) { + if ((pipe->iface_index & iface_mask) == iface_index) { + /* free pipe */ + pipe->edesc = NULL; + } + pipe++; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_fill_iface_data + * + * This function will fill in interface data and allocate USB pipes + * for all the endpoints that belong to the given interface. This + * function is typically called when setting the configuration or when + * setting an alternate interface. + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_fill_iface_data(struct usb2_device *udev, + uint8_t iface_index, uint8_t alt_index) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_pipe *pipe; + struct usb2_pipe *pipe_end; + struct usb2_interface_descriptor *id; + struct usb2_endpoint_descriptor *ed = NULL; + struct usb2_descriptor *desc; + uint8_t nendpt; + + if (iface == NULL) { + return (USB_ERR_INVAL); + } + DPRINTFN(5, "iface_index=%d alt_index=%d\n", + iface_index, alt_index); + + sx_assert(udev->default_sx + 1, SA_LOCKED); + + pipe = udev->pipes; + pipe_end = udev->pipes + USB_EP_MAX; + + /* + * Check if any USB pipes on the given USB interface are in + * use: + */ + while (pipe != pipe_end) { + if ((pipe->edesc != NULL) && + (pipe->iface_index == iface_index) && + (pipe->refcount != 0)) { + return (USB_ERR_IN_USE); + } + pipe++; + } + + pipe = &udev->pipes[0]; + + id = usb2_find_idesc(udev->cdesc, iface_index, alt_index); + if (id == NULL) { + return (USB_ERR_INVAL); + } + /* + * Free old pipes after we know that an interface descriptor exists, + * if any. + */ + usb2_free_pipe_data(udev, iface_index, 0 - 1); + + /* Setup USB interface structure */ + iface->idesc = id; + iface->alt_index = alt_index; + iface->parent_iface_index = USB_IFACE_INDEX_ANY; + + nendpt = id->bNumEndpoints; + DPRINTFN(5, "found idesc nendpt=%d\n", nendpt); + + desc = (void *)id; + + while (nendpt--) { + DPRINTFN(11, "endpt=%d\n", nendpt); + + while ((desc = usb2_desc_foreach(udev->cdesc, desc))) { + if ((desc->bDescriptorType == UDESC_ENDPOINT) && + (desc->bLength >= sizeof(*ed))) { + goto found; + } + if (desc->bDescriptorType == UDESC_INTERFACE) { + break; + } + } + goto error; + +found: + ed = (void *)desc; + + /* find a free pipe */ + while (pipe != pipe_end) { + if (pipe->edesc == NULL) { + /* pipe is free */ + usb2_fill_pipe_data(udev, iface_index, ed, pipe); + break; + } + pipe++; + } + } + return (USB_ERR_NORMAL_COMPLETION); + +error: + /* passed end, or bad desc */ + DPRINTFN(0, "%s: bad descriptor(s), addr=%d!\n", + __FUNCTION__, udev->address); + + /* free old pipes if any */ + usb2_free_pipe_data(udev, iface_index, 0 - 1); + return (USB_ERR_INVAL); +} + +/*------------------------------------------------------------------------* + * usb2_free_iface_data + * + * This function will free all USB interfaces and USB pipes belonging + * to an USB device. + *------------------------------------------------------------------------*/ +static void +usb2_free_iface_data(struct usb2_device *udev) +{ + struct usb2_interface *iface = udev->ifaces; + struct usb2_interface *iface_end = udev->ifaces + USB_IFACE_MAX; + + /* mtx_assert() */ + + /* free Linux compat device, if any */ + if (udev->linux_dev) { + usb_linux_free_device(udev->linux_dev); + udev->linux_dev = NULL; + } + /* free all pipes, if any */ + usb2_free_pipe_data(udev, 0, 0); + + /* free all interfaces, if any */ + while (iface != iface_end) { + iface->idesc = NULL; + iface->alt_index = 0; + iface->parent_iface_index = USB_IFACE_INDEX_ANY; + iface->perm.mode = 0; /* disable permissions */ + iface++; + } + + /* free "cdesc" after "ifaces", if any */ + if (udev->cdesc) { + free(udev->cdesc, M_USB); + udev->cdesc = NULL; + } + /* set unconfigured state */ + udev->curr_config_no = USB_UNCONFIG_NO; + udev->curr_config_index = USB_UNCONFIG_INDEX; + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_config_index + * + * This function selects configuration by index, independent of the + * actual configuration number. This function should not be used by + * USB drivers. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_set_config_index(struct usb2_device *udev, uint8_t index) +{ + struct usb2_status ds; + struct usb2_hub_descriptor hd; + struct usb2_config_descriptor *cdp; + uint16_t power; + uint16_t max_power; + uint8_t nifc; + uint8_t selfpowered; + uint8_t do_unlock; + usb2_error_t err; + + DPRINTFN(6, "udev=%p index=%d\n", udev, index); + + /* automatic locking */ + if (sx_xlocked(udev->default_sx + 1)) { + do_unlock = 0; + } else { + do_unlock = 1; + sx_xlock(udev->default_sx + 1); + } + + /* detach all interface drivers */ + usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 1); + + /* free all FIFOs except control endpoint FIFOs */ + usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 0); + + /* free all configuration data structures */ + usb2_free_iface_data(udev); + + if (index == USB_UNCONFIG_INDEX) { + /* + * Leave unallocated when unconfiguring the + * device. "usb2_free_iface_data()" will also reset + * the current config number and index. + */ + err = usb2_req_set_config(udev, &Giant, USB_UNCONFIG_NO); + goto done; + } + /* get the full config descriptor */ + err = usb2_req_get_config_desc_full(udev, + &Giant, &cdp, M_USB, index); + if (err) { + goto done; + } + /* set the new config descriptor */ + + udev->cdesc = cdp; + + if (cdp->bNumInterface > USB_IFACE_MAX) { + DPRINTFN(0, "too many interfaces: %d\n", cdp->bNumInterface); + cdp->bNumInterface = USB_IFACE_MAX; + } + /* Figure out if the device is self or bus powered. */ + selfpowered = 0; + if ((!udev->flags.uq_bus_powered) && + (cdp->bmAttributes & UC_SELF_POWERED) && + (udev->flags.usb2_mode == USB_MODE_HOST)) { + /* May be self powered. */ + if (cdp->bmAttributes & UC_BUS_POWERED) { + /* Must ask device. */ + if (udev->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. + */ + err = usb2_req_get_hub_descriptor + (udev, &Giant, &hd, 1); + if (err) { + DPRINTFN(0, "could not read " + "HUB descriptor: %s\n", + usb2_errstr(err)); + + } else if (UGETW(hd.wHubCharacteristics) & + UHD_PWR_INDIVIDUAL) { + selfpowered = 1; + } + DPRINTF("characteristics=0x%04x\n", + UGETW(hd.wHubCharacteristics)); + } else { + err = usb2_req_get_device_status + (udev, &Giant, &ds); + if (err) { + DPRINTFN(0, "could not read " + "device status: %s\n", + usb2_errstr(err)); + } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) { + selfpowered = 1; + } + DPRINTF("status=0x%04x \n", + UGETW(ds.wStatus)); + } + } else + selfpowered = 1; + } + DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " + "selfpowered=%d, power=%d\n", + udev, cdp, + cdp->bConfigurationValue, udev->address, cdp->bmAttributes, + selfpowered, cdp->bMaxPower * 2); + + /* Check if we have enough power. */ + power = cdp->bMaxPower * 2; + + if (udev->parent_hub) { + max_power = udev->parent_hub->hub->portpower; + } else { + max_power = USB_MAX_POWER; + } + + if (power > max_power) { + DPRINTFN(0, "power exceeded %d > %d\n", power, max_power); + err = USB_ERR_NO_POWER; + goto done; + } + /* Only update "self_powered" in USB Host Mode */ + if (udev->flags.usb2_mode == USB_MODE_HOST) { + udev->flags.self_powered = selfpowered; + } + udev->power = power; + udev->curr_config_no = cdp->bConfigurationValue; + udev->curr_config_index = index; + + /* Set the actual configuration value. */ + err = usb2_req_set_config(udev, &Giant, cdp->bConfigurationValue); + if (err) { + goto done; + } + /* Allocate and fill interface data. */ + nifc = cdp->bNumInterface; + while (nifc--) { + err = usb2_fill_iface_data(udev, nifc, 0); + if (err) { + goto done; + } + } + +done: + DPRINTF("error=%s\n", usb2_errstr(err)); + if (err) { + usb2_free_iface_data(udev); + } + if (do_unlock) { + sx_unlock(udev->default_sx + 1); + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_set_alt_interface_index + * + * This function will select an alternate interface index for the + * given interface index. The interface should not be in use when this + * function is called. That means there should be no open USB + * transfers. Else an error is returned. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_set_alt_interface_index(struct usb2_device *udev, + uint8_t iface_index, uint8_t alt_index) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + usb2_error_t err; + uint8_t do_unlock; + + /* automatic locking */ + if (sx_xlocked(udev->default_sx + 1)) { + do_unlock = 0; + } else { + do_unlock = 1; + sx_xlock(udev->default_sx + 1); + } + if (iface == NULL) { + err = USB_ERR_INVAL; + goto done; + } + if (udev->flags.usb2_mode == USB_MODE_DEVICE) { + usb2_detach_device(udev, iface_index, 1); + } + /* free all FIFOs for this interface */ + usb2_fifo_free_wrap(udev, iface_index, 0); + + err = usb2_fill_iface_data(udev, iface_index, alt_index); + if (err) { + goto done; + } + err = usb2_req_set_alt_interface_no + (udev, &Giant, iface_index, + iface->idesc->bAlternateSetting); + +done: + if (do_unlock) { + sx_unlock(udev->default_sx + 1); + } + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_set_endpoint_stall + * + * This function is used to make a BULK or INTERRUPT endpoint + * send STALL tokens. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_set_endpoint_stall(struct usb2_device *udev, struct usb2_pipe *pipe, + uint8_t do_stall) +{ + struct usb2_xfer *xfer; + uint8_t et; + uint8_t was_stalled; + + if (pipe == NULL) { + /* nothing to do */ + DPRINTF("Cannot find endpoint\n"); + /* + * Pretend that the clear or set stall request is + * successful else some USB host stacks can do + * strange things, especially when a control endpoint + * stalls. + */ + return (0); + } + et = (pipe->edesc->bmAttributes & UE_XFERTYPE); + + if ((et != UE_BULK) && + (et != UE_INTERRUPT)) { + /* + * Should not stall control + * nor isochronous endpoints. + */ + DPRINTF("Invalid endpoint\n"); + return (0); + } + mtx_lock(&udev->bus->mtx); + + /* store current stall state */ + was_stalled = pipe->is_stalled; + + /* check for no change */ + if (was_stalled && do_stall) { + /* if the pipe is already stalled do nothing */ + mtx_unlock(&udev->bus->mtx); + DPRINTF("No change\n"); + return (0); + } + /* set stalled state */ + pipe->is_stalled = 1; + + if (do_stall || (!was_stalled)) { + if (!was_stalled) { + /* lookup the current USB transfer, if any */ + xfer = pipe->pipe_q.curr; + } else { + xfer = NULL; + } + + /* + * If "xfer" is non-NULL the "set_stall" method will + * complete the USB transfer like in case of a timeout + * setting the error code "USB_ERR_STALLED". + */ + (udev->bus->methods->set_stall) (udev, xfer, pipe); + } + if (!do_stall) { + pipe->toggle_next = 0; /* reset data toggle */ + pipe->is_stalled = 0; /* clear stalled state */ + + (udev->bus->methods->clear_stall) (udev, pipe); + + /* start up the current or next transfer, if any */ + usb2_command_wrapper(&pipe->pipe_q, pipe->pipe_q.curr); + } + mtx_unlock(&udev->bus->mtx); + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_reset_iface_endpoints - used in USB device side mode + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_reset_iface_endpoints(struct usb2_device *udev, uint8_t iface_index) +{ + struct usb2_pipe *pipe; + struct usb2_pipe *pipe_end; + usb2_error_t err; + + pipe = udev->pipes; + pipe_end = udev->pipes + USB_EP_MAX; + + for (; pipe != pipe_end; pipe++) { + + if ((pipe->edesc == NULL) || + (pipe->iface_index != iface_index)) { + continue; + } + /* simulate a clear stall from the peer */ + err = usb2_set_endpoint_stall(udev, pipe, 0); + if (err) { + /* just ignore */ + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_detach_device_sub + * + * This function will try to detach an USB device. If it fails a panic + * will result. + *------------------------------------------------------------------------*/ +static void +usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev, + uint8_t free_subdev) +{ + device_t dev; + int err; + + if (!free_subdev) { + + *ppdev = NULL; + + } else if (*ppdev) { + + /* + * NOTE: It is important to clear "*ppdev" before deleting + * the child due to some device methods being called late + * during the delete process ! + */ + dev = *ppdev; + *ppdev = NULL; + + device_printf(dev, "at %s, port %d, addr %d " + "(disconnected)\n", + device_get_nameunit(udev->parent_dev), + udev->port_no, udev->address); + + if (device_is_attached(dev)) { + if (udev->flags.suspended) { + err = DEVICE_RESUME(dev); + if (err) { + device_printf(dev, "Resume failed!\n"); + } + } + if (device_detach(dev)) { + goto error; + } + } + if (device_delete_child(udev->parent_dev, dev)) { + goto error; + } + } + return; + +error: + /* Detach is not allowed to fail in the USB world */ + panic("An USB driver would not detach!\n"); + return; +} + +/*------------------------------------------------------------------------* + * usb2_detach_device + * + * The following function will detach the matching interfaces. + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_detach_device(struct usb2_device *udev, uint8_t iface_index, + uint8_t free_subdev) +{ + struct usb2_interface *iface; + uint8_t i; + uint8_t do_unlock; + + if (udev == NULL) { + /* nothing to do */ + return; + } + DPRINTFN(4, "udev=%p\n", udev); + + /* automatic locking */ + if (sx_xlocked(udev->default_sx + 1)) { + do_unlock = 0; + } else { + do_unlock = 1; + sx_xlock(udev->default_sx + 1); + } + + /* + * First detach the child to give the child's detach routine a + * chance to detach the sub-devices in the correct order. + * Then delete the child using "device_delete_child()" which + * will detach all sub-devices from the bottom and upwards! + */ + if (iface_index != USB_IFACE_INDEX_ANY) { + i = iface_index; + iface_index = i + 1; + } else { + i = 0; + iface_index = USB_IFACE_MAX; + } + + /* do the detach */ + + for (; i != iface_index; i++) { + + iface = usb2_get_iface(udev, i); + if (iface == NULL) { + /* looks like the end of the USB interfaces */ + break; + } + usb2_detach_device_sub(udev, &iface->subdev, free_subdev); + } + + if (do_unlock) { + sx_unlock(udev->default_sx + 1); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_probe_and_attach_sub + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +usb2_probe_and_attach_sub(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + struct usb2_interface *iface; + device_t dev; + int err; + + iface = uaa->iface; + if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) { + /* leave interface alone */ + return (0); + } + dev = iface->subdev; + if (dev) { + + /* clean up after module unload */ + + if (device_is_attached(dev)) { + /* already a device there */ + return (0); + } + /* clear "iface->subdev" as early as possible */ + + iface->subdev = NULL; + + if (device_delete_child(udev->parent_dev, dev)) { + + /* + * Panic here, else one can get a double call + * to device_detach(). USB devices should + * never fail on detach! + */ + panic("device_delete_child() failed!\n"); + } + } + if (uaa->temp_dev == NULL) { + + /* create a new child */ + uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1); + if (uaa->temp_dev == NULL) { + device_printf(udev->parent_dev, + "Device creation failed!\n"); + return (1); /* failure */ + } + device_set_ivars(uaa->temp_dev, uaa); + device_quiet(uaa->temp_dev); + } + /* + * Set "subdev" before probe and attach so that "devd" gets + * the information it needs. + */ + iface->subdev = uaa->temp_dev; + + if (device_probe_and_attach(iface->subdev) == 0) { + /* + * The USB attach arguments are only available during probe + * and attach ! + */ + uaa->temp_dev = NULL; + device_set_ivars(iface->subdev, NULL); + + if (udev->flags.suspended) { + err = DEVICE_SUSPEND(iface->subdev); + device_printf(iface->subdev, "Suspend failed\n"); + } + return (0); /* success */ + } else { + /* No USB driver found */ + iface->subdev = NULL; + } + return (1); /* failure */ +} + +/*------------------------------------------------------------------------* + * usb2_set_parent_iface + * + * Using this function will lock the alternate interface setting on an + * interface. It is typically used for multi interface drivers. In USB + * device side mode it is assumed that the alternate interfaces all + * have the same endpoint descriptors. The default parent index value + * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not + * locked. + *------------------------------------------------------------------------*/ +void +usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index, + uint8_t parent_index) +{ + struct usb2_interface *iface; + + iface = usb2_get_iface(udev, iface_index); + if (iface) { + iface->parent_iface_index = parent_index; + } + return; +} + +static void +usb2_init_attach_arg(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + bzero(uaa, sizeof(*uaa)); + + uaa->device = udev; + uaa->usb2_mode = udev->flags.usb2_mode; + uaa->port = udev->port_no; + + uaa->info.idVendor = UGETW(udev->ddesc.idVendor); + uaa->info.idProduct = UGETW(udev->ddesc.idProduct); + uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice); + uaa->info.bDeviceClass = udev->ddesc.bDeviceClass; + uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass; + uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol; + uaa->info.bConfigIndex = udev->curr_config_index; + uaa->info.bConfigNum = udev->curr_config_no; + + return; +} + +/*------------------------------------------------------------------------* + * usb2_probe_and_attach + * + * This function is called from "uhub_explore_sub()", + * "usb2_handle_set_config()" and "usb2_handle_request()". + * + * Returns: + * 0: Success + * Else: A control transfer failed + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_probe_and_attach(struct usb2_device *udev, uint8_t iface_index) +{ + struct usb2_attach_arg uaa; + struct usb2_interface *iface; + uint8_t i; + uint8_t j; + uint8_t do_unlock; + + if (udev == NULL) { + DPRINTF("udev == NULL\n"); + return (USB_ERR_INVAL); + } + /* automatic locking */ + if (sx_xlocked(udev->default_sx + 1)) { + do_unlock = 0; + } else { + do_unlock = 1; + sx_xlock(udev->default_sx + 1); + } + + if (udev->curr_config_index == USB_UNCONFIG_INDEX) { + /* do nothing - no configuration has been set */ + goto done; + } + /* setup USB attach arguments */ + + usb2_init_attach_arg(udev, &uaa); + + /* Check if only one interface should be probed: */ + if (iface_index != USB_IFACE_INDEX_ANY) { + i = iface_index; + j = i + 1; + } else { + i = 0; + j = USB_IFACE_MAX; + } + + /* Do the probe and attach */ + for (; i != j; i++) { + + iface = usb2_get_iface(udev, i); + if (iface == NULL) { + /* + * Looks like the end of the USB + * interfaces ! + */ + DPRINTFN(2, "end of interfaces " + "at %u\n", i); + break; + } + if (iface->idesc == NULL) { + /* no interface descriptor */ + continue; + } + uaa.iface = iface; + + uaa.info.bInterfaceClass = + iface->idesc->bInterfaceClass; + uaa.info.bInterfaceSubClass = + iface->idesc->bInterfaceSubClass; + uaa.info.bInterfaceProtocol = + iface->idesc->bInterfaceProtocol; + uaa.info.bIfaceIndex = i; + uaa.info.bIfaceNum = + iface->idesc->bInterfaceNumber; + uaa.use_generic = 0; + + DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", + uaa.info.bInterfaceClass, + uaa.info.bInterfaceSubClass, + uaa.info.bInterfaceProtocol, + uaa.info.bIfaceIndex, + uaa.info.bIfaceNum); + + /* try specific interface drivers first */ + + if (usb2_probe_and_attach_sub(udev, &uaa)) { + /* ignore */ + } + /* try generic interface drivers last */ + + uaa.use_generic = 1; + + if (usb2_probe_and_attach_sub(udev, &uaa)) { + /* ignore */ + } + } + + if (uaa.temp_dev) { + /* remove the last created child; it is unused */ + + if (device_delete_child(udev->parent_dev, uaa.temp_dev)) { + DPRINTFN(0, "device delete child failed!\n"); + } + } +done: + if (do_unlock) { + sx_unlock(udev->default_sx + 1); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_suspend_resume_sub + * + * This function is called when the suspend or resume methods should + * be executed on an USB device. + *------------------------------------------------------------------------*/ +static void +usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspend) +{ + int err; + + if (dev == NULL) { + return; + } + if (!device_is_attached(dev)) { + return; + } + if (do_suspend) { + err = DEVICE_SUSPEND(dev); + } else { + err = DEVICE_RESUME(dev); + } + if (err) { + device_printf(dev, "%s failed!\n", + do_suspend ? "Suspend" : "Resume"); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_suspend_resume_device + * + * The following function will suspend or resume the USB device. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend) +{ + struct usb2_interface *iface; + uint8_t i; + + if (udev == NULL) { + /* nothing to do */ + return (0); + } + DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend); + + sx_assert(udev->default_sx + 1, SA_LOCKED); + + mtx_lock(&udev->bus->mtx); + /* filter the suspend events */ + if (udev->flags.suspended == do_suspend) { + mtx_unlock(&udev->bus->mtx); + /* nothing to do */ + return (0); + } + udev->flags.suspended = do_suspend; + mtx_unlock(&udev->bus->mtx); + + /* do the suspend or resume */ + + for (i = 0; i != USB_IFACE_MAX; i++) { + + iface = usb2_get_iface(udev, i); + if (iface == NULL) { + /* looks like the end of the USB interfaces */ + break; + } + usb2_suspend_resume_sub(udev, iface->subdev, do_suspend); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_clear_stall_proc + * + * This function performs generic USB clear stall operations. + *------------------------------------------------------------------------*/ +static void +usb2_clear_stall_proc(struct usb2_proc_msg *_pm) +{ + struct usb2_clear_stall_msg *pm = (void *)_pm; + struct usb2_device *udev = pm->udev; + + /* Change lock */ + mtx_unlock(&udev->bus->mtx); + mtx_lock(udev->default_mtx); + + /* Start clear stall callback */ + usb2_transfer_start(udev->default_xfer[1]); + + /* Change lock */ + mtx_unlock(udev->default_mtx); + mtx_lock(&udev->bus->mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_alloc_device + * + * This function allocates a new USB device. This function is 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 get strings. + * + * Return values: + * 0: Failure + * Else: Success + *------------------------------------------------------------------------*/ +struct usb2_device * +usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, + struct usb2_device *parent_hub, uint8_t depth, + uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb2_mode) +{ + struct usb2_attach_arg uaa; + struct usb2_device *udev; + struct usb2_device *adev; + struct usb2_device *hub; + uint8_t *scratch_ptr; + uint32_t scratch_size; + usb2_error_t err; + uint8_t device_index; + + DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " + "port_index=%u, port_no=%u, speed=%u, usb2_mode=%u\n", + parent_dev, bus, parent_hub, depth, port_index, port_no, + speed, usb2_mode); + + /* + * Find an unused device index. In USB Host mode this is the + * same as the device address. + * + * NOTE: Index 1 is reserved for the Root HUB. + */ + for (device_index = USB_ROOT_HUB_ADDR; device_index != + USB_MAX_DEVICES; device_index++) { + if (bus->devices[device_index] == NULL) + break; + } + + if (device_index == USB_MAX_DEVICES) { + device_printf(bus->bdev, + "No free USB device index for new device!\n"); + return (NULL); + } + if (depth > 0x10) { + device_printf(bus->bdev, + "Invalid device depth!\n"); + return (NULL); + } + udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); + if (udev == NULL) { + return (NULL); + } + /* initialise our SX-lock */ + sx_init(udev->default_sx, "0123456789ABCDEF - USB device SX lock" + depth); + + /* initialise our SX-lock */ + sx_init(udev->default_sx + 1, "0123456789ABCDEF - USB config SX lock" + depth); + + usb2_cv_init(udev->default_cv, "WCTRL"); + usb2_cv_init(udev->default_cv + 1, "UGONE"); + + /* initialise our mutex */ + mtx_init(udev->default_mtx, "USB device mutex", NULL, MTX_DEF); + + /* initialise generic clear stall */ + udev->cs_msg[0].hdr.pm_callback = &usb2_clear_stall_proc; + udev->cs_msg[0].udev = udev; + udev->cs_msg[1].hdr.pm_callback = &usb2_clear_stall_proc; + udev->cs_msg[1].udev = udev; + + /* initialise some USB device fields */ + udev->parent_hub = parent_hub; + udev->parent_dev = parent_dev; + udev->port_index = port_index; + udev->port_no = port_no; + udev->depth = depth; + udev->bus = bus; + udev->address = USB_START_ADDR; /* default value */ + udev->plugtime = (uint32_t)ticks; + udev->power_mode = USB_POWER_MODE_ON; + + /* we are not ready yet */ + udev->refcount = 1; + + /* set up default endpoint descriptor */ + udev->default_ep_desc.bLength = sizeof(udev->default_ep_desc); + udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT; + udev->default_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; + udev->default_ep_desc.bmAttributes = UE_CONTROL; + udev->default_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; + udev->default_ep_desc.wMaxPacketSize[1] = 0; + udev->default_ep_desc.bInterval = 0; + udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; + + udev->speed = speed; + udev->flags.usb2_mode = usb2_mode; + + /* check speed combination */ + + hub = udev->parent_hub; + if (hub) { + if (usb2_hub_speed_combs[hub->speed][speed] == 0) { +#if USB_DEBUG + printf("%s: the selected subdevice and HUB speed " + "combination is not supported %d/%d.\n", + __FUNCTION__, speed, hub->speed); +#endif + /* reject this combination */ + err = USB_ERR_INVAL; + goto done; + } + } + /* search for our High Speed USB HUB, if any */ + + adev = udev; + hub = udev->parent_hub; + + while (hub) { + if (hub->speed == USB_SPEED_HIGH) { + udev->hs_hub_addr = hub->address; + udev->hs_port_no = adev->port_no; + break; + } + adev = hub; + hub = hub->parent_hub; + } + + /* init the default pipe */ + usb2_fill_pipe_data(udev, 0, + &udev->default_ep_desc, + &udev->default_pipe); + + /* set device index */ + udev->device_index = device_index; + + if (udev->flags.usb2_mode == USB_MODE_HOST) { + + err = usb2_req_set_address(udev, &Giant, device_index); + + /* This is the new USB device address from now on */ + + udev->address = device_index; + + /* + * We ignore any set-address errors, hence there are + * buggy USB devices out there that actually receive + * the SETUP PID, but manage to set the address before + * the STATUS stage is ACK'ed. If the device responds + * to the subsequent get-descriptor at the new + * address, then we know that the set-address command + * was successful. + */ + if (err) { + DPRINTFN(0, "set address %d failed " + "(ignored)\n", udev->address); + } + /* allow device time to set new address */ + usb2_pause_mtx(&Giant, USB_SET_ADDRESS_SETTLE); + } else { + /* We are not self powered */ + udev->flags.self_powered = 0; + + /* Set unconfigured state */ + udev->curr_config_no = USB_UNCONFIG_NO; + udev->curr_config_index = USB_UNCONFIG_INDEX; + + /* Setup USB descriptors */ + err = (usb2_temp_setup_by_index_p) (udev, usb2_template); + if (err) { + DPRINTFN(0, "setting up USB template failed maybe the USB " + "template module has not been loaded\n"); + goto done; + } + } + + /* + * Get the first 8 bytes of the device descriptor ! + * + * NOTE: "usb2_do_request" will check the device descriptor + * next time we do a request to see if the maximum packet size + * changed! The 8 first bytes of the device descriptor + * contains the maximum packet size to use on control endpoint + * 0. If this value is different from "USB_MAX_IPACKET" a new + * USB control request will be setup! + */ + err = usb2_req_get_desc(udev, &Giant, &udev->ddesc, + USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); + if (err) { + DPRINTFN(0, "getting device descriptor " + "at addr %d failed!\n", udev->address); + goto done; + } + DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " + "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", + udev->address, UGETW(udev->ddesc.bcdUSB), + udev->ddesc.bDeviceClass, + udev->ddesc.bDeviceSubClass, + udev->ddesc.bDeviceProtocol, + udev->ddesc.bMaxPacketSize, + udev->ddesc.bLength, + udev->speed); + + /* get the full device descriptor */ + err = usb2_req_get_device_desc(udev, &Giant, &udev->ddesc); + if (err) { + DPRINTF("addr=%d, getting full desc failed\n", + udev->address); + goto done; + } + /* + * Setup temporary USB attach args so that we can figure out some + * basic quirks for this device. + */ + usb2_init_attach_arg(udev, &uaa); + + if (usb2_test_quirk(&uaa, UQ_BUS_POWERED)) { + udev->flags.uq_bus_powered = 1; + } + if (usb2_test_quirk(&uaa, UQ_POWER_CLAIM)) { + udev->flags.uq_power_claim = 1; + } + if (usb2_test_quirk(&uaa, UQ_NO_STRINGS)) { + udev->flags.no_strings = 1; + } + /* + * Workaround for buggy USB devices. + * + * It appears that some string-less USB chips will crash and + * disappear if any attempts are made to read any string + * descriptors. + * + * Try to detect such chips by checking the strings in the USB + * device descriptor. If no strings are present there we + * simply disable all USB strings. + */ + scratch_ptr = udev->bus->scratch[0].data; + scratch_size = sizeof(udev->bus->scratch[0].data); + + if (udev->ddesc.iManufacturer || + udev->ddesc.iProduct || + udev->ddesc.iSerialNumber) { + /* read out the language ID string */ + err = usb2_req_get_string_desc(udev, &Giant, + (char *)scratch_ptr, 4, scratch_size, + USB_LANGUAGE_TABLE); + } else { + err = USB_ERR_INVAL; + } + + if (err || (scratch_ptr[0] < 4)) { + udev->flags.no_strings = 1; + } else { + /* pick the first language as the default */ + udev->langid = UGETW(scratch_ptr + 2); + } + + /* assume 100mA bus powered for now. Changed when configured. */ + udev->power = USB_MIN_POWER; + + /* get serial number string */ + err = usb2_req_get_string_any + (udev, &Giant, (char *)scratch_ptr, + scratch_size, udev->ddesc.iSerialNumber); + + strlcpy(udev->serial, (char *)scratch_ptr, sizeof(udev->serial)); + + /* get manufacturer string */ + err = usb2_req_get_string_any + (udev, &Giant, (char *)scratch_ptr, + scratch_size, udev->ddesc.iManufacturer); + + strlcpy(udev->manufacturer, (char *)scratch_ptr, sizeof(udev->manufacturer)); + + /* get product string */ + err = usb2_req_get_string_any + (udev, &Giant, (char *)scratch_ptr, + scratch_size, udev->ddesc.iProduct); + + strlcpy(udev->product, (char *)scratch_ptr, sizeof(udev->product)); + + /* finish up all the strings */ + usb2_check_strings(udev); + + if (udev->flags.usb2_mode == USB_MODE_HOST) { + uint8_t config_index; + uint8_t config_quirk; + + /* + * Most USB devices should attach to config index 0 by + * default + */ + if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_0)) { + config_index = 0; + config_quirk = 1; + } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_1)) { + config_index = 1; + config_quirk = 1; + } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_2)) { + config_index = 2; + config_quirk = 1; + } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_3)) { + config_index = 3; + config_quirk = 1; + } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_4)) { + config_index = 4; + config_quirk = 1; + } else { + config_index = 0; + config_quirk = 0; + } + +repeat_set_config: + + DPRINTF("setting config %u\n", config_index); + + /* get the USB device configured */ + sx_xlock(udev->default_sx + 1); + err = usb2_set_config_index(udev, config_index); + sx_unlock(udev->default_sx + 1); + if (err) { + DPRINTFN(0, "Failure selecting " + "configuration index %u: %s, port %u, addr %u\n", + config_index, usb2_errstr(err), udev->port_no, + udev->address); + + } else if (config_quirk) { + /* user quirk selects configuration index */ + } else if ((config_index + 1) < udev->ddesc.bNumConfigurations) { + + if ((udev->cdesc->bNumInterface < 2) && + (usb2_get_no_endpoints(udev->cdesc) == 0)) { + DPRINTFN(0, "Found no endpoints " + "(trying next config)!\n"); + config_index++; + goto repeat_set_config; + } + if (config_index == 0) { + /* + * Try to figure out if we have an + * auto-install disk there: + */ + if (usb2_test_autoinstall(udev, 0) == 0) { + DPRINTFN(0, "Found possible auto-install " + "disk (trying next config)\n"); + config_index++; + goto repeat_set_config; + } + } + } else if (UGETW(udev->ddesc.idVendor) == USB_VENDOR_HUAWEI) { + if (usb2_test_huawei(udev, 0) == 0) { + DPRINTFN(0, "Found Huawei auto-install disk!\n"); + err = USB_ERR_STALLED; /* fake an error */ + } + } + } else { + err = 0; /* set success */ + } + + DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n", + udev->address, udev, udev->parent_hub); + + /* register our device - we are ready */ + usb2_bus_port_set_device(bus, parent_hub ? + parent_hub->hub->ports + port_index : NULL, udev, device_index); + + /* make a symlink for UGEN */ + if (snprintf((char *)scratch_ptr, scratch_size, + USB_DEVICE_NAME "%u.%u.0.0", + device_get_unit(udev->bus->bdev), + udev->device_index)) { + /* ignore */ + } + udev->ugen_symlink = + usb2_alloc_symlink((char *)scratch_ptr, "ugen%u.%u", + device_get_unit(udev->bus->bdev), + udev->device_index); + + printf("ugen%u.%u: <%s> at %s\n", + device_get_unit(udev->bus->bdev), + udev->device_index, udev->manufacturer, + device_get_nameunit(udev->bus->bdev)); + + usb2_notify_addq("+", udev); +done: + if (err) { + /* free device */ + usb2_free_device(udev); + udev = NULL; + } + return (udev); +} + +/*------------------------------------------------------------------------* + * usb2_free_device + * + * This function is NULL safe and will free an USB device. + *------------------------------------------------------------------------*/ +void +usb2_free_device(struct usb2_device *udev) +{ + struct usb2_bus *bus; + + if (udev == NULL) { + /* already freed */ + return; + } + DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); + + usb2_notify_addq("-", udev); + + bus = udev->bus; + + /* + * Destroy UGEN symlink, if any + */ + if (udev->ugen_symlink) { + usb2_free_symlink(udev->ugen_symlink); + udev->ugen_symlink = NULL; + } + /* + * Unregister our device first which will prevent any further + * references: + */ + usb2_bus_port_set_device(bus, udev->parent_hub ? + udev->parent_hub->hub->ports + udev->port_index : NULL, + NULL, USB_ROOT_HUB_ADDR); + + /* wait for all pending references to go away: */ + + mtx_lock(&usb2_ref_lock); + udev->refcount--; + while (udev->refcount != 0) { + usb2_cv_wait(udev->default_cv + 1, &usb2_ref_lock); + } + mtx_unlock(&usb2_ref_lock); + + if (udev->flags.usb2_mode == USB_MODE_DEVICE) { + /* stop receiving any control transfers (Device Side Mode) */ + usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX); + } + /* free all FIFOs */ + usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 1); + + /* + * Free all interface related data and FIFOs, if any. + */ + usb2_free_iface_data(udev); + + /* unsetup any leftover default USB transfers */ + usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX); + + (usb2_temp_unsetup_p) (udev); + + sx_destroy(udev->default_sx); + sx_destroy(udev->default_sx + 1); + + usb2_cv_destroy(udev->default_cv); + usb2_cv_destroy(udev->default_cv + 1); + + mtx_destroy(udev->default_mtx); + + /* free device */ + free(udev, M_USB); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_get_iface + * + * This function is the safe way to get the USB interface structure + * pointer by interface index. + * + * Return values: + * NULL: Interface not present. + * Else: Pointer to USB interface structure. + *------------------------------------------------------------------------*/ +struct usb2_interface * +usb2_get_iface(struct usb2_device *udev, uint8_t iface_index) +{ + struct usb2_interface *iface = udev->ifaces + iface_index; + + if ((iface < udev->ifaces) || + (iface_index >= USB_IFACE_MAX) || + (udev->cdesc == NULL) || + (iface_index >= udev->cdesc->bNumInterface)) { + return (NULL); + } + return (iface); +} + +/*------------------------------------------------------------------------* + * usb2_find_descriptor + * + * This function will lookup the first descriptor that matches the + * criteria given by the arguments "type" and "subtype". Descriptors + * will only be searched within the interface having the index + * "iface_index". If the "id" argument points to an USB descriptor, + * it will be skipped before the search is started. This allows + * searching for multiple descriptors using the same criteria. Else + * the search is started after the interface descriptor. + * + * Return values: + * NULL: End of descriptors + * Else: A descriptor matching the criteria + *------------------------------------------------------------------------*/ +void * +usb2_find_descriptor(struct usb2_device *udev, void *id, uint8_t iface_index, + uint8_t type, uint8_t type_mask, + uint8_t subtype, uint8_t subtype_mask) +{ + struct usb2_descriptor *desc; + struct usb2_config_descriptor *cd; + struct usb2_interface *iface; + + cd = usb2_get_config_descriptor(udev); + if (cd == NULL) { + return (NULL); + } + if (id == NULL) { + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (NULL); + } + id = usb2_get_interface_descriptor(iface); + if (id == NULL) { + return (NULL); + } + } + desc = (void *)id; + + while ((desc = usb2_desc_foreach(cd, desc))) { + + if (desc->bDescriptorType == UDESC_INTERFACE) { + break; + } + if (((desc->bDescriptorType & type_mask) == type) && + ((desc->bDescriptorSubtype & subtype_mask) == subtype)) { + return (desc); + } + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_devinfo + * + * This function will dump information from the device descriptor + * belonging to the USB device pointed to by "udev", to the string + * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes + * including the terminating zero. + *------------------------------------------------------------------------*/ +void +usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len) +{ + struct usb2_device_descriptor *udd = &udev->ddesc; + uint16_t bcdDevice; + uint16_t bcdUSB; + + bcdUSB = UGETW(udd->bcdUSB); + bcdDevice = UGETW(udd->bcdDevice); + + if (udd->bDeviceClass != 0xFF) { + snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" + "%x.%02x, addr %d", udev->manufacturer, udev->product, + udd->bDeviceClass, udd->bDeviceSubClass, + (bcdUSB >> 8), bcdUSB & 0xFF, + (bcdDevice >> 8), bcdDevice & 0xFF, + udev->address); + } else { + snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" + "%x.%02x, addr %d", udev->manufacturer, udev->product, + (bcdUSB >> 8), bcdUSB & 0xFF, + (bcdDevice >> 8), bcdDevice & 0xFF, + udev->address); + } + return; +} + +#if USB_VERBOSE +/* + * Descriptions of of known vendors and devices ("products"). + */ +struct usb_knowndev { + uint16_t vendor; + uint16_t product; + uint32_t flags; + const char *vendorname; + const char *productname; +}; + +#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ + +#include +#include +#endif /* USB_VERBOSE */ + +/*------------------------------------------------------------------------* + * usb2_check_strings + * + * This function checks the manufacturer and product strings and will + * fill in defaults for missing strings. + *------------------------------------------------------------------------*/ +static void +usb2_check_strings(struct usb2_device *udev) +{ + struct usb2_device_descriptor *udd = &udev->ddesc; + const char *vendor; + const char *product; + +#if USB_VERBOSE + const struct usb_knowndev *kdp; + +#endif + uint16_t vendor_id; + uint16_t product_id; + + usb2_trim_spaces(udev->manufacturer); + usb2_trim_spaces(udev->product); + + if (udev->manufacturer[0]) { + vendor = udev->manufacturer; + } else { + vendor = NULL; + } + + if (udev->product[0]) { + product = udev->product; + } else { + product = NULL; + } + + vendor_id = UGETW(udd->idVendor); + product_id = UGETW(udd->idProduct); + +#if USB_VERBOSE + if (vendor == NULL || product == NULL) { + + for (kdp = usb_knowndevs; + kdp->vendorname != NULL; + kdp++) { + if (kdp->vendor == vendor_id && + (kdp->product == product_id || + (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 && *vendor) { + if (udev->manufacturer != vendor) { + strlcpy(udev->manufacturer, vendor, + sizeof(udev->manufacturer)); + } + } else { + snprintf(udev->manufacturer, + sizeof(udev->manufacturer), "vendor 0x%04x", vendor_id); + } + + if (product && *product) { + if (udev->product != product) { + strlcpy(udev->product, product, + sizeof(udev->product)); + } + } else { + snprintf(udev->product, + sizeof(udev->product), "product 0x%04x", product_id); + } + return; +} + +uint8_t +usb2_get_speed(struct usb2_device *udev) +{ + return (udev->speed); +} + +struct usb2_device_descriptor * +usb2_get_device_descriptor(struct usb2_device *udev) +{ + if (udev == NULL) + return (NULL); /* be NULL safe */ + return (&udev->ddesc); +} + +struct usb2_config_descriptor * +usb2_get_config_descriptor(struct usb2_device *udev) +{ + if (udev == NULL) + return (NULL); /* be NULL safe */ + return (udev->cdesc); +} + +/*------------------------------------------------------------------------* + * usb2_test_quirk - test a device for a given quirk + * + * Return values: + * 0: The USB device does not have the given quirk. + * Else: The USB device has the given quirk. + *------------------------------------------------------------------------*/ +uint8_t +usb2_test_quirk(const struct usb2_attach_arg *uaa, uint16_t quirk) +{ + uint8_t found; + + found = (usb2_test_quirk_p) (&uaa->info, quirk); + return (found); +} + +struct usb2_interface_descriptor * +usb2_get_interface_descriptor(struct usb2_interface *iface) +{ + if (iface == NULL) + return (NULL); /* be NULL safe */ + return (iface->idesc); +} + +uint8_t +usb2_get_interface_altindex(struct usb2_interface *iface) +{ + return (iface->alt_index); +} + +uint8_t +usb2_get_bus_index(struct usb2_device *udev) +{ + return ((uint8_t)device_get_unit(udev->bus->bdev)); +} + +uint8_t +usb2_get_device_index(struct usb2_device *udev) +{ + return (udev->device_index); +} + +/*------------------------------------------------------------------------* + * usb2_notify_addq + * + * This function will generate events for dev. + *------------------------------------------------------------------------*/ +static void +usb2_notify_addq(const char *type, struct usb2_device *udev) +{ + char *data = NULL; + struct malloc_type *mt; + + mtx_lock(&malloc_mtx); + mt = malloc_desc2type("bus"); /* XXX M_BUS */ + mtx_unlock(&malloc_mtx); + if (mt == NULL) + return; + + data = malloc(512, mt, M_NOWAIT); + if (data == NULL) + return; + + /* String it all together. */ + if (udev->parent_hub) { + snprintf(data, 1024, + "%s" + "ugen%u.%u " + "vendor=0x%04x " + "product=0x%04x " + "devclass=0x%02x " + "devsubclass=0x%02x " + "sernum=\"%s\" " + "at " + "port=%u " + "on " + "ugen%u.%u\n", + type, + device_get_unit(udev->bus->bdev), + udev->device_index, + UGETW(udev->ddesc.idVendor), + UGETW(udev->ddesc.idProduct), + udev->ddesc.bDeviceClass, + udev->ddesc.bDeviceSubClass, + udev->serial, + udev->port_no, + device_get_unit(udev->bus->bdev), + udev->parent_hub->device_index); + } else { + snprintf(data, 1024, + "%s" + "ugen%u.%u " + "vendor=0x%04x " + "product=0x%04x " + "devclass=0x%02x " + "devsubclass=0x%02x " + "sernum=\"%s\" " + "at port=%u " + "on " + "%s\n", + type, + device_get_unit(udev->bus->bdev), + udev->device_index, + UGETW(udev->ddesc.idVendor), + UGETW(udev->ddesc.idProduct), + udev->ddesc.bDeviceClass, + udev->ddesc.bDeviceSubClass, + udev->serial, + udev->port_no, + device_get_nameunit(device_get_parent(udev->bus->bdev))); + } + devctl_queue_data(data); + return; +} + +/*------------------------------------------------------------------------* + * usb2_fifo_free_wrap + * + * The function will free the FIFOs. + *------------------------------------------------------------------------*/ +static void +usb2_fifo_free_wrap(struct usb2_device *udev, + uint8_t iface_index, uint8_t free_all) +{ + struct usb2_fifo *f; + struct usb2_pipe *pipe; + uint16_t i; + + /* + * Free any USB FIFOs on the given interface: + */ + for (i = 0; i != USB_FIFO_MAX; i++) { + f = udev->fifo[i]; + if (f == NULL) { + continue; + } + pipe = f->priv_sc0; + if ((pipe == &udev->default_pipe) && (free_all == 0)) { + /* don't free UGEN control endpoint yet */ + continue; + } + /* Check if the interface index matches */ + if ((iface_index == f->iface_index) || + (iface_index == USB_IFACE_INDEX_ANY)) { + usb2_fifo_free(f); + } + } + return; +} diff --git a/sys/dev/usb2/core/usb2_device.h b/sys/dev/usb2/core/usb2_device.h new file mode 100644 index 000000000000..3c5225c226d3 --- /dev/null +++ b/sys/dev/usb2/core/usb2_device.h @@ -0,0 +1,162 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_DEVICE_H_ +#define _USB2_DEVICE_H_ + +struct usb2_symlink; + +#define USB_DEFAULT_XFER_MAX 2 + +struct usb2_clear_stall_msg { + struct usb2_proc_msg hdr; + struct usb2_device *udev; +}; + +/* + * The following structure defines an USB pipe which is equal to an + * USB endpoint. + */ +struct usb2_pipe { + struct usb2_xfer_queue pipe_q; /* queue of USB transfers */ + + struct usb2_xfer *xfer_block; /* blocking USB transfer */ + struct usb2_endpoint_descriptor *edesc; + struct usb2_pipe_methods *methods; /* set by HC driver */ + + uint16_t isoc_next; + uint16_t refcount; + + uint8_t toggle_next:1; /* next data toggle value */ + uint8_t is_stalled:1; /* set if pipe is stalled */ + uint8_t is_synced:1; /* set if we a synchronised */ + uint8_t unused:5; + uint8_t iface_index; /* not used by "default pipe" */ +}; + +/* + * The following structure defines an USB interface. + */ +struct usb2_interface { + struct usb2_perm perm; /* interface permissions */ + struct usb2_interface_descriptor *idesc; + device_t subdev; + uint8_t alt_index; + uint8_t parent_iface_index; +}; + +/* + * The following structure defines the USB device flags. + */ +struct usb2_device_flags { + uint8_t usb2_mode:1; /* USB mode (see USB_MODE_XXX) */ + uint8_t self_powered:1; /* set if USB device is self powered */ + uint8_t suspended:1; /* set if USB device is suspended */ + uint8_t no_strings:1; /* set if USB device does not support + * strings */ + uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */ + uint8_t uq_bus_powered:1; /* set if BUS powered quirk is present */ + uint8_t uq_power_claim:1; /* set if power claim quirk is present */ +}; + +/* + * The following structure defines an USB device. There exists one of + * these structures for every USB device. + */ +struct usb2_device { + struct usb2_clear_stall_msg cs_msg[2]; /* generic clear stall + * messages */ + struct usb2_perm perm; + struct sx default_sx[2]; + struct mtx default_mtx[1]; + struct cv default_cv[2]; + struct usb2_interface ifaces[USB_IFACE_MAX]; + struct usb2_pipe default_pipe; /* Control Endpoint 0 */ + struct usb2_pipe pipes[USB_EP_MAX]; + + struct usb2_bus *bus; /* our USB BUS */ + device_t parent_dev; /* parent device */ + struct usb2_device *parent_hub; + struct usb2_config_descriptor *cdesc; /* full config descr */ + struct usb2_hub *hub; /* only if this is a hub */ + struct usb_device *linux_dev; + struct usb2_xfer *default_xfer[USB_DEFAULT_XFER_MAX]; + struct usb2_temp_data *usb2_template_ptr; + struct usb2_pipe *pipe_curr; /* current clear stall pipe */ + struct usb2_fifo *fifo[USB_FIFO_MAX]; + struct usb2_symlink *ugen_symlink; /* our generic symlink */ + + uint32_t plugtime; /* copy of "ticks" */ + + uint16_t refcount; +#define USB_DEV_REF_MAX 0xffff + + uint16_t power; /* mA the device uses */ + uint16_t langid; /* language for strings */ + + uint8_t address; /* device addess */ + uint8_t device_index; /* device index in "bus->devices" */ + uint8_t curr_config_index; /* current configuration index */ + uint8_t curr_config_no; /* current configuration number */ + uint8_t depth; /* distance from root HUB */ + uint8_t speed; /* low/full/high speed */ + uint8_t port_index; /* parent HUB port index */ + uint8_t port_no; /* parent HUB port number */ + uint8_t hs_hub_addr; /* high-speed HUB address */ + uint8_t hs_port_no; /* high-speed HUB port number */ + uint8_t driver_added_refcount; /* our driver added generation count */ + uint8_t power_mode; /* see USB_POWER_XXX */ + + /* the "flags" field is write-protected by "bus->mtx" */ + + struct usb2_device_flags flags; + + struct usb2_endpoint_descriptor default_ep_desc; /* for pipe 0 */ + struct usb2_device_descriptor ddesc; /* device descriptor */ + + char serial[64]; /* serial number */ + char manufacturer[64]; /* manufacturer string */ + char product[64]; /* product string */ +}; + +/* function prototypes */ + +struct usb2_device *usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, struct usb2_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb2_mode); +struct usb2_pipe *usb2_get_pipe(struct usb2_device *udev, uint8_t iface_index, const struct usb2_config *setup); +struct usb2_pipe *usb2_get_pipe_by_addr(struct usb2_device *udev, uint8_t ea_val); +usb2_error_t usb2_interface_count(struct usb2_device *udev, uint8_t *count); +usb2_error_t usb2_probe_and_attach(struct usb2_device *udev, uint8_t iface_index); +usb2_error_t usb2_reset_iface_endpoints(struct usb2_device *udev, uint8_t iface_index); +usb2_error_t usb2_set_config_index(struct usb2_device *udev, uint8_t index); +usb2_error_t usb2_set_endpoint_stall(struct usb2_device *udev, struct usb2_pipe *pipe, uint8_t do_stall); +usb2_error_t usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend); +void usb2_detach_device(struct usb2_device *udev, uint8_t iface_index, uint8_t free_subdev); +void usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len); +void usb2_free_device(struct usb2_device *udev); +void *usb2_find_descriptor(struct usb2_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); +void usb_linux_free_device(struct usb_device *dev); + +#endif /* _USB2_DEVICE_H_ */ diff --git a/sys/dev/usb2/core/usb2_dynamic.c b/sys/dev/usb2/core/usb2_dynamic.c new file mode 100644 index 000000000000..2bd4b01331ce --- /dev/null +++ b/sys/dev/usb2/core/usb2_dynamic.c @@ -0,0 +1,140 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#include +#include +#include +#include + +/* function prototypes */ +static usb2_temp_get_desc_t usb2_temp_get_desc_w; +static usb2_temp_setup_by_index_t usb2_temp_setup_by_index_w; +static usb2_temp_unsetup_t usb2_temp_unsetup_w; +static usb2_test_quirk_t usb2_test_quirk_w; +static usb2_quirk_ioctl_t usb2_quirk_ioctl_w; + +/* global variables */ +usb2_temp_get_desc_t *usb2_temp_get_desc_p = &usb2_temp_get_desc_w; +usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w; +usb2_temp_unsetup_t *usb2_temp_unsetup_p = &usb2_temp_unsetup_w; +usb2_test_quirk_t *usb2_test_quirk_p = &usb2_test_quirk_w; +usb2_quirk_ioctl_t *usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w; +devclass_t usb2_devclass_ptr = NULL; + +static usb2_error_t +usb2_temp_setup_by_index_w(struct usb2_device *udev, uint16_t index) +{ + return (USB_ERR_INVAL); +} + +static uint8_t +usb2_test_quirk_w(const struct usb2_lookup_info *info, uint16_t quirk) +{ + return (0); /* no match */ +} + +static int +usb2_quirk_ioctl_w(unsigned long cmd, caddr_t data, int fflag, struct thread *td) +{ + return (ENOIOCTL); +} + +static void +usb2_temp_get_desc_w(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength) +{ + /* stall */ + *pPtr = NULL; + *pLength = 0; + return; +} + +static void +usb2_temp_unsetup_w(struct usb2_device *udev) +{ + if (udev->usb2_template_ptr) { + + free(udev->usb2_template_ptr, M_USB); + + udev->usb2_template_ptr = NULL; + } + return; +} + +void +usb2_quirk_unload(void *arg) +{ + /* reset function pointers */ + + usb2_test_quirk_p = &usb2_test_quirk_w; + usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w; + + /* wait for CPU to exit the loaded functions, if any */ + + /* XXX this is a tradeoff */ + + pause("WAIT", hz); + + return; +} + +void +usb2_temp_unload(void *arg) +{ + /* reset function pointers */ + + usb2_temp_get_desc_p = &usb2_temp_get_desc_w; + usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w; + usb2_temp_unsetup_p = &usb2_temp_unsetup_w; + + /* wait for CPU to exit the loaded functions, if any */ + + /* XXX this is a tradeoff */ + + pause("WAIT", hz); + + return; +} + +void +usb2_bus_unload(void *arg) +{ + /* reset function pointers */ + + usb2_devclass_ptr = NULL; + + /* wait for CPU to exit the loaded functions, if any */ + + /* XXX this is a tradeoff */ + + pause("WAIT", hz); + + return; +} diff --git a/sys/dev/usb2/core/usb2_dynamic.h b/sys/dev/usb2/core/usb2_dynamic.h new file mode 100644 index 000000000000..ca59f96e2d00 --- /dev/null +++ b/sys/dev/usb2/core/usb2_dynamic.h @@ -0,0 +1,61 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_DYNAMIC_H_ +#define _USB2_DYNAMIC_H_ + +/* prototypes */ + +struct usb2_device; +struct usb2_lookup_info; +struct usb2_device_request; + +/* typedefs */ + +typedef usb2_error_t (usb2_temp_setup_by_index_t)(struct usb2_device *udev, uint16_t index); +typedef uint8_t (usb2_test_quirk_t)(const struct usb2_lookup_info *info, uint16_t quirk); +typedef int (usb2_quirk_ioctl_t)(unsigned long cmd, caddr_t data, int fflag, struct thread *td); +typedef void (usb2_temp_get_desc_t)(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength); +typedef void (usb2_temp_unsetup_t)(struct usb2_device *udev); + +/* global function pointers */ + +extern usb2_temp_get_desc_t *usb2_temp_get_desc_p; +extern usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p; +extern usb2_temp_unsetup_t *usb2_temp_unsetup_p; +extern usb2_test_quirk_t *usb2_test_quirk_p; +extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p; +extern devclass_t usb2_devclass_ptr; + +/* function prototypes */ + +void usb2_temp_unload(void *); +void usb2_quirk_unload(void *); +void usb2_bus_unload(void *); + +uint8_t usb2_test_quirk(const struct usb2_attach_arg *uaa, uint16_t quirk); + +#endif /* _USB2_DYNAMIC_H_ */ diff --git a/sys/dev/usb2/core/usb2_error.c b/sys/dev/usb2/core/usb2_error.c new file mode 100644 index 000000000000..51668599f2d8 --- /dev/null +++ b/sys/dev/usb2/core/usb2_error.c @@ -0,0 +1,44 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +#include + +USB_MAKE_DEBUG_TABLE(USB_ERR); + +/*------------------------------------------------------------------------* + * usb2_errstr + * + * This function converts an USB error code into a string. + *------------------------------------------------------------------------*/ +const char * +usb2_errstr(usb2_error_t err) +{ + return ((err < USB_ERR_MAX) ? + USB_ERR[err] : "USB_ERR_UNKNOWN"); +} diff --git a/sys/dev/usb2/core/usb2_generic.c b/sys/dev/usb2/core/usb2_generic.c new file mode 100644 index 000000000000..56ada335bad6 --- /dev/null +++ b/sys/dev/usb2/core/usb2_generic.c @@ -0,0 +1,2226 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR ugen_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* defines */ + +#define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */ +#define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */ +#define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ + +/* function prototypes */ + +static usb2_callback_t ugen_read_clear_stall_callback; +static usb2_callback_t ugen_write_clear_stall_callback; +static usb2_callback_t ugen_default_read_callback; +static usb2_callback_t ugen_default_write_callback; +static usb2_callback_t ugen_isoc_read_callback; +static usb2_callback_t ugen_isoc_write_callback; +static usb2_callback_t ugen_default_fs_callback; + +static usb2_fifo_open_t ugen_open; +static usb2_fifo_close_t ugen_close; +static usb2_fifo_ioctl_t ugen_ioctl; +static usb2_fifo_cmd_t ugen_start_read; +static usb2_fifo_cmd_t ugen_start_write; +static usb2_fifo_cmd_t ugen_stop_io; + +static int ugen_transfer_setup(struct usb2_fifo *f, const struct usb2_config *setup, uint8_t n_setup); +static int ugen_open_pipe_write(struct usb2_fifo *f); +static int ugen_open_pipe_read(struct usb2_fifo *f); +static int ugen_set_config(struct usb2_fifo *f, uint8_t index); +static int ugen_set_interface(struct usb2_fifo *f, uint8_t iface_index, uint8_t alt_index); +static int ugen_get_cdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *pgd); +static int ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd); +static int usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di); +static int ugen_re_enumerate(struct usb2_fifo *f); +static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags); +static int ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags); +static int ugen_fs_uninit(struct usb2_fifo *f); +static uint8_t ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex); + + +/* structures */ + +struct usb2_fifo_methods usb2_ugen_methods = { + .f_open = &ugen_open, + .f_close = &ugen_close, + .f_ioctl = &ugen_ioctl, + .f_start_read = &ugen_start_read, + .f_stop_read = &ugen_stop_io, + .f_start_write = &ugen_start_write, + .f_stop_write = &ugen_stop_io, +}; + +#if USB_DEBUG +static int ugen_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); +SYSCTL_INT(_hw_usb2_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugen_debug, + 0, "Debug level"); +#endif + + +/* prototypes */ + +static int +ugen_transfer_setup(struct usb2_fifo *f, + const struct usb2_config *setup, uint8_t n_setup) +{ + struct usb2_pipe *pipe = f->priv_sc0; + struct usb2_device *udev = f->udev; + uint8_t iface_index = pipe->iface_index; + int error; + + mtx_unlock(f->priv_mtx); + + /* + * "usb2_transfer_setup()" can sleep so one needs to make a wrapper, + * exiting the mutex and checking things + */ + error = usb2_transfer_setup(udev, &iface_index, f->xfer, + setup, n_setup, f, f->priv_mtx); + if (error == 0) { + + if (f->xfer[0]->nframes == 1) { + error = usb2_fifo_alloc_buffer(f, + f->xfer[0]->max_data_length, 2); + } else { + error = usb2_fifo_alloc_buffer(f, + f->xfer[0]->max_frame_size, + 2 * f->xfer[0]->nframes); + } + if (error) { + usb2_transfer_unsetup(f->xfer, n_setup); + } + } + mtx_lock(f->priv_mtx); + + return (error); +} + +static int +ugen_open(struct usb2_fifo *f, int fflags, struct thread *td) +{ + struct usb2_pipe *pipe = f->priv_sc0; + struct usb2_endpoint_descriptor *ed = pipe->edesc; + uint8_t type; + + DPRINTFN(6, "flag=0x%x\n", fflags); + + mtx_lock(f->priv_mtx); + if (usb2_get_speed(f->udev) == USB_SPEED_HIGH) { + f->nframes = UGEN_HW_FRAMES * 8; + f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; + } else { + f->nframes = UGEN_HW_FRAMES; + f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; + } + + type = ed->bmAttributes & UE_XFERTYPE; + if (type == UE_INTERRUPT) { + f->bufsize = 0; /* use "wMaxPacketSize" */ + } + f->timeout = USB_NO_TIMEOUT; + f->flag_short = 0; + f->fifo_zlp = 0; + mtx_unlock(f->priv_mtx); + + return (0); +} + +static void +ugen_close(struct usb2_fifo *f, int fflags, struct thread *td) +{ + DPRINTFN(6, "flag=0x%x\n", fflags); + + /* cleanup */ + + mtx_lock(f->priv_mtx); + usb2_transfer_stop(f->xfer[0]); + usb2_transfer_stop(f->xfer[1]); + mtx_unlock(f->priv_mtx); + + usb2_transfer_unsetup(f->xfer, 2); + usb2_fifo_free_buffer(f); + + if (ugen_fs_uninit(f)) { + /* ignore any errors - we are closing */ + } + return; +} + +static int +ugen_open_pipe_write(struct usb2_fifo *f) +{ + struct usb2_config usb2_config[2]; + struct usb2_pipe *pipe = f->priv_sc0; + struct usb2_endpoint_descriptor *ed = pipe->edesc; + + mtx_assert(f->priv_mtx, MA_OWNED); + + if (f->xfer[0] || f->xfer[1]) { + /* transfers are already opened */ + return (0); + } + if (f->fs_xfer) { + /* should not happen */ + return (EINVAL); + } + bzero(usb2_config, sizeof(usb2_config)); + + usb2_config[1].type = UE_CONTROL; + usb2_config[1].endpoint = 0; + usb2_config[1].direction = UE_DIR_ANY; + usb2_config[1].mh.timeout = 1000; /* 1 second */ + usb2_config[1].mh.interval = 50;/* 50 milliseconds */ + usb2_config[1].mh.bufsize = sizeof(struct usb2_device_request); + usb2_config[1].mh.callback = &ugen_write_clear_stall_callback; + + usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE; + usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; + usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); + usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL; + usb2_config[0].mh.flags.proxy_buffer = 1; + + switch (ed->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + case UE_BULK: + if (f->flag_short) { + usb2_config[0].mh.flags.force_short_xfer = 1; + } + usb2_config[0].mh.callback = &ugen_default_write_callback; + usb2_config[0].mh.timeout = f->timeout; + usb2_config[0].mh.frames = 1; + usb2_config[0].mh.bufsize = f->bufsize; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + if (ugen_transfer_setup(f, usb2_config, 2)) { + return (EIO); + } + /* first transfer does not clear stall */ + f->flag_stall = 0; + break; + + case UE_ISOCHRONOUS: + usb2_config[0].mh.flags.short_xfer_ok = 1; + usb2_config[0].mh.bufsize = 0; /* use default */ + usb2_config[0].mh.frames = f->nframes; + usb2_config[0].mh.callback = &ugen_isoc_write_callback; + usb2_config[0].mh.timeout = 0; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + + /* clone configuration */ + usb2_config[1] = usb2_config[0]; + + if (ugen_transfer_setup(f, usb2_config, 2)) { + return (EIO); + } + break; + default: + return (EINVAL); + } + return (0); +} + +static int +ugen_open_pipe_read(struct usb2_fifo *f) +{ + struct usb2_config usb2_config[2]; + struct usb2_pipe *pipe = f->priv_sc0; + struct usb2_endpoint_descriptor *ed = pipe->edesc; + + mtx_assert(f->priv_mtx, MA_OWNED); + + if (f->xfer[0] || f->xfer[1]) { + /* transfers are already opened */ + return (0); + } + if (f->fs_xfer) { + /* should not happen */ + return (EINVAL); + } + bzero(usb2_config, sizeof(usb2_config)); + + usb2_config[1].type = UE_CONTROL; + usb2_config[1].endpoint = 0; + usb2_config[1].direction = UE_DIR_ANY; + usb2_config[1].mh.timeout = 1000; /* 1 second */ + usb2_config[1].mh.interval = 50;/* 50 milliseconds */ + usb2_config[1].mh.bufsize = sizeof(struct usb2_device_request); + usb2_config[1].mh.callback = &ugen_read_clear_stall_callback; + + usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE; + usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; + usb2_config[0].direction = UE_DIR_IN; + usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL; + usb2_config[0].mh.flags.proxy_buffer = 1; + + switch (ed->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + case UE_BULK: + if (f->flag_short) { + usb2_config[0].mh.flags.short_xfer_ok = 1; + } + usb2_config[0].mh.timeout = f->timeout; + usb2_config[0].mh.frames = 1; + usb2_config[0].mh.callback = &ugen_default_read_callback; + usb2_config[0].mh.bufsize = f->bufsize; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + + if (ugen_transfer_setup(f, usb2_config, 2)) { + return (EIO); + } + /* first transfer does not clear stall */ + f->flag_stall = 0; + break; + + case UE_ISOCHRONOUS: + usb2_config[0].mh.flags.short_xfer_ok = 1; + usb2_config[0].mh.bufsize = 0; /* use default */ + usb2_config[0].mh.frames = f->nframes; + usb2_config[0].mh.callback = &ugen_isoc_read_callback; + usb2_config[0].mh.timeout = 0; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + + /* clone configuration */ + usb2_config[1] = usb2_config[0]; + + if (ugen_transfer_setup(f, usb2_config, 2)) { + return (EIO); + } + break; + + default: + return (EINVAL); + } + return (0); +} + +static void +ugen_start_read(struct usb2_fifo *f) +{ + /* check that pipes are open */ + if (ugen_open_pipe_read(f)) { + /* signal error */ + usb2_fifo_put_data_error(f); + } + /* start transfers */ + usb2_transfer_start(f->xfer[0]); + usb2_transfer_start(f->xfer[1]); + return; +} + +static void +ugen_start_write(struct usb2_fifo *f) +{ + /* check that pipes are open */ + if (ugen_open_pipe_write(f)) { + /* signal error */ + usb2_fifo_get_data_error(f); + } + /* start transfers */ + usb2_transfer_start(f->xfer[0]); + usb2_transfer_start(f->xfer[1]); + return; +} + +static void +ugen_stop_io(struct usb2_fifo *f) +{ + /* stop transfers */ + usb2_transfer_stop(f->xfer[0]); + usb2_transfer_stop(f->xfer[1]); + return; +} + +static void +ugen_default_read_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + struct usb2_mbuf *m; + + DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen == 0) { + if (f->fifo_zlp != 4) { + f->fifo_zlp++; + } else { + /* + * Throttle a little bit we have multiple ZLPs + * in a row! + */ + xfer->interval = 64; /* ms */ + } + } else { + /* clear throttle */ + xfer->interval = 0; + f->fifo_zlp = 0; + } + usb2_fifo_put_data(f, xfer->frbuffers, 0, + xfer->actlen, 1); + + case USB_ST_SETUP: + if (f->flag_stall) { + usb2_transfer_start(f->xfer[1]); + break; + } + USB_IF_POLL(&f->free_q, m); + if (m) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + f->flag_stall = 1; + f->fifo_zlp = 0; + usb2_transfer_start(f->xfer[1]); + } + break; + } + return; +} + +static void +ugen_default_write_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + uint32_t actlen; + + DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + /* + * If writing is in stall, just jump to clear stall + * callback and solve the situation. + */ + if (f->flag_stall) { + usb2_transfer_start(f->xfer[1]); + break; + } + /* + * Write data, setup and perform hardware transfer. + */ + if (usb2_fifo_get_data(f, xfer->frbuffers, 0, + xfer->max_data_length, &actlen, 0)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + f->flag_stall = 1; + usb2_transfer_start(f->xfer[1]); + } + break; + } + return; +} + +static void +ugen_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + struct usb2_xfer *xfer_other = f->xfer[0]; + + if (f->flag_stall == 0) { + /* nothing to do */ + return; + } + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTFN(5, "f=%p: stall cleared\n", f); + f->flag_stall = 0; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ugen_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + struct usb2_xfer *xfer_other = f->xfer[0]; + + if (f->flag_stall == 0) { + /* nothing to do */ + return; + } + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTFN(5, "f=%p: stall cleared\n", f); + f->flag_stall = 0; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ugen_isoc_read_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + uint32_t offset; + uint16_t n; + + DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(6, "actlen=%d\n", xfer->actlen); + + offset = 0; + + for (n = 0; n != xfer->aframes; n++) { + usb2_fifo_put_data(f, xfer->frbuffers, offset, + xfer->frlengths[n], 1); + offset += xfer->max_frame_size; + } + + case USB_ST_SETUP: +tr_setup: + for (n = 0; n != xfer->nframes; n++) { + /* setup size for next transfer */ + xfer->frlengths[n] = xfer->max_frame_size; + } + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + goto tr_setup; + } + return; +} + +static void +ugen_isoc_write_callback(struct usb2_xfer *xfer) +{ + struct usb2_fifo *f = xfer->priv_sc; + uint32_t actlen; + uint32_t offset; + uint16_t n; + + DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +tr_setup: + offset = 0; + for (n = 0; n != xfer->nframes; n++) { + if (usb2_fifo_get_data(f, xfer->frbuffers, offset, + xfer->max_frame_size, &actlen, 1)) { + xfer->frlengths[n] = actlen; + offset += actlen; + } else { + break; + } + } + + for (; n != xfer->nframes; n++) { + /* fill in zero frames */ + xfer->frlengths[n] = 0; + } + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + goto tr_setup; + } + return; +} + +static int +ugen_set_config(struct usb2_fifo *f, uint8_t index) +{ + DPRINTFN(2, "index %u\n", index); + + if (f->flag_no_uref) { + /* not the control endpoint - just forget it */ + return (EINVAL); + } + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + /* not possible in device side mode */ + return (ENOTTY); + } + if (f->udev->curr_config_index == index) { + /* no change needed */ + return (0); + } + /* change setting - will free generic FIFOs, if any */ + if (usb2_set_config_index(f->udev, index)) { + return (EIO); + } + /* probe and attach */ + if (usb2_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) { + return (EIO); + } + return (0); +} + +static int +ugen_set_interface(struct usb2_fifo *f, + uint8_t iface_index, uint8_t alt_index) +{ + DPRINTFN(2, "%u, %u\n", iface_index, alt_index); + + if (f->flag_no_uref) { + /* not the control endpoint - just forget it */ + return (EINVAL); + } + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + /* not possible in device side mode */ + return (ENOTTY); + } + /* change setting - will free generic FIFOs, if any */ + if (usb2_set_alt_interface_index(f->udev, iface_index, alt_index)) { + return (EIO); + } + /* probe and attach */ + if (usb2_probe_and_attach(f->udev, iface_index)) { + return (EIO); + } + return (0); +} + +/*------------------------------------------------------------------------* + * ugen_get_cdesc + * + * This function will retrieve the complete configuration descriptor + * at the given index. + *------------------------------------------------------------------------*/ +static int +ugen_get_cdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd) +{ + struct usb2_config_descriptor *cdesc; + struct usb2_device *udev = f->udev; + int error; + uint16_t len; + uint8_t free_data; + + DPRINTFN(6, "\n"); + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + if (ugd->ugd_data == NULL) { + /* userland pointer should not be zero */ + return (EINVAL); + } + if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || + (ugd->ugd_config_index == udev->curr_config_index)) { + cdesc = usb2_get_config_descriptor(udev); + if (cdesc == NULL) { + return (ENXIO); + } + free_data = 0; + + } else { + if (usb2_req_get_config_desc_full(udev, + &Giant, &cdesc, M_USBDEV, + ugd->ugd_config_index)) { + return (ENXIO); + } + free_data = 1; + } + + len = UGETW(cdesc->wTotalLength); + if (len > ugd->ugd_maxlen) { + len = ugd->ugd_maxlen; + } + DPRINTFN(6, "len=%u\n", len); + + ugd->ugd_actlen = len; + ugd->ugd_offset = 0; + + error = copyout(cdesc, ugd->ugd_data, len); + + if (free_data) { + free(cdesc, M_USBDEV); + } + return (error); +} + +static int +ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd) +{ + void *ptr = f->udev->bus->scratch[0].data; + uint16_t size = sizeof(f->udev->bus->scratch[0].data); + int error; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + if (usb2_req_get_string_desc(f->udev, &Giant, ptr, + size, ugd->ugd_lang_id, ugd->ugd_string_index)) { + error = EINVAL; + } else { + + if (size > ((uint8_t *)ptr)[0]) { + size = ((uint8_t *)ptr)[0]; + } + if (size > ugd->ugd_maxlen) { + size = ugd->ugd_maxlen; + } + ugd->ugd_actlen = size; + ugd->ugd_offset = 0; + + error = copyout(ptr, ugd->ugd_data, size); + } + return (error); +} + +/*------------------------------------------------------------------------* + * usb2_gen_fill_devicenames + * + * This function dumps information about an USB device names to + * userland. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static int +usb2_gen_fill_devicenames(struct usb2_fifo *f, struct usb2_device_names *dn) +{ + struct usb2_interface *iface; + const char *ptr; + char *dst; + char buf[32]; + int error = 0; + int len; + int max_len; + uint8_t i; + uint8_t first = 1; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + max_len = dn->udn_devnames_len; + dst = dn->udn_devnames_ptr; + + if (max_len == 0) { + return (EINVAL); + } + /* put a zero there */ + error = copyout("", dst, 1); + if (error) { + return (error); + } + for (i = 0;; i++) { + iface = usb2_get_iface(f->udev, i); + if (iface == NULL) { + break; + } + if ((iface->subdev != NULL) && + device_is_attached(iface->subdev)) { + ptr = device_get_nameunit(iface->subdev); + if (!first) { + strlcpy(buf, ", ", sizeof(buf)); + } else { + buf[0] = 0; + } + strlcat(buf, ptr, sizeof(buf)); + len = strlen(buf) + 1; + if (len > max_len) { + break; + } + error = copyout(buf, dst, len); + if (error) { + return (error); + } + len--; + dst += len; + max_len -= len; + first = 0; + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_gen_fill_deviceinfo + * + * This function dumps information about an USB device to the + * structure pointed to by the "di" argument. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static int +usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di) +{ + struct usb2_device *udev; + struct usb2_device *hub; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + udev = f->udev; + + bzero(di, sizeof(di[0])); + + di->udi_bus = device_get_unit(udev->bus->bdev); + di->udi_addr = udev->address; + di->udi_index = udev->device_index; + strlcpy(di->udi_serial, udev->serial, + sizeof(di->udi_serial)); + strlcpy(di->udi_vendor, udev->manufacturer, + sizeof(di->udi_vendor)); + strlcpy(di->udi_product, udev->product, + sizeof(di->udi_product)); + usb2_printBCD(di->udi_release, sizeof(di->udi_release), + UGETW(udev->ddesc.bcdDevice)); + di->udi_vendorNo = UGETW(udev->ddesc.idVendor); + di->udi_productNo = UGETW(udev->ddesc.idProduct); + di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); + di->udi_class = udev->ddesc.bDeviceClass; + di->udi_subclass = udev->ddesc.bDeviceSubClass; + di->udi_protocol = udev->ddesc.bDeviceProtocol; + di->udi_config_no = udev->curr_config_no; + di->udi_config_index = udev->curr_config_index; + di->udi_power = udev->flags.self_powered ? 0 : udev->power; + di->udi_speed = udev->speed; + di->udi_mode = udev->flags.usb2_mode; + di->udi_power_mode = udev->power_mode; + if (udev->flags.suspended) { + di->udi_suspended = 1; + } else { + di->udi_suspended = 0; + } + + hub = udev->parent_hub; + if (hub) { + di->udi_hubaddr = hub->address; + di->udi_hubindex = hub->device_index; + di->udi_hubport = udev->port_no; + } + return (0); +} + +/*------------------------------------------------------------------------* + * ugen_check_request + * + * Return values: + * 0: Access allowed + * Else: No access + *------------------------------------------------------------------------*/ +static int +ugen_check_request(struct usb2_device *udev, struct usb2_device_request *req) +{ + struct usb2_pipe *pipe; + int error; + + /* + * Avoid requests that would damage the bus integrity: + */ + if (((req->bmRequestType == UT_WRITE_DEVICE) && + (req->bRequest == UR_SET_ADDRESS)) || + ((req->bmRequestType == UT_WRITE_DEVICE) && + (req->bRequest == UR_SET_CONFIG)) || + ((req->bmRequestType == UT_WRITE_INTERFACE) && + (req->bRequest == UR_SET_INTERFACE))) { + /* + * These requests can be useful for testing USB drivers. + */ + error = priv_check(curthread, PRIV_DRIVER); + if (error) { + return (error); + } + } + /* + * Special case - handle clearing of stall + */ + if (req->bmRequestType == UT_WRITE_ENDPOINT) { + + pipe = usb2_get_pipe_by_addr(udev, req->wIndex[0]); + if (pipe == NULL) { + return (EINVAL); + } + if (usb2_check_thread_perm(udev, curthread, FREAD | FWRITE, + pipe->iface_index, req->wIndex[0] & UE_ADDR)) { + return (EPERM); + } + if ((req->bRequest == UR_CLEAR_FEATURE) && + (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { + usb2_clear_data_toggle(udev, pipe); + } + } + /* TODO: add more checks to verify the interface index */ + + return (0); +} + +int +ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur) +{ + int error; + uint16_t len; + uint16_t actlen; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + if (ugen_check_request(f->udev, &ur->ucr_request)) { + return (EPERM); + } + len = UGETW(ur->ucr_request.wLength); + + /* check if "ucr_data" is valid */ + if (len != 0) { + if (ur->ucr_data == NULL) { + return (EFAULT); + } + } + /* do the USB request */ + error = usb2_do_request_flags + (f->udev, NULL, &ur->ucr_request, ur->ucr_data, + (ur->ucr_flags & USB_SHORT_XFER_OK) | + USB_USER_DATA_PTR, &actlen, + USB_DEFAULT_TIMEOUT); + + ur->ucr_actlen = actlen; + + if (error) { + error = EIO; + } + return (error); +} + +/*------------------------------------------------------------------------ + * ugen_re_enumerate + *------------------------------------------------------------------------*/ +static int +ugen_re_enumerate(struct usb2_fifo *f) +{ + struct usb2_device *udev = f->udev; + int error; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + /* + * This request can be useful for testing USB drivers: + */ + error = priv_check(curthread, PRIV_DRIVER); + if (error) { + return (error); + } + mtx_lock(f->priv_mtx); + error = usb2_req_re_enumerate(udev, f->priv_mtx); + mtx_unlock(f->priv_mtx); + + if (error) { + return (ENXIO); + } + return (0); +} + +static int +ugen_fs_uninit(struct usb2_fifo *f) +{ + if (f->fs_xfer == NULL) { + return (EINVAL); + } + usb2_transfer_unsetup(f->fs_xfer, f->fs_ep_max); + free(f->fs_xfer, M_USB); + f->fs_xfer = NULL; + f->fs_ep_max = 0; + f->fs_ep_ptr = NULL; + f->flag_iscomplete = 0; + f->flag_no_uref = 0; /* restore operation */ + usb2_fifo_free_buffer(f); + return (0); +} + +static uint8_t +ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&f->used_q, m); + + if (m) { + *pindex = *((uint8_t *)(m->cur_data_ptr)); + + USB_IF_ENQUEUE(&f->free_q, m); + + return (0); /* success */ + } else { + + *pindex = 0; /* fix compiler warning */ + + f->flag_iscomplete = 0; + } + return (1); /* failure */ +} + +static void +ugen_fs_set_complete(struct usb2_fifo *f, uint8_t index) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&f->free_q, m); + + if (m == NULL) { + /* can happen during close */ + DPRINTF("out of buffers\n"); + return; + } + USB_MBUF_RESET(m); + + *((uint8_t *)(m->cur_data_ptr)) = index; + + USB_IF_ENQUEUE(&f->used_q, m); + + f->flag_iscomplete = 1; + + usb2_fifo_wakeup(f); + + return; +} + +static int +ugen_fs_copy_in(struct usb2_fifo *f, uint8_t ep_index) +{ + struct usb2_device_request *req; + struct usb2_xfer *xfer; + struct usb2_fs_endpoint fs_ep; + void *uaddr; /* userland pointer */ + void *kaddr; + uint32_t offset; + uint32_t length; + uint32_t n; + uint32_t rem; + int error; + uint8_t isread; + + if (ep_index >= f->fs_ep_max) { + return (EINVAL); + } + xfer = f->fs_xfer[ep_index]; + if (xfer == NULL) { + return (EINVAL); + } + mtx_lock(f->priv_mtx); + if (usb2_transfer_pending(xfer)) { + mtx_unlock(f->priv_mtx); + return (EBUSY); /* should not happen */ + } + mtx_unlock(f->priv_mtx); + + error = copyin(f->fs_ep_ptr + + ep_index, &fs_ep, sizeof(fs_ep)); + if (error) { + return (error); + } + /* security checks */ + + if (fs_ep.nFrames > xfer->max_frame_count) { + xfer->error = USB_ERR_INVAL; + goto complete; + } + if (fs_ep.nFrames == 0) { + xfer->error = USB_ERR_INVAL; + goto complete; + } + error = copyin(fs_ep.ppBuffer, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + /* reset first frame */ + usb2_set_frame_offset(xfer, 0, 0); + + if (xfer->flags_int.control_xfr) { + + req = xfer->frbuffers[0].buffer; + + error = copyin(fs_ep.pLength, + &length, sizeof(length)); + if (error) { + return (error); + } + if (length >= sizeof(*req)) { + xfer->error = USB_ERR_INVAL; + goto complete; + } + if (length != 0) { + error = copyin(uaddr, req, length); + if (error) { + return (error); + } + } + if (ugen_check_request(f->udev, req)) { + xfer->error = USB_ERR_INVAL; + goto complete; + } + xfer->frlengths[0] = length; + + /* Host mode only ! */ + if ((req->bmRequestType & + (UT_READ | UT_WRITE)) == UT_READ) { + isread = 1; + } else { + isread = 0; + } + n = 1; + offset = sizeof(*req); + + } else { + /* Device and Host mode */ + if (USB_GET_DATA_ISREAD(xfer)) { + isread = 1; + } else { + isread = 0; + } + n = 0; + offset = 0; + } + + rem = xfer->max_data_length; + xfer->nframes = fs_ep.nFrames; + xfer->timeout = fs_ep.timeout; + if (xfer->timeout > 65535) { + xfer->timeout = 65535; + } + if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) + xfer->flags.short_xfer_ok = 1; + else + xfer->flags.short_xfer_ok = 0; + + if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) + xfer->flags.short_frames_ok = 1; + else + xfer->flags.short_frames_ok = 0; + + if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) + xfer->flags.force_short_xfer = 1; + else + xfer->flags.force_short_xfer = 0; + + if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) + xfer->flags.stall_pipe = 1; + else + xfer->flags.stall_pipe = 0; + + for (; n != xfer->nframes; n++) { + + error = copyin(fs_ep.pLength + n, + &length, sizeof(length)); + if (error) { + break; + } + xfer->frlengths[n] = length; + + if (length > rem) { + xfer->error = USB_ERR_INVAL; + goto complete; + } + rem -= length; + + if (!isread) { + + /* we need to know the source buffer */ + error = copyin(fs_ep.ppBuffer + n, + &uaddr, sizeof(uaddr)); + if (error) { + break; + } + if (xfer->flags_int.isochronous_xfr) { + /* get kernel buffer address */ + kaddr = xfer->frbuffers[0].buffer; + kaddr = USB_ADD_BYTES(kaddr, offset); + } else { + /* set current frame offset */ + usb2_set_frame_offset(xfer, offset, n); + + /* get kernel buffer address */ + kaddr = xfer->frbuffers[n].buffer; + } + + /* move data */ + error = copyin(uaddr, kaddr, length); + if (error) { + break; + } + } + offset += length; + } + return (error); + +complete: + mtx_lock(f->priv_mtx); + ugen_fs_set_complete(f, ep_index); + mtx_unlock(f->priv_mtx); + return (0); +} + +static int +ugen_fs_copy_out(struct usb2_fifo *f, uint8_t ep_index) +{ + struct usb2_device_request *req; + struct usb2_xfer *xfer; + struct usb2_fs_endpoint fs_ep; + struct usb2_fs_endpoint *fs_ep_uptr; /* userland ptr */ + void *uaddr; /* userland ptr */ + void *kaddr; + uint32_t offset; + uint32_t length; + uint32_t temp; + uint32_t n; + uint32_t rem; + int error; + uint8_t isread; + + if (ep_index >= f->fs_ep_max) { + return (EINVAL); + } + xfer = f->fs_xfer[ep_index]; + if (xfer == NULL) { + return (EINVAL); + } + mtx_lock(f->priv_mtx); + if (usb2_transfer_pending(xfer)) { + mtx_unlock(f->priv_mtx); + return (EBUSY); /* should not happen */ + } + mtx_unlock(f->priv_mtx); + + fs_ep_uptr = f->fs_ep_ptr + ep_index; + error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); + if (error) { + return (error); + } + fs_ep.status = xfer->error; + fs_ep.aFrames = xfer->aframes; + fs_ep.isoc_time_complete = xfer->isoc_time_complete; + if (xfer->error) { + goto complete; + } + if (xfer->flags_int.control_xfr) { + req = xfer->frbuffers[0].buffer; + + /* Host mode only ! */ + if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { + isread = 1; + } else { + isread = 0; + } + if (xfer->nframes == 0) + n = 0; /* should never happen */ + else + n = 1; + } else { + /* Device and Host mode */ + if (USB_GET_DATA_ISREAD(xfer)) { + isread = 1; + } else { + isread = 0; + } + n = 0; + } + + /* Update lengths and copy out data */ + + rem = xfer->max_data_length; + offset = 0; + + for (; n != xfer->nframes; n++) { + + /* get initial length into "temp" */ + error = copyin(fs_ep.pLength + n, + &temp, sizeof(temp)); + if (error) { + return (error); + } + if (temp > rem) { + /* the userland length has been corrupted */ + DPRINTF("corrupt userland length " + "%u > %u\n", temp, rem); + fs_ep.status = USB_ERR_INVAL; + goto complete; + } + rem -= temp; + + /* get actual transfer length */ + length = xfer->frlengths[n]; + if (length > temp) { + /* data overflow */ + fs_ep.status = USB_ERR_INVAL; + DPRINTF("data overflow %u > %u\n", + length, temp); + goto complete; + } + if (isread) { + + /* we need to know the destination buffer */ + error = copyin(fs_ep.ppBuffer + n, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + if (xfer->flags_int.isochronous_xfr) { + /* only one frame buffer */ + kaddr = USB_ADD_BYTES( + xfer->frbuffers[0].buffer, offset); + } else { + /* multiple frame buffers */ + kaddr = xfer->frbuffers[n].buffer; + } + + /* move data */ + error = copyout(kaddr, uaddr, length); + if (error) { + return (error); + } + } + /* + * Update offset according to initial length, which is + * needed by isochronous transfers! + */ + offset += temp; + + /* update length */ + error = copyout(&length, + fs_ep.pLength + n, sizeof(length)); + if (error) { + return (error); + } + } + +complete: + /* update "aFrames" */ + error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, + sizeof(fs_ep.aFrames)); + if (error) + goto done; + + /* update "isoc_time_complete" */ + error = copyout(&fs_ep.isoc_time_complete, + &fs_ep_uptr->isoc_time_complete, + sizeof(fs_ep.isoc_time_complete)); + if (error) + goto done; + /* update "status" */ + error = copyout(&fs_ep.status, &fs_ep_uptr->status, + sizeof(fs_ep.status)); +done: + return (error); +} + +static uint8_t +ugen_fifo_in_use(struct usb2_fifo *f, int fflags) +{ + struct usb2_fifo *f_rx; + struct usb2_fifo *f_tx; + + f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; + f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; + + if ((fflags & FREAD) && f_rx && + (f_rx->xfer[0] || f_rx->xfer[1])) { + return (1); /* RX FIFO in use */ + } + if ((fflags & FWRITE) && f_tx && + (f_tx->xfer[0] || f_tx->xfer[1])) { + return (1); /* TX FIFO in use */ + } + return (0); /* not in use */ +} + +static int +ugen_fs_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags) +{ + struct usb2_config usb2_config[1]; + struct usb2_device_request req; + union { + struct usb2_fs_complete *pcomp; + struct usb2_fs_start *pstart; + struct usb2_fs_stop *pstop; + struct usb2_fs_init *pinit; + struct usb2_fs_uninit *puninit; + struct usb2_fs_open *popen; + struct usb2_fs_close *pclose; + struct usb2_fs_clear_stall_sync *pstall; + void *addr; + } u; + struct usb2_pipe *pipe; + struct usb2_endpoint_descriptor *ed; + int error = 0; + uint8_t iface_index; + uint8_t isread; + uint8_t ep_index; + + u.addr = addr; + + switch (cmd) { + case USB_FS_COMPLETE: + mtx_lock(f->priv_mtx); + error = ugen_fs_get_complete(f, &ep_index); + mtx_unlock(f->priv_mtx); + + if (error) { + error = EBUSY; + break; + } + u.pcomp->ep_index = ep_index; + error = ugen_fs_copy_out(f, u.pcomp->ep_index); + break; + + case USB_FS_START: + error = ugen_fs_copy_in(f, u.pstart->ep_index); + if (error) { + break; + } + mtx_lock(f->priv_mtx); + usb2_transfer_start(f->fs_xfer[u.pstart->ep_index]); + mtx_unlock(f->priv_mtx); + break; + + case USB_FS_STOP: + if (u.pstop->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + mtx_lock(f->priv_mtx); + usb2_transfer_stop(f->fs_xfer[u.pstop->ep_index]); + mtx_unlock(f->priv_mtx); + break; + + case USB_FS_INIT: + /* verify input parameters */ + if (u.pinit->pEndpoints == NULL) { + error = EINVAL; + break; + } + if (u.pinit->ep_index_max > 127) { + error = EINVAL; + break; + } + if (u.pinit->ep_index_max == 0) { + error = EINVAL; + break; + } + if (f->fs_xfer != NULL) { + error = EBUSY; + break; + } + if (f->flag_no_uref) { + error = EINVAL; + break; + } + if (f->dev_ep_index != 0) { + error = EINVAL; + break; + } + if (ugen_fifo_in_use(f, fflags)) { + error = EBUSY; + break; + } + error = usb2_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); + if (error) { + break; + } + f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * + u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); + if (f->fs_xfer == NULL) { + usb2_fifo_free_buffer(f); + error = ENOMEM; + break; + } + f->fs_ep_max = u.pinit->ep_index_max; + f->fs_ep_ptr = u.pinit->pEndpoints; + break; + + case USB_FS_UNINIT: + if (u.puninit->dummy != 0) { + error = EINVAL; + break; + } + error = ugen_fs_uninit(f); + break; + + case USB_FS_OPEN: + if (u.popen->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + if (f->fs_xfer[u.popen->ep_index] != NULL) { + error = EBUSY; + break; + } + if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { + u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; + } + if (u.popen->max_frames > USB_FS_MAX_FRAMES) { + u.popen->max_frames = USB_FS_MAX_FRAMES; + break; + } + if (u.popen->max_frames == 0) { + error = EINVAL; + break; + } + pipe = usb2_get_pipe_by_addr(f->udev, u.popen->ep_no); + if (pipe == NULL) { + error = EINVAL; + break; + } + ed = pipe->edesc; + if (ed == NULL) { + error = ENXIO; + break; + } + iface_index = pipe->iface_index; + + error = usb2_check_thread_perm(f->udev, curthread, fflags, + iface_index, u.popen->ep_no); + if (error) { + break; + } + bzero(usb2_config, sizeof(usb2_config)); + + usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE; + usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; + usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); + usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL; + usb2_config[0].mh.flags.proxy_buffer = 1; + usb2_config[0].mh.callback = &ugen_default_fs_callback; + usb2_config[0].mh.timeout = 0; /* no timeout */ + usb2_config[0].mh.frames = u.popen->max_frames; + usb2_config[0].mh.bufsize = u.popen->max_bufsize; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + + if (usb2_config[0].type == UE_CONTROL) { + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + error = EINVAL; + break; + } + } else { + + isread = ((usb2_config[0].endpoint & + (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); + + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + isread = !isread; + } + /* check permissions */ + if (isread) { + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + } else { + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + } + } + error = usb2_transfer_setup(f->udev, &iface_index, + f->fs_xfer + u.popen->ep_index, usb2_config, 1, + f, f->priv_mtx); + if (error == 0) { + /* update maximums */ + u.popen->max_packet_length = + f->fs_xfer[u.popen->ep_index]->max_frame_size; + u.popen->max_bufsize = + f->fs_xfer[u.popen->ep_index]->max_data_length; + f->fs_xfer[u.popen->ep_index]->priv_fifo = + ((uint8_t *)0) + u.popen->ep_index; + /* + * Increase performance by dropping locks we + * don't need: + */ + f->flag_no_uref = 1; + } else { + error = ENOMEM; + } + break; + + case USB_FS_CLOSE: + if (u.pclose->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + if (f->fs_xfer[u.pclose->ep_index] == NULL) { + error = EINVAL; + break; + } + usb2_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); + break; + + case USB_FS_CLEAR_STALL_SYNC: + if (u.pstall->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + if (f->fs_xfer[u.pstall->ep_index] == NULL) { + error = EINVAL; + break; + } + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + error = EINVAL; + break; + } + mtx_lock(f->priv_mtx); + error = usb2_transfer_pending(f->fs_xfer[u.pstall->ep_index]); + mtx_unlock(f->priv_mtx); + + if (error) { + return (EBUSY); + } + pipe = f->fs_xfer[u.pstall->ep_index]->pipe; + + /* setup a clear-stall packet */ + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + req.wIndex[0] = pipe->edesc->bEndpointAddress; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + error = usb2_do_request(f->udev, NULL, &req, NULL); + if (error == 0) { + usb2_clear_data_toggle(f->udev, pipe); + } else { + error = ENXIO; + } + break; + + default: + error = ENOTTY; + break; + } + return (error); +} + +static int +ugen_set_short_xfer(struct usb2_fifo *f, void *addr) +{ + uint8_t t; + + if (*(int *)addr) + t = 1; + else + t = 0; + + if (f->flag_short == t) { + /* same value like before - accept */ + return (0); + } + if (f->xfer[0] || f->xfer[1]) { + /* cannot change this during transfer */ + return (EBUSY); + } + f->flag_short = t; + return (0); +} + +static int +ugen_set_timeout(struct usb2_fifo *f, void *addr) +{ + f->timeout = *(int *)addr; + if (f->timeout > 65535) { + /* limit user input */ + f->timeout = 65535; + } + return (0); +} + +static int +ugen_get_frame_size(struct usb2_fifo *f, void *addr) +{ + if (f->xfer[0]) { + *(int *)addr = f->xfer[0]->max_frame_size; + } else { + return (EINVAL); + } + return (0); +} + +static int +ugen_set_buffer_size(struct usb2_fifo *f, void *addr) +{ + uint32_t t; + + if (*(int *)addr < 1024) + t = 1024; + else if (*(int *)addr < (256 * 1024)) + t = *(int *)addr; + else + t = 256 * 1024; + + if (f->bufsize == t) { + /* same value like before - accept */ + return (0); + } + if (f->xfer[0] || f->xfer[1]) { + /* cannot change this during transfer */ + return (EBUSY); + } + f->bufsize = t; + return (0); +} + +static int +ugen_get_buffer_size(struct usb2_fifo *f, void *addr) +{ + *(int *)addr = f->bufsize; + return (0); +} + +static int +ugen_get_iface_desc(struct usb2_fifo *f, + struct usb2_interface_descriptor *idesc) +{ + struct usb2_interface *iface; + + iface = usb2_get_iface(f->udev, f->iface_index); + if (iface && iface->idesc) { + *idesc = *(iface->idesc); + } else { + return (EIO); + } + return (0); +} + +static int +ugen_get_endpoint_desc(struct usb2_fifo *f, + struct usb2_endpoint_descriptor *ed) +{ + struct usb2_pipe *pipe; + + pipe = f->priv_sc0; + + if (pipe && pipe->edesc) { + *ed = *pipe->edesc; + } else { + return (EINVAL); + } + return (0); +} + +static int +ugen_set_power_mode(struct usb2_fifo *f, int mode) +{ + struct usb2_device *udev = f->udev; + int err; + + if ((udev == NULL) || + (udev->parent_hub == NULL)) { + return (EINVAL); + } + err = priv_check(curthread, PRIV_ROOT); + if (err) { + return (err); + } + switch (mode) { + case USB_POWER_MODE_OFF: + /* clear suspend */ + err = usb2_req_clear_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + if (err) + break; + + /* clear port enable */ + err = usb2_req_clear_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_ENABLE); + break; + + case USB_POWER_MODE_ON: + /* enable port */ + err = usb2_req_set_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_ENABLE); + + /* FALLTHROUGH */ + + case USB_POWER_MODE_SAVE: + case USB_POWER_MODE_RESUME: + /* TODO: implement USB power save */ + err = usb2_req_clear_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + break; + + case USB_POWER_MODE_SUSPEND: + /* TODO: implement USB power save */ + err = usb2_req_set_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + break; + default: + return (EINVAL); + } + + if (err) + return (ENXIO); /* I/O failure */ + + udev->power_mode = mode; /* update copy of power mode */ + + return (0); /* success */ +} + +static int +ugen_get_power_mode(struct usb2_fifo *f) +{ + struct usb2_device *udev = f->udev; + + if ((udev == NULL) || + (udev->parent_hub == NULL)) { + return (USB_POWER_MODE_ON); + } + return (udev->power_mode); +} + +static int +ugen_do_port_feature(struct usb2_fifo *f, uint8_t port_no, + uint8_t set, uint16_t feature) +{ + struct usb2_device *udev = f->udev; + struct usb2_hub *hub; + int err; + + err = priv_check(curthread, PRIV_ROOT); + if (err) { + return (err); + } + if (port_no == 0) { + return (EINVAL); + } + if ((udev == NULL) || + (udev->hub == NULL)) { + return (EINVAL); + } + hub = udev->hub; + + if (port_no > hub->nports) { + return (EINVAL); + } + if (set) + err = usb2_req_set_port_feature(udev, + NULL, port_no, feature); + else + err = usb2_req_clear_port_feature(udev, + NULL, port_no, feature); + + if (err) + return (ENXIO); /* failure */ + + return (0); /* success */ +} + +static int +ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags) +{ + struct usb2_fifo *f_rx; + struct usb2_fifo *f_tx; + int error = 0; + + f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; + f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; + + switch (cmd) { + case USB_SET_RX_SHORT_XFER: + if (fflags & FREAD) { + error = ugen_set_short_xfer(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_TX_FORCE_SHORT: + if (fflags & FWRITE) { + error = ugen_set_short_xfer(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_RX_TIMEOUT: + if (fflags & FREAD) { + error = ugen_set_timeout(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_TX_TIMEOUT: + if (fflags & FWRITE) { + error = ugen_set_timeout(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_RX_FRAME_SIZE: + if (fflags & FREAD) { + error = ugen_get_frame_size(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_TX_FRAME_SIZE: + if (fflags & FWRITE) { + error = ugen_get_frame_size(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_RX_BUFFER_SIZE: + if (fflags & FREAD) { + error = ugen_set_buffer_size(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_TX_BUFFER_SIZE: + if (fflags & FWRITE) { + error = ugen_set_buffer_size(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_RX_BUFFER_SIZE: + if (fflags & FREAD) { + error = ugen_get_buffer_size(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_TX_BUFFER_SIZE: + if (fflags & FWRITE) { + error = ugen_get_buffer_size(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_RX_INTERFACE_DESC: + if (fflags & FREAD) { + error = ugen_get_iface_desc(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_TX_INTERFACE_DESC: + if (fflags & FWRITE) { + error = ugen_get_iface_desc(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_RX_ENDPOINT_DESC: + if (fflags & FREAD) { + error = ugen_get_endpoint_desc(f_rx, addr); + } else { + error = EINVAL; + } + break; + + case USB_GET_TX_ENDPOINT_DESC: + if (fflags & FWRITE) { + error = ugen_get_endpoint_desc(f_tx, addr); + } else { + error = EINVAL; + } + break; + + case USB_SET_RX_STALL_FLAG: + if ((fflags & FREAD) && (*(int *)addr)) { + f_rx->flag_stall = 1; + } + break; + + case USB_SET_TX_STALL_FLAG: + if ((fflags & FWRITE) && (*(int *)addr)) { + f_tx->flag_stall = 1; + } + break; + + default: + error = ENOTTY; + break; + } + return (error); +} + +static int +ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags) +{ + union { + struct usb2_interface_descriptor *idesc; + struct usb2_alt_interface *ai; + struct usb2_device_descriptor *ddesc; + struct usb2_config_descriptor *cdesc; + struct usb2_device_stats *stat; + uint32_t *ptime; + void *addr; + int *pint; + } u; + struct usb2_device_descriptor *dtemp; + struct usb2_config_descriptor *ctemp; + struct usb2_interface *iface; + int error = 0; + uint8_t n; + + u.addr = addr; + + switch (cmd) { + case USB_DISCOVER: + usb2_needs_explore_all(); + break; + + case USB_SETDEBUG: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + usb2_debug = *(int *)addr; + break; + + case USB_GET_CONFIG: + *(int *)addr = f->udev->curr_config_index; + break; + + case USB_SET_CONFIG: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + error = ugen_set_config(f, *(int *)addr); + break; + + case USB_GET_ALTINTERFACE: + iface = usb2_get_iface(f->udev, + u.ai->uai_interface_index); + if (iface && iface->idesc) { + u.ai->uai_alt_index = iface->alt_index; + } else { + error = EINVAL; + } + break; + + case USB_SET_ALTINTERFACE: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + error = ugen_set_interface(f, + u.ai->uai_interface_index, u.ai->uai_alt_index); + break; + + case USB_GET_DEVICE_DESC: + dtemp = usb2_get_device_descriptor(f->udev); + if (!dtemp) { + error = EIO; + break; + } + *u.ddesc = *dtemp; + break; + + case USB_GET_CONFIG_DESC: + ctemp = usb2_get_config_descriptor(f->udev); + if (!ctemp) { + error = EIO; + break; + } + *u.cdesc = *ctemp; + break; + + case USB_GET_FULL_DESC: + error = ugen_get_cdesc(f, addr); + break; + + case USB_GET_STRING_DESC: + error = ugen_get_sdesc(f, addr); + break; + + case USB_REQUEST: + case USB_DO_REQUEST: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + error = ugen_do_request(f, addr); + break; + + case USB_DEVICEINFO: + case USB_GET_DEVICEINFO: + error = usb2_gen_fill_deviceinfo(f, addr); + break; + + case USB_GET_DEVICENAMES: + error = usb2_gen_fill_devicenames(f, addr); + break; + + case USB_DEVICESTATS: + for (n = 0; n != 4; n++) { + + u.stat->uds_requests_fail[n] = + f->udev->bus->stats_err.uds_requests[n]; + + u.stat->uds_requests_ok[n] = + f->udev->bus->stats_ok.uds_requests[n]; + } + break; + + case USB_DEVICEENUMERATE: + error = ugen_re_enumerate(f); + break; + + case USB_GET_PLUGTIME: + *u.ptime = f->udev->plugtime; + break; + + case USB_CLAIM_INTERFACE: + case USB_RELEASE_INTERFACE: + /* TODO */ + break; + + case USB_IFACE_DRIVER_ACTIVE: + /* TODO */ + *u.pint = 0; + break; + + case USB_IFACE_DRIVER_DETACH: + /* TODO */ + error = priv_check(curthread, PRIV_DRIVER); + if (error) { + break; + } + error = EINVAL; + break; + + case USB_SET_POWER_MODE: + error = ugen_set_power_mode(f, *u.pint); + break; + + case USB_GET_POWER_MODE: + *u.pint = ugen_get_power_mode(f); + break; + + case USB_SET_PORT_ENABLE: + error = ugen_do_port_feature(f, + *u.pint, 1, UHF_PORT_ENABLE); + break; + + case USB_SET_PORT_DISABLE: + error = ugen_do_port_feature(f, + *u.pint, 0, UHF_PORT_ENABLE); + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +static int +ugen_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags, + struct thread *td) +{ + int error; + + DPRINTFN(6, "cmd=%08lx\n", cmd); + error = ugen_fs_ioctl(f, cmd, addr, fflags); + if (error == ENOTTY) { + if (f->flag_no_uref) { + mtx_lock(f->priv_mtx); + error = ugen_iface_ioctl(f, cmd, addr, fflags); + mtx_unlock(f->priv_mtx); + } else { + error = ugen_ctrl_ioctl(f, cmd, addr, fflags); + } + } + DPRINTFN(6, "error=%d\n", error); + return (error); +} + +static void +ugen_default_fs_callback(struct usb2_xfer *xfer) +{ + ; /* workaround for a bug in "indent" */ + + DPRINTF("st=%u alen=%u aframes=%u\n", + USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + usb2_start_hardware(xfer); + break; + default: + ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); + break; + } + return; +} diff --git a/sys/dev/usb2/core/usb2_generic.h b/sys/dev/usb2/core/usb2_generic.h new file mode 100644 index 000000000000..3a4e7c940ea4 --- /dev/null +++ b/sys/dev/usb2/core/usb2_generic.h @@ -0,0 +1,33 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_GENERIC_H_ +#define _USB2_GENERIC_H_ + +extern struct usb2_fifo_methods usb2_ugen_methods; +int ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur); + +#endif /* _USB2_GENERIC_H_ */ diff --git a/sys/dev/usb2/core/usb2_handle_request.c b/sys/dev/usb2/core/usb2_handle_request.c new file mode 100644 index 000000000000..7dc82c072699 --- /dev/null +++ b/sys/dev/usb2/core/usb2_handle_request.c @@ -0,0 +1,750 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* enum */ + +enum { + ST_DATA, + ST_POST_STATUS, +}; + +/* function prototypes */ + +static uint8_t usb2_handle_get_stall(struct usb2_device *udev, uint8_t ea_val); +static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on); +static usb2_error_t usb2_handle_request(struct usb2_xfer *xfer); +static usb2_error_t usb2_handle_set_config(struct usb2_xfer *xfer, uint8_t conf_no); +static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *xfer, uint8_t ep, uint8_t do_stall); +static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *xfer, void **ppdata, uint16_t *plen, struct usb2_device_request req, uint16_t off, uint8_t state); + +/*------------------------------------------------------------------------* + * usb2_handle_request_callback + * + * This function is the USB callback for generic USB Device control + * transfers. + *------------------------------------------------------------------------*/ +void +usb2_handle_request_callback(struct usb2_xfer *xfer) +{ + usb2_error_t err; + + /* check the current transfer state */ + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + + /* handle the request */ + err = usb2_handle_request(xfer); + + if (err) { + + if (err == USB_ERR_BAD_CONTEXT) { + /* we need to re-setup the control transfer */ + usb2_needs_explore(xfer->udev->bus, 0); + break; + } + /* + * If no control transfer is active, + * receive the next SETUP message: + */ + goto tr_restart; + } + usb2_start_hardware(xfer); + break; + + default: + if (xfer->error != USB_ERR_CANCELLED) { + /* should not happen - try stalling */ + goto tr_restart; + } + break; + } + return; + +tr_restart: + xfer->frlengths[0] = sizeof(struct usb2_device_request); + xfer->nframes = 1; + xfer->flags.manual_status = 1; + xfer->flags.force_short_xfer = 0; + xfer->flags.stall_pipe = 1; /* cancel previous transfer, if any */ + usb2_start_hardware(xfer); + return; +} + +/*------------------------------------------------------------------------* + * usb2_handle_set_config + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_handle_set_config(struct usb2_xfer *xfer, uint8_t conf_no) +{ + usb2_error_t err = 0; + + /* + * We need to protect against other threads doing probe and + * attach: + */ + mtx_unlock(xfer->priv_mtx); + mtx_lock(&Giant); /* XXX */ + sx_xlock(xfer->udev->default_sx + 1); + + if (conf_no == USB_UNCONFIG_NO) { + conf_no = USB_UNCONFIG_INDEX; + } else { + /* + * The relationship between config number and config index + * is very simple in our case: + */ + conf_no--; + } + + if (usb2_set_config_index(xfer->udev, conf_no)) { + DPRINTF("set config %d failed\n", conf_no); + err = USB_ERR_STALLED; + goto done; + } + if (usb2_probe_and_attach(xfer->udev, USB_IFACE_INDEX_ANY)) { + DPRINTF("probe and attach failed\n"); + err = USB_ERR_STALLED; + goto done; + } +done: + mtx_unlock(&Giant); /* XXX */ + sx_unlock(xfer->udev->default_sx + 1); + mtx_lock(xfer->priv_mtx); + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_handle_iface_request + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_handle_iface_request(struct usb2_xfer *xfer, + void **ppdata, uint16_t *plen, + struct usb2_device_request req, uint16_t off, uint8_t state) +{ + struct usb2_interface *iface; + struct usb2_interface *iface_parent; /* parent interface */ + struct usb2_device *udev = xfer->udev; + int error; + uint8_t iface_index; + + if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { + iface_index = req.wIndex[0]; /* unicast */ + } else { + iface_index = 0; /* broadcast */ + } + + /* + * We need to protect against other threads doing probe and + * attach: + */ + mtx_unlock(xfer->priv_mtx); + mtx_lock(&Giant); /* XXX */ + sx_xlock(udev->default_sx + 1); + + error = ENXIO; + +tr_repeat: + iface = usb2_get_iface(udev, iface_index); + if ((iface == NULL) || + (iface->idesc == NULL)) { + /* end of interfaces non-existing interface */ + goto tr_stalled; + } + /* forward request to interface, if any */ + + if ((error != 0) && + (error != ENOTTY) && + (iface->subdev != NULL) && + device_is_attached(iface->subdev)) { +#if 0 + DEVMETHOD(usb2_handle_request, NULL); /* dummy */ +#endif + error = USB2_HANDLE_REQUEST(iface->subdev, + &req, ppdata, plen, + off, (state == ST_POST_STATUS)); + } + iface_parent = usb2_get_iface(udev, iface->parent_iface_index); + + if ((iface_parent == NULL) || + (iface_parent->idesc == NULL)) { + /* non-existing interface */ + iface_parent = NULL; + } + /* forward request to parent interface, if any */ + + if ((error != 0) && + (error != ENOTTY) && + (iface_parent != NULL) && + (iface_parent->subdev != NULL) && + ((req.bmRequestType & 0x1F) == UT_INTERFACE) && + (iface_parent->subdev != iface->subdev) && + device_is_attached(iface_parent->subdev)) { + error = USB2_HANDLE_REQUEST(iface_parent->subdev, + &req, ppdata, plen, off, + (state == ST_POST_STATUS)); + } + if (error == 0) { + /* negativly adjust pointer and length */ + *ppdata = ((uint8_t *)(*ppdata)) - off; + *plen += off; + goto tr_valid; + } else if (error == ENOTTY) { + goto tr_stalled; + } + if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { + iface_index++; /* iterate */ + goto tr_repeat; + } + if (state == ST_POST_STATUS) { + /* we are complete */ + goto tr_valid; + } + switch (req.bmRequestType) { + case UT_WRITE_INTERFACE: + switch (req.bRequest) { + case UR_SET_INTERFACE: + /* + * Handle special case. If we have parent interface + * we just reset the endpoints, because this is a + * multi interface device and re-attaching only a + * part of the device is not possible. Also if the + * alternate setting is the same like before we just + * reset the interface endoints. + */ + if ((iface_parent != NULL) || + (iface->alt_index == req.wValue[0])) { + error = usb2_reset_iface_endpoints(udev, + iface_index); + if (error) { + DPRINTF("alt setting failed %s\n", + usb2_errstr(error)); + goto tr_stalled; + } + break; + } + error = usb2_set_alt_interface_index(udev, + iface_index, req.wValue[0]); + if (error) { + DPRINTF("alt setting failed %s\n", + usb2_errstr(error)); + goto tr_stalled; + } + error = usb2_probe_and_attach(udev, + iface_index); + if (error) { + DPRINTF("alt setting probe failed\n"); + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + break; + + case UT_READ_INTERFACE: + switch (req.bRequest) { + case UR_GET_INTERFACE: + *ppdata = &iface->alt_index; + *plen = 1; + break; + + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } +tr_valid: + mtx_unlock(&Giant); + sx_unlock(udev->default_sx + 1); + mtx_lock(xfer->priv_mtx); + return (0); + +tr_stalled: + mtx_unlock(&Giant); + sx_unlock(udev->default_sx + 1); + mtx_lock(xfer->priv_mtx); + return (USB_ERR_STALLED); +} + +/*------------------------------------------------------------------------* + * usb2_handle_stall + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_handle_set_stall(struct usb2_xfer *xfer, uint8_t ep, uint8_t do_stall) +{ + usb2_error_t err; + + mtx_unlock(xfer->priv_mtx); + err = usb2_set_endpoint_stall(xfer->udev, + usb2_get_pipe_by_addr(xfer->udev, ep), do_stall); + mtx_lock(xfer->priv_mtx); + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_handle_get_stall + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +usb2_handle_get_stall(struct usb2_device *udev, uint8_t ea_val) +{ + struct usb2_pipe *pipe; + uint8_t halted; + + pipe = usb2_get_pipe_by_addr(udev, ea_val); + if (pipe == NULL) { + /* nothing to do */ + return (0); + } + mtx_lock(&udev->bus->mtx); + halted = pipe->is_stalled; + mtx_unlock(&udev->bus->mtx); + + return (halted); +} + +/*------------------------------------------------------------------------* + * usb2_handle_remote_wakeup + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on) +{ + struct usb2_device *udev; + struct usb2_bus *bus; + + udev = xfer->udev; + bus = udev->bus; + + mtx_lock(&bus->mtx); + + if (is_on) { + udev->flags.remote_wakeup = 1; + } else { + udev->flags.remote_wakeup = 0; + } + + (bus->methods->rem_wakeup_set) (xfer->udev, is_on); + + mtx_unlock(&bus->mtx); + + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_handle_request + * + * Internal state sequence: + * + * ST_DATA -> ST_POST_STATUS + * + * Returns: + * 0: Ready to start hardware + * Else: Stall current transfer, if any + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_handle_request(struct usb2_xfer *xfer) +{ + struct usb2_device_request req; + struct usb2_device *udev; + const void *src_zcopy; /* zero-copy source pointer */ + const void *src_mcopy; /* non zero-copy source pointer */ + uint16_t off; /* data offset */ + uint16_t rem; /* data remainder */ + uint16_t max_len; /* max fragment length */ + uint16_t wValue; + uint16_t wIndex; + uint8_t state; + usb2_error_t err; + union { + uWord wStatus; + uint8_t buf[2]; + } temp; + + /* + * Filter the USB transfer state into + * something which we understand: + */ + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + state = ST_DATA; + + if (!xfer->flags_int.control_act) { + /* nothing to do */ + goto tr_stalled; + } + break; + + default: /* USB_ST_TRANSFERRED */ + if (!xfer->flags_int.control_act) { + state = ST_POST_STATUS; + } else { + state = ST_DATA; + } + break; + } + + /* reset frame stuff */ + + xfer->frlengths[0] = 0; + + usb2_set_frame_offset(xfer, 0, 0); + usb2_set_frame_offset(xfer, sizeof(req), 1); + + /* get the current request, if any */ + + usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); + + if (xfer->flags_int.control_rem == 0xFFFF) { + /* first time - not initialised */ + rem = UGETW(req.wLength); + off = 0; + } else { + /* not first time - initialised */ + rem = xfer->flags_int.control_rem; + off = UGETW(req.wLength) - rem; + } + + /* set some defaults */ + + max_len = 0; + src_zcopy = NULL; + src_mcopy = NULL; + udev = xfer->udev; + + /* get some request fields decoded */ + + wValue = UGETW(req.wValue); + wIndex = UGETW(req.wIndex); + + DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " + "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, + req.bRequest, wValue, wIndex, off, rem, state); + + /* demultiplex the control request */ + + switch (req.bmRequestType) { + case UT_READ_DEVICE: + if (state != ST_DATA) { + break; + } + switch (req.bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_descriptor; + case UR_GET_CONFIG: + goto tr_handle_get_config; + case UR_GET_STATUS: + goto tr_handle_get_status; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_DEVICE: + switch (req.bRequest) { + case UR_SET_ADDRESS: + goto tr_handle_set_address; + case UR_SET_CONFIG: + goto tr_handle_set_config; + case UR_CLEAR_FEATURE: + switch (wValue) { + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_clear_wakeup; + default: + goto tr_stalled; + } + break; + case UR_SET_FEATURE: + switch (wValue) { + case UF_DEVICE_REMOTE_WAKEUP: + goto tr_handle_set_wakeup; + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + break; + + case UT_WRITE_ENDPOINT: + switch (req.bRequest) { + case UR_CLEAR_FEATURE: + switch (wValue) { + case UF_ENDPOINT_HALT: + goto tr_handle_clear_halt; + default: + goto tr_stalled; + } + break; + case UR_SET_FEATURE: + switch (wValue) { + case UF_ENDPOINT_HALT: + goto tr_handle_set_halt; + default: + goto tr_stalled; + } + break; + default: + goto tr_stalled; + } + break; + + case UT_READ_ENDPOINT: + switch (req.bRequest) { + case UR_GET_STATUS: + goto tr_handle_get_ep_status; + default: + goto tr_stalled; + } + break; + default: + /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ + err = usb2_handle_iface_request(xfer, + USB_ADD_BYTES(&src_zcopy, 0), + &max_len, req, off, state); + if (err == 0) { + goto tr_valid; + } + /* + * Reset zero-copy pointer and max length + * variable in case they were unintentionally + * set: + */ + src_zcopy = NULL; + max_len = 0; + + /* + * Check if we have a vendor specific + * descriptor: + */ + goto tr_handle_get_descriptor; + } + goto tr_valid; + +tr_handle_get_descriptor: + (usb2_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); + if (src_zcopy == NULL) { + goto tr_stalled; + } + goto tr_valid; + +tr_handle_get_config: + temp.buf[0] = udev->curr_config_no; + src_mcopy = temp.buf; + max_len = 1; + goto tr_valid; + +tr_handle_get_status: + + wValue = 0; + + mtx_lock(&udev->bus->mtx); + if (udev->flags.remote_wakeup) { + wValue |= UDS_REMOTE_WAKEUP; + } + if (udev->flags.self_powered) { + wValue |= UDS_SELF_POWERED; + } + mtx_unlock(&udev->bus->mtx); + + USETW(temp.wStatus, wValue); + src_mcopy = temp.wStatus; + max_len = sizeof(temp.wStatus); + goto tr_valid; + +tr_handle_set_address: + if (state == ST_DATA) { + if (wValue >= 0x80) { + /* invalid value */ + goto tr_stalled; + } else if (udev->curr_config_no != 0) { + /* we are configured ! */ + goto tr_stalled; + } + } else if (state == ST_POST_STATUS) { + udev->address = (wValue & 0x7F); + goto tr_bad_context; + } + goto tr_valid; + +tr_handle_set_config: + if (state == ST_DATA) { + if (usb2_handle_set_config(xfer, req.wValue[0])) { + goto tr_stalled; + } + } + goto tr_valid; + +tr_handle_clear_halt: + if (state == ST_DATA) { + if (usb2_handle_set_stall(xfer, req.wIndex[0], 0)) { + goto tr_stalled; + } + } + goto tr_valid; + +tr_handle_clear_wakeup: + if (state == ST_DATA) { + if (usb2_handle_remote_wakeup(xfer, 0)) { + goto tr_stalled; + } + } + goto tr_valid; + +tr_handle_set_halt: + if (state == ST_DATA) { + if (usb2_handle_set_stall(xfer, req.wIndex[0], 1)) { + goto tr_stalled; + } + } + goto tr_valid; + +tr_handle_set_wakeup: + if (state == ST_DATA) { + if (usb2_handle_remote_wakeup(xfer, 1)) { + goto tr_stalled; + } + } + goto tr_valid; + +tr_handle_get_ep_status: + if (state == ST_DATA) { + temp.wStatus[0] = + usb2_handle_get_stall(udev, req.wIndex[0]); + temp.wStatus[1] = 0; + src_mcopy = temp.wStatus; + max_len = sizeof(temp.wStatus); + } + goto tr_valid; + +tr_valid: + if (state == ST_POST_STATUS) { + goto tr_stalled; + } + /* subtract offset from length */ + + max_len -= off; + + /* Compute the real maximum data length */ + + if (max_len > xfer->max_data_length) { + max_len = xfer->max_data_length; + } + if (max_len > rem) { + max_len = rem; + } + /* + * If the remainder is greater than the maximum data length, + * we need to truncate the value for the sake of the + * comparison below: + */ + if (rem > xfer->max_data_length) { + rem = xfer->max_data_length; + } + if (rem != max_len) { + /* + * If we don't transfer the data we can transfer, then + * the transfer is short ! + */ + xfer->flags.force_short_xfer = 1; + xfer->nframes = 2; + } else { + /* + * Default case + */ + xfer->flags.force_short_xfer = 0; + xfer->nframes = max_len ? 2 : 1; + } + if (max_len > 0) { + if (src_mcopy) { + src_mcopy = USB_ADD_BYTES(src_mcopy, off); + usb2_copy_in(xfer->frbuffers + 1, 0, + src_mcopy, max_len); + } else { + usb2_set_frame_data(xfer, + USB_ADD_BYTES(src_zcopy, off), 1); + } + xfer->frlengths[1] = max_len; + } else { + /* the end is reached, send status */ + xfer->flags.manual_status = 0; + xfer->frlengths[1] = 0; + } + DPRINTF("success\n"); + return (0); /* success */ + +tr_stalled: + DPRINTF("%s\n", (state == ST_POST_STATUS) ? + "complete" : "stalled"); + return (USB_ERR_STALLED); + +tr_bad_context: + DPRINTF("bad context\n"); + return (USB_ERR_BAD_CONTEXT); +} diff --git a/sys/dev/usb2/core/usb2_handle_request.h b/sys/dev/usb2/core/usb2_handle_request.h new file mode 100644 index 000000000000..6cc050321371 --- /dev/null +++ b/sys/dev/usb2/core/usb2_handle_request.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_HANDLE_REQUEST_H_ +#define _USB2_HANDLE_REQUEST_H_ + +#endif /* _USB2_HANDLE_REQUEST_H_ */ diff --git a/sys/dev/usb2/core/usb2_hid.c b/sys/dev/usb2/core/usb2_hid.c new file mode 100644 index 000000000000..04d7d4d0c0d7 --- /dev/null +++ b/sys/dev/usb2/core/usb2_hid.c @@ -0,0 +1,582 @@ +/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ + + +#include +__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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include + +static void hid_clear_local(struct hid_item *); + +#define MAXUSAGE 100 +struct hid_data { + const uint8_t *start; + const uint8_t *end; + const uint8_t *p; + struct hid_item cur; + int32_t usages[MAXUSAGE]; + int nu; + int minset; + int multi; + int multimax; + int kindset; +}; + +/*------------------------------------------------------------------------* + * hid_clear_local + *------------------------------------------------------------------------*/ +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; +} + +/*------------------------------------------------------------------------* + * hid_start_parse + *------------------------------------------------------------------------*/ +struct hid_data * +hid_start_parse(const 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 = ((const uint8_t *)d) + len; + s->kindset = kindset; + return (s); +} + +/*------------------------------------------------------------------------* + * hid_end_parse + *------------------------------------------------------------------------*/ +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); +} + +/*------------------------------------------------------------------------* + * hid_get_item + *------------------------------------------------------------------------*/ +int +hid_get_item(struct hid_data *s, struct hid_item *h) +{ + struct hid_item *c = &s->cur; + unsigned int bTag, bType, bSize; + uint32_t oldpos; + const uint8_t *data; + int32_t dval; + const uint8_t *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; + } + } +} + +/*------------------------------------------------------------------------* + * hid_report_size + *------------------------------------------------------------------------*/ +int +hid_report_size(const void *buf, int len, enum hid_kind k, uint8_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); +} + +/*------------------------------------------------------------------------* + * hid_locate + *------------------------------------------------------------------------*/ +int +hid_locate(const void *desc, int size, uint32_t u, enum hid_kind k, + struct hid_location *loc, uint32_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); +} + +/*------------------------------------------------------------------------* + * hid_get_data + *------------------------------------------------------------------------*/ +uint32_t +hid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc) +{ + uint32_t hpos = loc->pos; + uint32_t hsize = loc->size; + uint32_t data; + int i, s, t; + + DPRINTFN(11, "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) { + t = (i / 8); + if (t < len) { + data |= buf[t] << ((t - s) * 8); + } + } + data >>= hpos % 8; + data &= (1 << hsize) - 1; + hsize = 32 - hsize; + /* Sign extend */ + data = ((int32_t)data << hsize) >> hsize; + DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", + loc->pos, loc->size, (long)data); + return (data); +} + +/*------------------------------------------------------------------------* + * hid_is_collection + *------------------------------------------------------------------------*/ +int +hid_is_collection(const void *desc, int size, uint32_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); +} + +/*------------------------------------------------------------------------* + * hid_get_descriptor_from_usb + * + * This function will search for a HID descriptor between two USB + * interface descriptors. + * + * Return values: + * NULL: No more HID descriptors. + * Else: Pointer to HID descriptor. + *------------------------------------------------------------------------*/ +struct usb2_hid_descriptor * +hid_get_descriptor_from_usb(struct usb2_config_descriptor *cd, + struct usb2_interface_descriptor *id) +{ + struct usb2_descriptor *desc = (void *)id; + + if (desc == NULL) { + return (NULL); + } + while ((desc = usb2_desc_foreach(cd, desc))) { + if ((desc->bDescriptorType == UDESC_HID) && + (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { + return (void *)desc; + } + if (desc->bDescriptorType == UDESC_INTERFACE) { + break; + } + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_hid_desc + * + * This function will read out an USB report descriptor from the USB + * device. + * + * Return values: + * NULL: Failure. + * Else: Success. The pointer should eventually be passed to free(). + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_hid_desc(struct usb2_device *udev, struct mtx *mtx, + void **descp, uint16_t *sizep, + usb2_malloc_type mem, uint8_t iface_index) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_hid_descriptor *hid; + usb2_error_t err; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + hid = hid_get_descriptor_from_usb + (usb2_get_config_descriptor(udev), iface->idesc); + + if (hid == NULL) { + return (USB_ERR_IOERROR); + } + *sizep = UGETW(hid->descrs[0].wDescriptorLength); + if (*sizep == 0) { + return (USB_ERR_IOERROR); + } + if (mtx) + mtx_unlock(mtx); + + *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); + + if (mtx) + mtx_lock(mtx); + + if (*descp == NULL) { + return (USB_ERR_NOMEM); + } + err = usb2_req_get_report_descriptor + (udev, mtx, *descp, *sizep, iface_index); + + if (err) { + free(*descp, mem); + *descp = NULL; + return (err); + } + return (USB_ERR_NORMAL_COMPLETION); +} diff --git a/sys/dev/usb2/core/usb2_hid.h b/sys/dev/usb2/core/usb2_hid.h new file mode 100644 index 000000000000..9809cde9bb3b --- /dev/null +++ b/sys/dev/usb2/core/usb2_hid.h @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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. + */ + +#ifndef _USB2_CORE_HID_H_ +#define _USB2_CORE_HID_H_ + +struct usb2_hid_descriptor; +struct usb2_config_descriptor; + +enum hid_kind { + hid_input, hid_output, hid_feature, hid_collection, hid_endcollection +}; + +struct hid_location { + uint32_t size; + uint32_t count; + uint32_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; + uint32_t flags; + /* Location */ + struct hid_location loc; + /* */ + struct hid_item *next; +}; + +/* prototypes from "usb2_hid.c" */ + +struct hid_data *hid_start_parse(const 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(const void *buf, int len, enum hid_kind k, uint8_t *id); +int hid_locate(const void *desc, int size, uint32_t usage, enum hid_kind kind, struct hid_location *loc, uint32_t *flags); +uint32_t hid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc); +int hid_is_collection(const void *desc, int size, uint32_t usage); +struct usb2_hid_descriptor *hid_get_descriptor_from_usb(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id); +usb2_error_t usb2_req_get_hid_desc(struct usb2_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, usb2_malloc_type mem, uint8_t iface_index); + +#endif /* _USB2_CORE_HID_H_ */ diff --git a/sys/dev/usb2/core/usb2_hub.c b/sys/dev/usb2/core/usb2_hub.c new file mode 100644 index 000000000000..b36085583a63 --- /dev/null +++ b/sys/dev/usb2/core/usb2_hub.c @@ -0,0 +1,1330 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. All rights reserved. + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +/* + * USB spec: http://www.usb.org/developers/docs/usbspec.zip + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR uhub_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define UHUB_INTR_INTERVAL 250 /* ms */ + +#if USB_DEBUG +static int uhub_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB"); +SYSCTL_INT(_hw_usb2_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0, + "Debug level"); +#endif + +struct uhub_current_state { + uint16_t port_change; + uint16_t port_status; +}; + +struct uhub_softc { + struct uhub_current_state sc_st;/* current state */ + device_t sc_dev; /* base device */ + struct usb2_device *sc_udev; /* USB device */ + struct usb2_xfer *sc_xfer[2]; /* interrupt xfer */ + uint8_t sc_flags; +#define UHUB_FLAG_INTR_STALL 0x02 + char sc_name[32]; +}; + +#define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) +#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) +#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) + +/* prototypes for type checking: */ + +static device_probe_t uhub_probe; +static device_attach_t uhub_attach; +static device_detach_t uhub_detach; + +static bus_driver_added_t uhub_driver_added; +static bus_child_location_str_t uhub_child_location_string; +static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; + +static usb2_callback_t uhub_intr_callback; +static usb2_callback_t uhub_intr_clear_stall_callback; + +static const struct usb2_config uhub_config[2] = { + + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_ANY, + .mh.timeout = 0, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &uhub_intr_callback, + .mh.interval = UHUB_INTR_INTERVAL, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0, + .direction = UE_DIR_ANY, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + .mh.flags = {}, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uhub_intr_clear_stall_callback, + }, +}; + +/* + * driver instance for "hub" connected to "usb" + * and "hub" connected to "hub" + */ +static devclass_t uhub_devclass; + +static driver_t uhub_driver = +{ + .name = "ushub", + .methods = (device_method_t[]){ + DEVMETHOD(device_probe, uhub_probe), + 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_string), + DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string), + DEVMETHOD(bus_driver_added, uhub_driver_added), + {0, 0} + }, + .size = sizeof(struct uhub_softc) +}; + +DRIVER_MODULE(ushub, usbus, uhub_driver, uhub_devclass, 0, 0); +DRIVER_MODULE(ushub, ushub, uhub_driver, uhub_devclass, NULL, 0); + +static void +uhub_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uhub_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UHUB_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uhub_intr_callback(struct usb2_xfer *xfer) +{ + struct uhub_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(2, "\n"); + /* + * This is an indication that some port + * has changed status. Notify the bus + * event handler thread that we need + * to be explored again: + */ + usb2_needs_explore(sc->sc_udev->bus, 0); + + case USB_ST_SETUP: + if (sc->sc_flags & UHUB_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[1]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= UHUB_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[1]); + } + return; + } +} + +/*------------------------------------------------------------------------* + * uhub_explore_sub - subroutine + * + * Return values: + * 0: Success + * Else: A control transaction failed + *------------------------------------------------------------------------*/ +static usb2_error_t +uhub_explore_sub(struct uhub_softc *sc, struct usb2_port *up) +{ + struct usb2_bus *bus; + struct usb2_device *child; + uint8_t refcount; + usb2_error_t err; + + bus = sc->sc_udev->bus; + err = 0; + + /* get driver added refcount from USB bus */ + refcount = bus->driver_added_refcount; + + /* get device assosiated with the given port */ + child = usb2_bus_port_get_device(bus, up); + if (child == NULL) { + /* nothing to do */ + goto done; + } + /* check if probe and attach should be done */ + + if (child->driver_added_refcount != refcount) { + child->driver_added_refcount = refcount; + err = usb2_probe_and_attach(child, + USB_IFACE_INDEX_ANY); + if (err) { + goto done; + } + } + /* start control transfer, if device mode */ + + if (child->flags.usb2_mode == USB_MODE_DEVICE) { + usb2_default_transfer_setup(child); + } + /* if a HUB becomes present, do a recursive HUB explore */ + + if (child->hub) { + err = (child->hub->explore) (child); + } +done: + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_read_port_status - factored out code + *------------------------------------------------------------------------*/ +static usb2_error_t +uhub_read_port_status(struct uhub_softc *sc, uint8_t portno) +{ + struct usb2_port_status ps; + usb2_error_t err; + + err = usb2_req_get_port_status( + sc->sc_udev, &Giant, &ps, portno); + + /* update status regardless of error */ + + sc->sc_st.port_status = UGETW(ps.wPortStatus); + sc->sc_st.port_change = UGETW(ps.wPortChange); + + /* debugging print */ + + DPRINTFN(4, "port %d, wPortStatus=0x%04x, " + "wPortChange=0x%04x, err=%s\n", + portno, sc->sc_st.port_status, + sc->sc_st.port_change, usb2_errstr(err)); + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_reattach_port + * + * Returns: + * 0: Success + * Else: A control transaction failed + *------------------------------------------------------------------------*/ +static usb2_error_t +uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) +{ + struct usb2_device *child; + struct usb2_device *udev; + usb2_error_t err; + uint8_t timeout; + uint8_t speed; + uint8_t usb2_mode; + + DPRINTF("reattaching port %d\n", portno); + + err = 0; + timeout = 0; + udev = sc->sc_udev; + child = usb2_bus_port_get_device(udev->bus, + udev->hub->ports + portno - 1); + +repeat: + + /* first clear the port connection change bit */ + + err = usb2_req_clear_port_feature + (udev, &Giant, portno, UHF_C_PORT_CONNECTION); + + if (err) { + goto error; + } + /* detach any existing devices */ + + if (child) { + usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1); + usb2_free_device(child); + child = NULL; + } + /* get fresh status */ + + err = uhub_read_port_status(sc, portno); + if (err) { + goto error; + } + /* check if nothing is connected to the port */ + + if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) { + goto error; + } + /* check if there is no power on the port and print a warning */ + + if (!(sc->sc_st.port_status & UPS_PORT_POWER)) { + DPRINTF("WARNING: strange, connected port %d " + "has no power\n", portno); + } + /* check if the device is in Host Mode */ + + if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { + + DPRINTF("Port %d is in Host Mode\n", portno); + + /* USB Host Mode */ + + /* wait for maximum device power up time */ + + usb2_pause_mtx(&Giant, USB_PORT_POWERUP_DELAY); + + /* reset port, which implies enabling it */ + + err = usb2_req_reset_port + (udev, &Giant, portno); + + if (err) { + DPRINTFN(0, "port %d reset " + "failed, error=%s\n", + portno, usb2_errstr(err)); + goto error; + } + /* get port status again, it might have changed during reset */ + + err = uhub_read_port_status(sc, portno); + if (err) { + goto error; + } + /* check if something changed during port reset */ + + if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) || + (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) { + if (timeout) { + DPRINTFN(0, "giving up port reset " + "- device vanished!\n"); + goto error; + } + timeout = 1; + goto repeat; + } + } else { + DPRINTF("Port %d is in Device Mode\n", portno); + } + + /* + * Figure out the device speed + */ + speed = + (sc->sc_st.port_status & UPS_HIGH_SPEED) ? USB_SPEED_HIGH : + (sc->sc_st.port_status & UPS_LOW_SPEED) ? USB_SPEED_LOW : USB_SPEED_FULL; + + /* + * Figure out the device mode + * + * NOTE: This part is currently FreeBSD specific. + */ + usb2_mode = + (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) ? + USB_MODE_DEVICE : USB_MODE_HOST; + + /* need to create a new child */ + + child = usb2_alloc_device(sc->sc_dev, udev->bus, udev, + udev->depth + 1, portno - 1, portno, speed, usb2_mode); + if (child == NULL) { + DPRINTFN(0, "could not allocate new device!\n"); + goto error; + } + return (0); /* success */ + +error: + if (child) { + usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1); + usb2_free_device(child); + child = NULL; + } + if (err == 0) { + if (sc->sc_st.port_status & UPS_PORT_ENABLED) { + err = usb2_req_clear_port_feature + (sc->sc_udev, &Giant, + portno, UHF_PORT_ENABLE); + } + } + if (err) { + DPRINTFN(0, "device problem (%s), " + "disabling port %d\n", usb2_errstr(err), portno); + } + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_suspend_resume_port + * + * Returns: + * 0: Success + * Else: A control transaction failed + *------------------------------------------------------------------------*/ +static usb2_error_t +uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) +{ + struct usb2_device *child; + struct usb2_device *udev; + uint8_t is_suspend; + usb2_error_t err; + + DPRINTF("port %d\n", portno); + + udev = sc->sc_udev; + child = usb2_bus_port_get_device(udev->bus, + udev->hub->ports + portno - 1); + + /* first clear the port suspend change bit */ + + err = usb2_req_clear_port_feature + (udev, &Giant, portno, UHF_C_PORT_SUSPEND); + + if (err) { + goto done; + } + /* get fresh status */ + + err = uhub_read_port_status(sc, portno); + if (err) { + goto done; + } + /* get current state */ + + if (sc->sc_st.port_status & UPS_SUSPEND) { + is_suspend = 1; + } else { + is_suspend = 0; + } + /* do the suspend or resume */ + + if (child) { + sx_xlock(child->default_sx + 1); + err = usb2_suspend_resume(child, is_suspend); + sx_unlock(child->default_sx + 1); + } +done: + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_explore + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +uhub_explore(struct usb2_device *udev) +{ + struct usb2_hub *hub; + struct uhub_softc *sc; + struct usb2_port *up; + usb2_error_t err; + uint8_t portno; + uint8_t x; + + hub = udev->hub; + sc = hub->hubsoftc; + + DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address); + + /* ignore hubs that are too deep */ + if (udev->depth > USB_HUB_MAX_DEPTH) { + return (USB_ERR_TOO_DEEP); + } + for (x = 0; x != hub->nports; x++) { + up = hub->ports + x; + portno = x + 1; + + err = uhub_read_port_status(sc, portno); + if (err) { + /* most likely the HUB is gone */ + break; + } + if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) { + err = usb2_req_clear_port_feature( + udev, &Giant, portno, UHF_C_PORT_ENABLE); + if (err) { + /* most likely the HUB is gone */ + break; + } + if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { + /* + * Ignore the port error if the device + * has vanished ! + */ + } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) { + DPRINTFN(0, "illegal enable change, " + "port %d\n", portno); + } else { + + if (up->restartcnt == USB_RESTART_MAX) { + /* XXX could try another speed ? */ + DPRINTFN(0, "port error, giving up " + "port %d\n", portno); + } else { + sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; + up->restartcnt++; + } + } + } + if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { + err = uhub_reattach_port(sc, portno); + if (err) { + /* most likely the HUB is gone */ + break; + } + } + if (sc->sc_st.port_change & UPS_C_SUSPEND) { + err = uhub_suspend_resume_port(sc, portno); + if (err) { + /* most likely the HUB is gone */ + break; + } + } + err = uhub_explore_sub(sc, up); + if (err) { + /* no device(s) present */ + continue; + } + /* explore succeeded - reset restart counter */ + up->restartcnt = 0; + } + return (USB_ERR_NORMAL_COMPLETION); +} + +static int +uhub_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* + * The subclass for USB HUBs is ignored because it is 0 for + * some and 1 for others. + */ + if ((uaa->info.bConfigIndex == 0) && + (uaa->info.bDeviceClass == UDCLASS_HUB)) { + return (0); + } + return (ENXIO); +} + +static int +uhub_attach(device_t dev) +{ + struct uhub_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_device *udev = uaa->device; + struct usb2_device *parent_hub = udev->parent_hub; + struct usb2_hub *hub; + struct usb2_hub_descriptor hubdesc; + uint16_t pwrdly; + uint8_t x; + uint8_t nports; + uint8_t portno; + uint8_t removable; + uint8_t iface_index; + usb2_error_t err; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = udev; + sc->sc_dev = dev; + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + device_set_usb2_desc(dev); + + DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, " + "parent->selfpowered=%d\n", + udev->depth, + udev->flags.self_powered, + parent_hub, + parent_hub ? + parent_hub->flags.self_powered : 0); + + if (udev->depth > USB_HUB_MAX_DEPTH) { + DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored!\n", + USB_HUB_MAX_DEPTH); + goto error; + } + if (!udev->flags.self_powered && parent_hub && + (!parent_hub->flags.self_powered)) { + DPRINTFN(0, "bus powered HUB connected to " + "bus powered HUB. HUB ignored!\n"); + goto error; + } + /* get HUB descriptor */ + + DPRINTFN(2, "getting HUB descriptor\n"); + + /* assuming that there is one port */ + err = usb2_req_get_hub_descriptor(udev, &Giant, &hubdesc, 1); + + nports = hubdesc.bNbrPorts; + + if (!err && (nports >= 8)) { + /* get complete HUB descriptor */ + err = usb2_req_get_hub_descriptor(udev, &Giant, &hubdesc, nports); + } + if (err) { + DPRINTFN(0, "getting hub descriptor failed," + "error=%s\n", usb2_errstr(err)); + goto error; + } + if (hubdesc.bNbrPorts != nports) { + DPRINTFN(0, "number of ports changed!\n"); + goto error; + } + if (nports == 0) { + DPRINTFN(0, "portless HUB!\n"); + goto error; + } + hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports), + M_USBDEV, M_WAITOK | M_ZERO); + + if (hub == NULL) { + goto error; + } + udev->hub = hub; + + /* init FULL-speed ISOCHRONOUS schedule */ + usb2_fs_isoc_schedule_init_all(hub->fs_isoc_schedule); + + /* initialize HUB structure */ + hub->hubsoftc = sc; + hub->explore = &uhub_explore; + hub->nports = hubdesc.bNbrPorts; + hub->hubudev = udev; + + /* if self powered hub, give ports maximum current */ + if (udev->flags.self_powered) { + hub->portpower = USB_MAX_POWER; + } else { + hub->portpower = USB_MIN_POWER; + } + + /* set up interrupt pipe */ + iface_index = 0; + err = usb2_transfer_setup(udev, &iface_index, sc->sc_xfer, + uhub_config, 2, sc, &Giant); + if (err) { + DPRINTFN(0, "cannot setup interrupt transfer, " + "errstr=%s!\n", usb2_errstr(err)); + goto error; + } + /* wait with power off for a while */ + usb2_pause_mtx(&Giant, USB_POWER_DOWN_TIME); + + /* + * 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 + */ + + /* XXX should check for none, individual, or ganged power? */ + + removable = 0; + pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + + USB_EXTRA_POWER_UP_TIME); + + for (x = 0; x != nports; x++) { + /* set up data structures */ + struct usb2_port *up = hub->ports + x; + + up->device_index = 0; + up->restartcnt = 0; + portno = x + 1; + + /* check if port is removable */ + if (!UHD_NOT_REMOV(&hubdesc, portno)) { + removable++; + } + if (!err) { + /* turn the power on */ + err = usb2_req_set_port_feature + (udev, &Giant, portno, UHF_PORT_POWER); + } + if (err) { + DPRINTFN(0, "port %d power on failed, %s\n", + portno, usb2_errstr(err)); + } + DPRINTF("turn on port %d power\n", + portno); + + /* wait for stable power */ + usb2_pause_mtx(&Giant, pwrdly); + } + + device_printf(dev, "%d port%s with %d " + "removable, %s powered\n", nports, (nports != 1) ? "s" : "", + removable, udev->flags.self_powered ? "self" : "bus"); + + /* start the interrupt endpoint */ + + mtx_lock(sc->sc_xfer[0]->priv_mtx); + usb2_transfer_start(sc->sc_xfer[0]); + mtx_unlock(sc->sc_xfer[0]->priv_mtx); + + return (0); + +error: + usb2_transfer_unsetup(sc->sc_xfer, 2); + + if (udev->hub) { + free(udev->hub, M_USBDEV); + udev->hub = NULL; + } + return (ENXIO); +} + +/* + * Called from process context when the hub is gone. + * Detach all devices on active ports. + */ +static int +uhub_detach(device_t dev) +{ + struct uhub_softc *sc = device_get_softc(dev); + struct usb2_hub *hub = sc->sc_udev->hub; + struct usb2_device *child; + uint8_t x; + + /* detach all children first */ + bus_generic_detach(dev); + + if (hub == NULL) { /* must be partially working */ + return (0); + } + for (x = 0; x != hub->nports; x++) { + + child = usb2_bus_port_get_device(sc->sc_udev->bus, hub->ports + x); + + if (child == NULL) { + continue; + } + /* + * Subdevices are not freed, because the caller of + * uhub_detach() will do that. + */ + usb2_detach_device(child, USB_IFACE_INDEX_ANY, 0); + usb2_free_device(child); + child = NULL; + } + + usb2_transfer_unsetup(sc->sc_xfer, 2); + + free(hub, M_USBDEV); + sc->sc_udev->hub = NULL; + return (0); +} + +static void +uhub_driver_added(device_t dev, driver_t *driver) +{ + usb2_needs_explore_all(); + return; +} + +struct hub_result { + struct usb2_device *udev; + uint8_t portno; + uint8_t iface_index; +}; + +static void +uhub_find_iface_index(struct usb2_hub *hub, device_t child, + struct hub_result *res) +{ + struct usb2_interface *iface; + struct usb2_device *udev; + uint8_t nports; + uint8_t x; + uint8_t i; + + nports = hub->nports; + for (x = 0; x != nports; x++) { + udev = usb2_bus_port_get_device(hub->hubudev->bus, + hub->ports + x); + if (!udev) { + continue; + } + for (i = 0; i != USB_IFACE_MAX; i++) { + iface = usb2_get_iface(udev, i); + if (iface && + (iface->subdev == child)) { + res->iface_index = i; + res->udev = udev; + res->portno = x + 1; + return; + } + } + } + res->iface_index = 0; + res->udev = NULL; + res->portno = 0; + return; +} + +static int +uhub_child_location_string(device_t parent, device_t child, + char *buf, size_t buflen) +{ + struct uhub_softc *sc = device_get_softc(parent); + struct usb2_hub *hub = sc->sc_udev->hub; + struct hub_result res; + + mtx_lock(&Giant); + uhub_find_iface_index(hub, child, &res); + if (!res.udev) { + DPRINTF("device not on hub\n"); + if (buflen) { + buf[0] = '\0'; + } + goto done; + } + snprintf(buf, buflen, "port=%u interface=%u", + res.portno, res.iface_index); +done: + mtx_unlock(&Giant); + + return (0); +} + +static int +uhub_child_pnpinfo_string(device_t parent, device_t child, + char *buf, size_t buflen) +{ + struct uhub_softc *sc = device_get_softc(parent); + struct usb2_hub *hub = sc->sc_udev->hub; + struct usb2_interface *iface; + struct hub_result res; + + mtx_lock(&Giant); + uhub_find_iface_index(hub, child, &res); + if (!res.udev) { + DPRINTF("device not on hub\n"); + if (buflen) { + buf[0] = '\0'; + } + goto done; + } + iface = usb2_get_iface(res.udev, res.iface_index); + if (iface && iface->idesc) { + snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " + "devclass=0x%02x devsubclass=0x%02x " + "sernum=\"%s\" " + "intclass=0x%02x intsubclass=0x%02x", + UGETW(res.udev->ddesc.idVendor), + UGETW(res.udev->ddesc.idProduct), + res.udev->ddesc.bDeviceClass, + res.udev->ddesc.bDeviceSubClass, + res.udev->serial, + iface->idesc->bInterfaceClass, + iface->idesc->bInterfaceSubClass); + } else { + if (buflen) { + buf[0] = '\0'; + } + goto done; + } +done: + mtx_unlock(&Giant); + + return (0); +} + +/* + * The USB Transaction Translator: + * =============================== + * + * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed + * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT + * USB transfers. To utilize bandwidth dynamically the "scatter and + * gather" principle must be applied. This means that bandwidth must + * be divided into equal parts of bandwidth. With regard to USB all + * data is transferred in smaller packets with length + * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is + * not a constant! + * + * The bandwidth scheduler which I have implemented will simply pack + * the USB transfers back to back until there is no more space in the + * schedule. Out of the 8 microframes which the USB 2.0 standard + * provides, only 6 are available for non-HIGH-speed devices. I have + * reserved the first 4 microframes for ISOCHRONOUS transfers. The + * last 2 microframes I have reserved for INTERRUPT transfers. Without + * this division, it is very difficult to allocate and free bandwidth + * dynamically. + * + * NOTE about the Transaction Translator in USB HUBs: + * + * USB HUBs have a very simple Transaction Translator, that will + * simply pipeline all the SPLIT transactions. That means that the + * transactions will be executed in the order they are queued! + * + */ + +/*------------------------------------------------------------------------* + * usb2_intr_find_best_slot + * + * Return value: + * The best Transaction Translation slot for an interrupt endpoint. + *------------------------------------------------------------------------*/ +static uint8_t +usb2_intr_find_best_slot(uint32_t *ptr, uint8_t start, uint8_t end) +{ + uint32_t max = 0xffffffff; + uint8_t x; + uint8_t y; + + y = 0; + + /* find the last slot with lesser used bandwidth */ + + for (x = start; x < end; x++) { + if (max >= ptr[x]) { + max = ptr[x]; + y = x; + } + } + return (y); +} + +/*------------------------------------------------------------------------* + * usb2_intr_schedule_adjust + * + * This function will update the bandwith usage for the microframe + * having index "slot" by "len" bytes. "len" can be negative. If the + * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX" + * the "slot" argument will be replaced by the slot having least used + * bandwidth. + * + * Returns: + * The slot on which the bandwidth update was done. + *------------------------------------------------------------------------*/ +uint8_t +usb2_intr_schedule_adjust(struct usb2_device *udev, int16_t len, uint8_t slot) +{ + struct usb2_bus *bus = udev->bus; + struct usb2_hub *hub; + + mtx_assert(&bus->mtx, MA_OWNED); + + if (usb2_get_speed(udev) == USB_SPEED_HIGH) { + if (slot >= USB_HS_MICRO_FRAMES_MAX) { + slot = usb2_intr_find_best_slot(bus->uframe_usage, 0, + USB_HS_MICRO_FRAMES_MAX); + } + bus->uframe_usage[slot] += len; + } else { + if (usb2_get_speed(udev) == USB_SPEED_LOW) { + len *= 8; + } + /* + * The Host Controller Driver should have + * performed checks so that the lookup + * below does not result in a NULL pointer + * access. + */ + + hub = bus->devices[udev->hs_hub_addr]->hub; + if (slot >= USB_HS_MICRO_FRAMES_MAX) { + slot = usb2_intr_find_best_slot(hub->uframe_usage, + USB_FS_ISOC_UFRAME_MAX, 6); + } + hub->uframe_usage[slot] += len; + bus->uframe_usage[slot] += len; + } + return (slot); +} + +/*------------------------------------------------------------------------* + * usb2_fs_isoc_schedule_init_sub + * + * This function initialises an USB FULL speed isochronous schedule + * entry. + *------------------------------------------------------------------------*/ +static void +usb2_fs_isoc_schedule_init_sub(struct usb2_fs_isoc_schedule *fss) +{ + fss->total_bytes = (USB_FS_ISOC_UFRAME_MAX * + USB_FS_BYTES_PER_HS_UFRAME); + fss->frame_bytes = (USB_FS_BYTES_PER_HS_UFRAME); + fss->frame_slot = 0; + return; +} + +/*------------------------------------------------------------------------* + * usb2_fs_isoc_schedule_init_all + * + * This function will reset the complete USB FULL speed isochronous + * bandwidth schedule. + *------------------------------------------------------------------------*/ +void +usb2_fs_isoc_schedule_init_all(struct usb2_fs_isoc_schedule *fss) +{ + struct usb2_fs_isoc_schedule *fss_end = fss + USB_ISOC_TIME_MAX; + + while (fss != fss_end) { + usb2_fs_isoc_schedule_init_sub(fss); + fss++; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_isoc_time_expand + * + * This function will expand the time counter from 7-bit to 16-bit. + * + * Returns: + * 16-bit isochronous time counter. + *------------------------------------------------------------------------*/ +uint16_t +usb2_isoc_time_expand(struct usb2_bus *bus, uint16_t isoc_time_curr) +{ + uint16_t rem; + + mtx_assert(&bus->mtx, MA_OWNED); + + rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1); + + isoc_time_curr &= (USB_ISOC_TIME_MAX - 1); + + if (isoc_time_curr < rem) { + /* the time counter wrapped around */ + bus->isoc_time_last += USB_ISOC_TIME_MAX; + } + /* update the remainder */ + + bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1); + bus->isoc_time_last |= isoc_time_curr; + + return (bus->isoc_time_last); +} + +/*------------------------------------------------------------------------* + * usb2_fs_isoc_schedule_isoc_time_expand + * + * This function does multiple things. First of all it will expand the + * passed isochronous time, which is the return value. Then it will + * store where the current FULL speed isochronous schedule is + * positioned in time and where the end is. See "pp_start" and + * "pp_end" arguments. + * + * Returns: + * Expanded version of "isoc_time". + * + * NOTE: This function depends on being called regularly with + * intervals less than "USB_ISOC_TIME_MAX". + *------------------------------------------------------------------------*/ +uint16_t +usb2_fs_isoc_schedule_isoc_time_expand(struct usb2_device *udev, + struct usb2_fs_isoc_schedule **pp_start, + struct usb2_fs_isoc_schedule **pp_end, + uint16_t isoc_time) +{ + struct usb2_fs_isoc_schedule *fss_end; + struct usb2_fs_isoc_schedule *fss_a; + struct usb2_fs_isoc_schedule *fss_b; + struct usb2_hub *hs_hub; + + isoc_time = usb2_isoc_time_expand(udev->bus, isoc_time); + + hs_hub = udev->bus->devices[udev->hs_hub_addr]->hub; + + if (hs_hub != NULL) { + + fss_a = hs_hub->fs_isoc_schedule + + (hs_hub->isoc_last_time % USB_ISOC_TIME_MAX); + + hs_hub->isoc_last_time = isoc_time; + + fss_b = hs_hub->fs_isoc_schedule + + (isoc_time % USB_ISOC_TIME_MAX); + + fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX; + + *pp_start = hs_hub->fs_isoc_schedule; + *pp_end = fss_end; + + while (fss_a != fss_b) { + if (fss_a == fss_end) { + fss_a = hs_hub->fs_isoc_schedule; + continue; + } + usb2_fs_isoc_schedule_init_sub(fss_a); + fss_a++; + } + + } else { + + *pp_start = NULL; + *pp_end = NULL; + } + return (isoc_time); +} + +/*------------------------------------------------------------------------* + * usb2_fs_isoc_schedule_alloc + * + * This function will allocate bandwidth for an isochronous FULL speed + * transaction in the FULL speed schedule. The microframe slot where + * the transaction should be started is stored in the byte pointed to + * by "pstart". The "len" argument specifies the length of the + * transaction in bytes. + * + * Returns: + * 0: Success + * Else: Error + *------------------------------------------------------------------------*/ +uint8_t +usb2_fs_isoc_schedule_alloc(struct usb2_fs_isoc_schedule *fss, + uint8_t *pstart, uint16_t len) +{ + uint8_t slot = fss->frame_slot; + + /* Compute overhead and bit-stuffing */ + + len += 8; + + len *= 7; + len /= 6; + + if (len > fss->total_bytes) { + *pstart = 0; /* set some dummy value */ + return (1); /* error */ + } + if (len > 0) { + + fss->total_bytes -= len; + + while (len >= fss->frame_bytes) { + len -= fss->frame_bytes; + fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME; + fss->frame_slot++; + } + + fss->frame_bytes -= len; + } + *pstart = slot; + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_bus_port_get_device + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +struct usb2_device * +usb2_bus_port_get_device(struct usb2_bus *bus, struct usb2_port *up) +{ + if ((bus == NULL) || (up == NULL)) { + /* be NULL safe */ + return (NULL); + } + if (up->device_index == 0) { + /* nothing to do */ + return (NULL); + } + return (bus->devices[up->device_index]); +} + +/*------------------------------------------------------------------------* + * usb2_bus_port_set_device + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_bus_port_set_device(struct usb2_bus *bus, struct usb2_port *up, + struct usb2_device *udev, uint8_t device_index) +{ + if (bus == NULL) { + /* be NULL safe */ + return; + } + /* + * There is only one case where we don't + * have an USB port, and that is the Root Hub! + */ + if (up) { + if (udev) { + up->device_index = device_index; + } else { + device_index = up->device_index; + up->device_index = 0; + } + } + /* + * Make relationships to our new device + */ + if (device_index != 0) { + mtx_lock(&usb2_ref_lock); + bus->devices[device_index] = udev; + mtx_unlock(&usb2_ref_lock); + } + /* + * Debug print + */ + DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_needs_explore + * + * This functions is called when the USB event thread needs to run. + *------------------------------------------------------------------------*/ +void +usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe) +{ + DPRINTF("\n"); + + if (bus == NULL) { + DPRINTF("No bus pointer!\n"); + return; + } + mtx_lock(&bus->mtx); + if (do_probe) { + bus->do_probe = 1; + } + if (usb2_proc_msignal(&bus->explore_proc, + &bus->explore_msg[0], &bus->explore_msg[1])) { + /* ignore */ + } + mtx_unlock(&bus->mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_needs_explore_all + * + * This function is called whenever a new driver is loaded and will + * cause that all USB busses are re-explored. + *------------------------------------------------------------------------*/ +void +usb2_needs_explore_all(void) +{ + struct usb2_bus *bus; + devclass_t dc; + device_t dev; + int max; + + DPRINTFN(3, "\n"); + + dc = usb2_devclass_ptr; + if (dc == NULL) { + DPRINTFN(0, "no devclass\n"); + return; + } + /* + * Explore all USB busses in parallell. + */ + max = devclass_get_maxunit(dc); + while (max >= 0) { + dev = devclass_get_device(dc, max); + if (dev) { + bus = device_get_softc(dev); + if (bus) { + usb2_needs_explore(bus, 1); + } + } + max--; + } + return; +} diff --git a/sys/dev/usb2/core/usb2_hub.h b/sys/dev/usb2/core/usb2_hub.h new file mode 100644 index 000000000000..183c9e02d854 --- /dev/null +++ b/sys/dev/usb2/core/usb2_hub.h @@ -0,0 +1,75 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_HUB_H_ +#define _USB2_HUB_H_ + +/* + * The following structure defines an USB port. + */ +struct usb2_port { + uint8_t restartcnt; +#define USB_RESTART_MAX 5 + uint8_t device_index; /* zero means not valid */ + uint8_t usb2_mode:1; /* current USB mode */ + uint8_t unused:7; +}; + +/* + * The following structure defines how many bytes are + * left in an 1ms USB time slot. + */ +struct usb2_fs_isoc_schedule { + uint16_t total_bytes; + uint8_t frame_bytes; + uint8_t frame_slot; +}; + +/* + * The following structure defines an USB HUB. + */ +struct usb2_hub { + struct usb2_fs_isoc_schedule fs_isoc_schedule[USB_ISOC_TIME_MAX]; + struct usb2_device *hubudev; /* the HUB device */ + usb2_error_t (*explore) (struct usb2_device *hub); + void *hubsoftc; + uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; + uint16_t portpower; /* mA per USB port */ + uint8_t isoc_last_time; + uint8_t nports; + struct usb2_port ports[0]; +}; + +/* function prototypes */ + +uint8_t usb2_intr_schedule_adjust(struct usb2_device *udev, int16_t len, uint8_t slot); +void usb2_fs_isoc_schedule_init_all(struct usb2_fs_isoc_schedule *fss); +void usb2_bus_port_set_device(struct usb2_bus *bus, struct usb2_port *up, struct usb2_device *udev, uint8_t device_index); +struct usb2_device *usb2_bus_port_get_device(struct usb2_bus *bus, struct usb2_port *up); +void usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe); +void usb2_needs_explore_all(void); + +#endif /* _USB2_HUB_H_ */ diff --git a/sys/dev/usb2/core/usb2_if.m b/sys/dev/usb2/core/usb2_if.m new file mode 100644 index 000000000000..d0db8d4445f9 --- /dev/null +++ b/sys/dev/usb2/core/usb2_if.m @@ -0,0 +1,52 @@ +#- +# Copyright (c) 2008 Hans Petter Selasky. 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 + +INTERFACE usb2; + +# The device received a control request +# +# Return values: +# 0: Success +# ENOTTY: Transaction stalled +# Else: Use builtin request handler +# +METHOD int handle_request { + device_t dev; + const void *req; /* pointer to the device request */ + void **pptr; /* data pointer */ + uint16_t *plen; /* maximum transfer length */ + uint16_t offset; /* data offset */ + uint8_t is_complete; /* set if transfer is complete */ +}; + diff --git a/sys/dev/usb2/core/usb2_lookup.c b/sys/dev/usb2/core/usb2_lookup.c new file mode 100644 index 000000000000..eaab8a3d6b12 --- /dev/null +++ b/sys/dev/usb2/core/usb2_lookup.c @@ -0,0 +1,134 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +/*------------------------------------------------------------------------* + * usb2_lookup_id_by_info + * + * This functions takes an array of "struct usb2_device_id" and tries + * to match the entries with the information in "struct usb2_lookup_info". + * + * NOTE: The "sizeof_id" parameter must be a multiple of the + * usb2_device_id structure size. Else the behaviour of this function + * is undefined. + * + * Return values: + * NULL: No match found. + * Else: Pointer to matching entry. + *------------------------------------------------------------------------*/ +const struct usb2_device_id * +usb2_lookup_id_by_info(const struct usb2_device_id *id, uint32_t sizeof_id, + const struct usb2_lookup_info *info) +{ + const struct usb2_device_id *id_end; + + if (id == NULL) { + goto done; + } + id_end = (const void *)(((const uint8_t *)id) + sizeof_id); + + /* + * Keep on matching array entries until we find a match or + * until we reach the end of the matching array: + */ + for (; id != id_end; id++) { + + if ((id->match_flag_vendor) && + (id->idVendor != info->idVendor)) { + continue; + } + if ((id->match_flag_product) && + (id->idProduct != info->idProduct)) { + continue; + } + if ((id->match_flag_dev_lo) && + (id->bcdDevice_lo > info->bcdDevice)) { + continue; + } + if ((id->match_flag_dev_hi) && + (id->bcdDevice_hi < info->bcdDevice)) { + continue; + } + if ((id->match_flag_dev_class) && + (id->bDeviceClass != info->bDeviceClass)) { + continue; + } + if ((id->match_flag_dev_subclass) && + (id->bDeviceSubClass != info->bDeviceSubClass)) { + continue; + } + if ((id->match_flag_dev_protocol) && + (id->bDeviceProtocol != info->bDeviceProtocol)) { + continue; + } + if ((info->bDeviceClass == 0xFF) && + (!(id->match_flag_vendor)) && + ((id->match_flag_int_class) || + (id->match_flag_int_subclass) || + (id->match_flag_int_protocol))) { + continue; + } + if ((id->match_flag_int_class) && + (id->bInterfaceClass != info->bInterfaceClass)) { + continue; + } + if ((id->match_flag_int_subclass) && + (id->bInterfaceSubClass != info->bInterfaceSubClass)) { + continue; + } + if ((id->match_flag_int_protocol) && + (id->bInterfaceProtocol != info->bInterfaceProtocol)) { + continue; + } + /* We found a match! */ + return (id); + } + +done: + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_lookup_id_by_uaa - factored out code + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +int +usb2_lookup_id_by_uaa(const struct usb2_device_id *id, uint32_t sizeof_id, + struct usb2_attach_arg *uaa) +{ + id = usb2_lookup_id_by_info(id, sizeof_id, &uaa->info); + if (id) { + /* copy driver info */ + uaa->driver_info = id->driver_info; + return (0); + } + return (ENXIO); +} diff --git a/sys/dev/usb2/core/usb2_lookup.h b/sys/dev/usb2/core/usb2_lookup.h new file mode 100644 index 000000000000..d4e39fd53b6e --- /dev/null +++ b/sys/dev/usb2/core/usb2_lookup.h @@ -0,0 +1,119 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_LOOKUP_H_ +#define _USB2_LOOKUP_H_ + +struct usb2_attach_arg; + +/* + * The following structure is used when looking up an USB driver for + * an USB device. It is inspired by the Linux structure called + * "usb2_device_id". + */ +struct usb2_device_id { + + /* Hook for driver specific information */ + const void *driver_info; + + /* Used for product specific matches; the BCD range is inclusive */ + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice_lo; + uint16_t bcdDevice_hi; + + /* Used for device class matches */ + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + + /* Used for interface class matches */ + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + + /* Select which fields to match against */ + uint8_t match_flag_vendor:1; + uint8_t match_flag_product:1; + uint8_t match_flag_dev_lo:1; + uint8_t match_flag_dev_hi:1; + uint8_t match_flag_dev_class:1; + uint8_t match_flag_dev_subclass:1; + uint8_t match_flag_dev_protocol:1; + uint8_t match_flag_int_class:1; + uint8_t match_flag_int_subclass:1; + uint8_t match_flag_int_protocol:1; +}; + +#define USB_VENDOR(vend) \ + .match_flag_vendor = 1, .idVendor = (vend) + +#define USB_PRODUCT(prod) \ + .match_flag_product = 1, .idProduct = (prod) + +#define USB_VP(vend,prod) \ + USB_VENDOR(vend), USB_PRODUCT(prod) + +#define USB_VPI(vend,prod,info) \ + USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) + +#define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ + .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) + +#define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ + .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) + +#define USB_DEV_CLASS(dc) \ + .match_flag_dev_class = 1, .bDeviceClass = (dc) + +#define USB_DEV_SUBCLASS(dsc) \ + .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) + +#define USB_DEV_PROTOCOL(dp) \ + .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) + +#define USB_IFACE_CLASS(ic) \ + .match_flag_int_class = 1, .bInterfaceClass = (ic) + +#define USB_IFACE_SUBCLASS(isc) \ + .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) + +#define USB_IFACE_PROTOCOL(ip) \ + .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) + +#define USB_IF_CSI(class,subclass,info) \ + USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) + +#define USB_DRIVER_INFO(ptr) \ + .driver_info = ((const void *)(ptr)) + +#define USB_GET_DRIVER_INFO(did) \ + (((const uint8_t *)((did)->driver_info)) - ((const uint8_t *)0)) + +const struct usb2_device_id *usb2_lookup_id_by_info(const struct usb2_device_id *id, uint32_t sizeof_id, const struct usb2_lookup_info *info); +int usb2_lookup_id_by_uaa(const struct usb2_device_id *id, uint32_t sizeof_id, struct usb2_attach_arg *uaa); + +#endif /* _USB2_LOOKUP_H_ */ diff --git a/sys/dev/usb2/core/usb2_mbuf.c b/sys/dev/usb2/core/usb2_mbuf.c new file mode 100644 index 000000000000..1f06f0f0f0dd --- /dev/null +++ b/sys/dev/usb2/core/usb2_mbuf.c @@ -0,0 +1,77 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +/*------------------------------------------------------------------------* + * usb2_alloc_mbufs - allocate mbufs to an usbd interface queue + * + * Returns: + * A pointer that should be passed to "free()" when the buffer(s) + * should be released. + *------------------------------------------------------------------------*/ +void * +usb2_alloc_mbufs(struct malloc_type *type, struct usb2_ifqueue *ifq, + uint32_t block_size, uint16_t nblocks) +{ + struct usb2_mbuf *m_ptr; + uint8_t *data_ptr; + void *free_ptr = NULL; + uint32_t alloc_size; + + /* align data */ + block_size += ((-block_size) & (USB_HOST_ALIGN - 1)); + + if (nblocks && block_size) { + + alloc_size = (block_size + sizeof(struct usb2_mbuf)) * nblocks; + + free_ptr = malloc(alloc_size, type, M_WAITOK | M_ZERO); + + if (free_ptr == NULL) { + goto done; + } + m_ptr = free_ptr; + data_ptr = (void *)(m_ptr + nblocks); + + while (nblocks--) { + + m_ptr->cur_data_ptr = + m_ptr->min_data_ptr = data_ptr; + + m_ptr->cur_data_len = + m_ptr->max_data_len = block_size; + + USB_IF_ENQUEUE(ifq, m_ptr); + + m_ptr++; + data_ptr += block_size; + } + } +done: + return (free_ptr); +} diff --git a/sys/dev/usb2/core/usb2_mbuf.h b/sys/dev/usb2/core/usb2_mbuf.h new file mode 100644 index 000000000000..521263ecab02 --- /dev/null +++ b/sys/dev/usb2/core/usb2_mbuf.h @@ -0,0 +1,100 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_MBUF_H_ +#define _USB2_MBUF_H_ + +/* + * The following structure defines a minimum re-implementation of the + * mbuf system in the kernel. + */ +struct usb2_mbuf { + uint8_t *cur_data_ptr; + uint8_t *min_data_ptr; + struct usb2_mbuf *usb2_nextpkt; + struct usb2_mbuf *usb2_next; + + uint32_t cur_data_len; + uint32_t max_data_len:31; + uint32_t last_packet:1; +}; + +/* + * The following structure defines a minimum re-implementation of the + * ifqueue structure in the kernel. + */ +struct usb2_ifqueue { + struct usb2_mbuf *ifq_head; + struct usb2_mbuf *ifq_tail; + + uint32_t ifq_len; + uint32_t ifq_maxlen; +}; + +#define USB_IF_ENQUEUE(ifq, m) do { \ + (m)->usb2_nextpkt = NULL; \ + if ((ifq)->ifq_tail == NULL) \ + (ifq)->ifq_head = (m); \ + else \ + (ifq)->ifq_tail->usb2_nextpkt = (m); \ + (ifq)->ifq_tail = (m); \ + (ifq)->ifq_len++; \ + } while (0) + +#define USB_IF_DEQUEUE(ifq, m) do { \ + (m) = (ifq)->ifq_head; \ + if (m) { \ + if (((ifq)->ifq_head = (m)->usb2_nextpkt) == NULL) { \ + (ifq)->ifq_tail = NULL; \ + } \ + (m)->usb2_nextpkt = NULL; \ + (ifq)->ifq_len--; \ + } \ + } while (0) + +#define USB_IF_PREPEND(ifq, m) do { \ + (m)->usb2_nextpkt = (ifq)->ifq_head; \ + if ((ifq)->ifq_tail == NULL) { \ + (ifq)->ifq_tail = (m); \ + } \ + (ifq)->ifq_head = (m); \ + (ifq)->ifq_len++; \ + } while (0) + +#define USB_IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) +#define USB_IF_QLEN(ifq) ((ifq)->ifq_len) +#define USB_IF_POLL(ifq, m) ((m) = (ifq)->ifq_head) + +#define USB_MBUF_RESET(m) do { \ + (m)->cur_data_ptr = (m)->min_data_ptr; \ + (m)->cur_data_len = (m)->max_data_len; \ + (m)->last_packet = 0; \ + } while (0) + +/* prototypes */ +void *usb2_alloc_mbufs(struct malloc_type *type, struct usb2_ifqueue *ifq, uint32_t block_size, uint16_t nblocks); + +#endif /* _USB2_MBUF_H_ */ diff --git a/sys/dev/usb2/core/usb2_msctest.c b/sys/dev/usb2/core/usb2_msctest.c new file mode 100644 index 000000000000..9db204a1523c --- /dev/null +++ b/sys/dev/usb2/core/usb2_msctest.c @@ -0,0 +1,612 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +/* + * The following file contains code that will detect USB autoinstall + * disks. + * + * TODO: Potentially we could add code to automatically detect USB + * mass storage quirks for not supported SCSI commands! + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum { + ST_COMMAND, + ST_DATA_RD, + ST_DATA_RD_CS, + ST_DATA_WR, + ST_DATA_WR_CS, + ST_STATUS, + ST_MAX, +}; + +enum { + DIR_IN, + DIR_OUT, + DIR_NONE, +}; + +#define BULK_SIZE 64 /* dummy */ + + +/* Command Block Wrapper */ +struct bbb_cbw { + 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; + +/* Command Status Wrapper */ +struct bbb_csw { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} __packed; + +struct bbb_transfer { + struct mtx mtx; + struct cv cv; + struct bbb_cbw cbw; + struct bbb_csw csw; + + struct usb2_xfer *xfer[ST_MAX]; + + uint8_t *data_ptr; + + uint32_t data_len; /* bytes */ + uint32_t data_rem; /* bytes */ + uint32_t data_timeout; /* ms */ + uint32_t actlen; /* bytes */ + + uint8_t cmd_len; /* bytes */ + uint8_t dir; + uint8_t lun; + uint8_t state; + uint8_t error; + uint8_t status_try; + + uint8_t buffer[256]; +}; + +static usb2_callback_t bbb_command_callback; +static usb2_callback_t bbb_data_read_callback; +static usb2_callback_t bbb_data_rd_cs_callback; +static usb2_callback_t bbb_data_write_callback; +static usb2_callback_t bbb_data_wr_cs_callback; +static usb2_callback_t bbb_status_callback; + +static const struct usb2_config bbb_config[ST_MAX] = { + + [ST_COMMAND] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = sizeof(struct bbb_cbw), + .mh.flags = {}, + .mh.callback = &bbb_command_callback, + .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ + }, + + [ST_DATA_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &bbb_data_read_callback, + .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ + }, + + [ST_DATA_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &bbb_data_rd_cs_callback, + .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ + }, + + [ST_DATA_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &bbb_data_write_callback, + .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ + }, + + [ST_DATA_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &bbb_data_wr_cs_callback, + .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ + }, + + [ST_STATUS] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = sizeof(struct bbb_csw), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &bbb_status_callback, + .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ + }, +}; + +static void +bbb_done(struct bbb_transfer *sc, uint8_t error) +{ + struct usb2_xfer *xfer; + + xfer = sc->xfer[sc->state]; + + /* verify the error code */ + + if (error) { + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + error = 1; + break; + default: + error = 2; + break; + } + } + sc->error = error; + sc->state = ST_COMMAND; + sc->status_try = 1; + usb2_cv_signal(&sc->cv); + return; +} + +static void +bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) +{ + sc->state = xfer_index; + usb2_transfer_start(sc->xfer[xfer_index]); + return; +} + +static void +bbb_data_clear_stall_callback(struct usb2_xfer *xfer, + uint8_t next_xfer, uint8_t stall_xfer) +{ + struct bbb_transfer *sc = xfer->priv_sc; + + if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + bbb_transfer_start(sc, next_xfer); + break; + default: + bbb_done(sc, 1); + break; + } + } + return; +} + +static void +bbb_command_callback(struct usb2_xfer *xfer) +{ + struct bbb_transfer *sc = xfer->priv_sc; + uint32_t tag; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + bbb_transfer_start + (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : + (sc->dir == DIR_OUT) ? ST_DATA_WR : + ST_STATUS)); + break; + + case USB_ST_SETUP: + sc->status_try = 0; + tag = UGETDW(sc->cbw.dCBWTag) + 1; + USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); + USETDW(sc->cbw.dCBWTag, tag); + USETDW(sc->cbw.dCBWDataTransferLength, sc->data_len); + sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); + sc->cbw.bCBWLUN = sc->lun; + if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) { + sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB); + DPRINTFN(0, "Truncating long command!\n"); + } + xfer->frlengths[0] = sizeof(sc->cbw); + + usb2_set_frame_data(xfer, &sc->cbw, 0); + usb2_start_hardware(xfer); + break; + + default: /* Error */ + bbb_done(sc, 1); + break; + } + return; +} + +static void +bbb_data_read_callback(struct usb2_xfer *xfer) +{ + struct bbb_transfer *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->data_rem -= xfer->actlen; + sc->data_ptr += xfer->actlen; + sc->actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF("max_bulk=%d, data_rem=%d\n", + max_bulk, sc->data_rem); + + if (sc->data_rem == 0) { + bbb_transfer_start(sc, ST_STATUS); + break; + } + if (max_bulk > sc->data_rem) { + max_bulk = sc->data_rem; + } + xfer->timeout = sc->data_timeout; + xfer->frlengths[0] = max_bulk; + + usb2_set_frame_data(xfer, sc->data_ptr, 0); + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + bbb_done(sc, 1); + } else { + bbb_transfer_start(sc, ST_DATA_RD_CS); + } + break; + } + return; +} + +static void +bbb_data_rd_cs_callback(struct usb2_xfer *xfer) +{ + bbb_data_clear_stall_callback(xfer, ST_STATUS, + ST_DATA_RD); + return; +} + +static void +bbb_data_write_callback(struct usb2_xfer *xfer) +{ + struct bbb_transfer *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->data_rem -= xfer->actlen; + sc->data_ptr += xfer->actlen; + sc->actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF("max_bulk=%d, data_rem=%d\n", + max_bulk, sc->data_rem); + + if (sc->data_rem == 0) { + bbb_transfer_start(sc, ST_STATUS); + return; + } + if (max_bulk > sc->data_rem) { + max_bulk = sc->data_rem; + } + xfer->timeout = sc->data_timeout; + xfer->frlengths[0] = max_bulk; + + usb2_set_frame_data(xfer, sc->data_ptr, 0); + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + bbb_done(sc, 1); + } else { + bbb_transfer_start(sc, ST_DATA_WR_CS); + } + return; + + } +} + +static void +bbb_data_wr_cs_callback(struct usb2_xfer *xfer) +{ + bbb_data_clear_stall_callback(xfer, ST_STATUS, + ST_DATA_WR); + return; +} + +static void +bbb_status_callback(struct usb2_xfer *xfer) +{ + struct bbb_transfer *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* very simple status check */ + + if (xfer->actlen < sizeof(sc->csw)) { + bbb_done(sc, 1);/* error */ + } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { + bbb_done(sc, 0);/* success */ + } else { + bbb_done(sc, 1);/* error */ + } + break; + + case USB_ST_SETUP: + xfer->frlengths[0] = sizeof(sc->csw); + + usb2_set_frame_data(xfer, &sc->csw, 0); + usb2_start_hardware(xfer); + break; + + default: + DPRINTFN(0, "Failed to read CSW: %s, try %d\n", + usb2_errstr(xfer->error), sc->status_try); + + if ((xfer->error == USB_ERR_CANCELLED) || + (sc->status_try)) { + bbb_done(sc, 1); + } else { + sc->status_try = 1; + bbb_transfer_start(sc, ST_DATA_RD_CS); + } + break; + } + return; +} + +/*------------------------------------------------------------------------* + * bbb_command_start - execute a SCSI command synchronously + * + * Return values + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, + void *data_ptr, uint32_t data_len, uint8_t cmd_len, + uint32_t data_timeout) +{ + sc->lun = lun; + sc->dir = data_len ? dir : DIR_NONE; + sc->data_ptr = data_ptr; + sc->data_len = data_len; + sc->data_rem = data_len; + sc->data_timeout = (data_timeout + USB_MS_HZ); + sc->actlen = 0; + sc->cmd_len = cmd_len; + + usb2_transfer_start(sc->xfer[sc->state]); + + while (usb2_transfer_pending(sc->xfer[sc->state])) { + usb2_cv_wait(&sc->cv, &sc->mtx); + } + return (sc->error); +} + +/*------------------------------------------------------------------------* + * usb2_test_autoinstall + * + * Return values: + * 0: This interface is an auto install disk (CD-ROM) + * Else: Not an auto install disk. + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index) +{ + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + usb2_error_t err; + uint8_t timeout; + uint8_t sid_type; + struct bbb_transfer *sc; + + if (udev == NULL) { + return (USB_ERR_INVAL); + } + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (USB_ERR_INVAL); + } + id = iface->idesc; + if (id == NULL) { + return (USB_ERR_INVAL); + } + if (id->bInterfaceClass != UICLASS_MASS) { + return (USB_ERR_INVAL); + } + switch (id->bInterfaceSubClass) { + case UISUBCLASS_SCSI: + case UISUBCLASS_UFI: + break; + default: + return (USB_ERR_INVAL); + } + + switch (id->bInterfaceProtocol) { + case UIPROTO_MASS_BBB_OLD: + case UIPROTO_MASS_BBB: + break; + default: + return (USB_ERR_INVAL); + } + + sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); + if (sc == NULL) { + return (USB_ERR_NOMEM); + } + mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); + usb2_cv_init(&sc->cv, "WBBB"); + + err = usb2_transfer_setup(udev, + &iface_index, sc->xfer, bbb_config, + ST_MAX, sc, &sc->mtx); + + if (err) { + goto done; + } + mtx_lock(&sc->mtx); + + timeout = 4; /* tries */ + +repeat_inquiry: + + sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */ + + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 6, USB_MS_HZ); + if (err) { + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 12, USB_MS_HZ); + if (err) { + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 16, USB_MS_HZ); + } + } + if ((sc->actlen != 0) && (err == 0)) { + sid_type = sc->buffer[0] & 0x1F; + if (sid_type == 0x05) { + /* CD-ROM */ + /* XXX could investigate more */ + return (0); + } + } else if (--timeout) { + usb2_pause_mtx(&sc->mtx, USB_MS_HZ); + goto repeat_inquiry; + } + err = USB_ERR_INVAL; + goto done; + +done: + mtx_unlock(&sc->mtx); + usb2_transfer_unsetup(sc->xfer, ST_MAX); + mtx_destroy(&sc->mtx); + usb2_cv_destroy(&sc->cv); + free(sc, M_USB); + return (err); +} + +/* + * Huawei Exxx radio devices have a built in flash disk which is their + * default power up configuration. This allows the device to carry its + * own installation software. + * + * Instead of following the USB spec, and create multiple + * configuration descriptors for this, the devices expects the driver + * to send UF_DEVICE_REMOTE_WAKEUP to endpoint 2 to reset the device, + * so it reprobes, now with the radio exposed. + */ + +usb2_error_t +usb2_test_huawei(struct usb2_device *udev, uint8_t iface_index) +{ + struct usb2_device_request req; + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + usb2_error_t err; + + if (udev == NULL) { + return (USB_ERR_INVAL); + } + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (USB_ERR_INVAL); + } + id = iface->idesc; + if (id == NULL) { + return (USB_ERR_INVAL); + } + if (id->bInterfaceClass != UICLASS_MASS) { + return (USB_ERR_INVAL); + } + /* Bend it like Beckham */ + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, 2); + USETW(req.wLength, 0); + + /* We get error at return, but it works */ + err = usb2_do_request_flags(udev, NULL, &req, NULL, 0, NULL, 1 * USB_MS_HZ); + + return (0); /* success */ +} diff --git a/sys/dev/usb2/core/usb2_msctest.h b/sys/dev/usb2/core/usb2_msctest.h new file mode 100644 index 000000000000..19aac3697bd1 --- /dev/null +++ b/sys/dev/usb2/core/usb2_msctest.h @@ -0,0 +1,33 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_MSCTEST_H_ +#define _USB2_MSCTEST_H_ + +usb2_error_t usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index); +usb2_error_t usb2_test_huawei(struct usb2_device *udev, uint8_t iface_index); + +#endif /* _USB2_MSCTEST_H_ */ diff --git a/sys/dev/usb2/core/usb2_parse.c b/sys/dev/usb2/core/usb2_parse.c new file mode 100644 index 000000000000..223b3c2634da --- /dev/null +++ b/sys/dev/usb2/core/usb2_parse.c @@ -0,0 +1,208 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +#include +#include + +/*------------------------------------------------------------------------* + * usb2_desc_foreach + * + * This function is the safe way to iterate across the USB config + * descriptor. It contains several checks against invalid + * descriptors. If the "desc" argument passed to this function is + * "NULL" the first descriptor, if any, will be returned. + * + * Return values: + * NULL: End of descriptors + * Else: Next descriptor after "desc" + *------------------------------------------------------------------------*/ +struct usb2_descriptor * +usb2_desc_foreach(struct usb2_config_descriptor *cd, struct usb2_descriptor *desc) +{ + void *end; + + if (cd == NULL) { + return (NULL); + } + end = USB_ADD_BYTES(cd, UGETW(cd->wTotalLength)); + + if (desc == NULL) { + desc = USB_ADD_BYTES(cd, 0); + } else { + desc = USB_ADD_BYTES(desc, desc->bLength); + } + return (((((void *)desc) >= ((void *)cd)) && + (((void *)desc) < end) && + (USB_ADD_BYTES(desc, desc->bLength) >= ((void *)cd)) && + (USB_ADD_BYTES(desc, desc->bLength) <= end) && + (desc->bLength >= sizeof(*desc))) ? desc : NULL); +} + +/*------------------------------------------------------------------------* + * usb2_find_idesc + * + * This function will return the interface descriptor, if any, that + * has index "iface_index" and alternate index "alt_index". + * + * Return values: + * NULL: End of descriptors + * Else: A valid interface descriptor + *------------------------------------------------------------------------*/ +struct usb2_interface_descriptor * +usb2_find_idesc(struct usb2_config_descriptor *cd, + uint8_t iface_index, uint8_t alt_index) +{ + struct usb2_descriptor *desc = NULL; + struct usb2_interface_descriptor *id; + uint8_t curidx = 0; + uint8_t lastidx = 0; + uint8_t curaidx = 0; + uint8_t first = 1; + + while ((desc = usb2_desc_foreach(cd, desc))) { + if ((desc->bDescriptorType == UDESC_INTERFACE) && + (desc->bLength >= sizeof(*id))) { + id = (void *)desc; + + if (first) { + first = 0; + lastidx = id->bInterfaceNumber; + + } else if (id->bInterfaceNumber != lastidx) { + + lastidx = id->bInterfaceNumber; + curidx++; + curaidx = 0; + + } else { + curaidx++; + } + + if ((iface_index == curidx) && (alt_index == curaidx)) { + return (id); + } + } + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_find_edesc + * + * This function will return the endpoint descriptor for the passed + * interface index, alternate index and endpoint index. + * + * Return values: + * NULL: End of descriptors + * Else: A valid endpoint descriptor + *------------------------------------------------------------------------*/ +struct usb2_endpoint_descriptor * +usb2_find_edesc(struct usb2_config_descriptor *cd, + uint8_t iface_index, uint8_t alt_index, uint8_t ep_index) +{ + struct usb2_descriptor *desc = NULL; + struct usb2_interface_descriptor *d; + uint8_t curidx = 0; + + d = usb2_find_idesc(cd, iface_index, alt_index); + if (d == NULL) + return (NULL); + + if (ep_index >= d->bNumEndpoints) /* quick exit */ + return (NULL); + + desc = ((void *)d); + + while ((desc = usb2_desc_foreach(cd, desc))) { + + if (desc->bDescriptorType == UDESC_INTERFACE) { + break; + } + if (desc->bDescriptorType == UDESC_ENDPOINT) { + if (curidx == ep_index) { + if (desc->bLength < + sizeof(struct usb2_endpoint_descriptor)) { + /* endpoint index is invalid */ + break; + } + return ((void *)desc); + } + curidx++; + } + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_get_no_endpoints + * + * This function will count the total number of endpoints available. + *------------------------------------------------------------------------*/ +uint16_t +usb2_get_no_endpoints(struct usb2_config_descriptor *cd) +{ + struct usb2_descriptor *desc = NULL; + uint16_t count = 0; + + while ((desc = usb2_desc_foreach(cd, desc))) { + if (desc->bDescriptorType == UDESC_ENDPOINT) { + count++; + } + } + return (count); +} + +/*------------------------------------------------------------------------* + * usb2_get_no_alts + * + * Return value: + * Number of alternate settings for the given "ifaceno". + * + * NOTE: The returned can be larger than the actual number of + * alternate settings. + *------------------------------------------------------------------------*/ +uint16_t +usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno) +{ + struct usb2_descriptor *desc = NULL; + struct usb2_interface_descriptor *id; + uint16_t n = 0; + + while ((desc = usb2_desc_foreach(cd, desc))) { + + if ((desc->bDescriptorType == UDESC_INTERFACE) && + (desc->bLength >= sizeof(*id))) { + id = (void *)desc; + if (id->bInterfaceNumber == ifaceno) { + n++; + } + } + } + return (n); +} diff --git a/sys/dev/usb2/core/usb2_parse.h b/sys/dev/usb2/core/usb2_parse.h new file mode 100644 index 000000000000..f367f1ad757b --- /dev/null +++ b/sys/dev/usb2/core/usb2_parse.h @@ -0,0 +1,36 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_PARSE_H_ +#define _USB2_PARSE_H_ + +struct usb2_descriptor *usb2_desc_foreach(struct usb2_config_descriptor *cd, struct usb2_descriptor *desc); +struct usb2_interface_descriptor *usb2_find_idesc(struct usb2_config_descriptor *cd, uint8_t iface_index, uint8_t alt_index); +struct usb2_endpoint_descriptor *usb2_find_edesc(struct usb2_config_descriptor *cd, uint8_t iface_index, uint8_t alt_index, uint8_t ep_index); +uint16_t usb2_get_no_endpoints(struct usb2_config_descriptor *cd); +uint16_t usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno); + +#endif /* _USB2_PARSE_H_ */ diff --git a/sys/dev/usb2/core/usb2_process.c b/sys/dev/usb2/core/usb2_process.c new file mode 100644 index 000000000000..97365722e269 --- /dev/null +++ b/sys/dev/usb2/core/usb2_process.c @@ -0,0 +1,480 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#define USB_DEBUG_VAR usb2_proc_debug + +#include +#include +#include +#include + +#include +#include +#include + +#if (__FreeBSD_version < 700000) +#define thread_lock(td) mtx_lock_spin(&sched_lock) +#define thread_unlock(td) mtx_unlock_spin(&sched_lock) +#endif + +#if (__FreeBSD_version >= 800000) +#define USB_THREAD_CREATE(f, s, p, ...) \ + kproc_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) +#define USB_THREAD_SUSPEND(p) kproc_suspend(p,0) +#define USB_THREAD_EXIT(err) kproc_exit(err) +#else +#define USB_THREAD_CREATE(f, s, p, ...) \ + kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) +#define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) +#define USB_THREAD_EXIT(err) kthread_exit(err) +#endif + +#if USB_DEBUG +static int usb2_proc_debug; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process"); +SYSCTL_INT(_hw_usb2_proc, OID_AUTO, debug, CTLFLAG_RW, &usb2_proc_debug, 0, + "Debug level"); +#endif + +/*------------------------------------------------------------------------* + * usb2_process + * + * This function is the USB process dispatcher. + *------------------------------------------------------------------------*/ +static void +usb2_process(void *arg) +{ + struct usb2_process *up = arg; + struct usb2_proc_msg *pm; + struct thread *td; + + /* adjust priority */ + td = curthread; + thread_lock(td); + sched_prio(td, up->up_prio); + thread_unlock(td); + + mtx_lock(up->up_mtx); + + up->up_curtd = td; + + while (1) { + + if (up->up_gone) { + break; + } + /* + * NOTE to reimplementors: dequeueing a command from the + * "used" queue and executing it must be atomic, with regard + * to the "up_mtx" mutex. That means any attempt to queue a + * command by another thread must be blocked until either: + * + * 1) the command sleeps + * + * 2) the command returns + * + * Here is a practical example that shows how this helps + * solving a problem: + * + * Assume that you want to set the baud rate on a USB serial + * device. During the programming of the device you don't + * want to receive nor transmit any data, because it will be + * garbage most likely anyway. The programming of our USB + * device takes 20 milliseconds and it needs to call + * functions that sleep. + * + * Non-working solution: Before we queue the programming + * command, we stop transmission and reception of data. Then + * we queue a programming command. At the end of the + * programming command we enable transmission and reception + * of data. + * + * Problem: If a second programming command is queued while the + * first one is sleeping, we end up enabling transmission + * and reception of data too early. + * + * Working solution: Before we queue the programming command, + * we stop transmission and reception of data. Then we queue + * a programming command. Then we queue a second command + * that only enables transmission and reception of data. + * + * Why it works: If a second programming command is queued + * while the first one is sleeping, then the queueing of a + * second command to enable the data transfers, will cause + * the previous one, which is still on the queue, to be + * removed from the queue, and re-inserted after the last + * baud rate programming command, which then gives the + * desired result. + */ + pm = TAILQ_FIRST(&up->up_qhead); + + if (pm) { + DPRINTF("Message pm=%p, cb=%p (enter)\n", + pm, pm->pm_callback); + + (pm->pm_callback) (pm); + + if (pm == TAILQ_FIRST(&up->up_qhead)) { + /* nothing changed */ + TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); + pm->pm_qentry.tqe_prev = NULL; + } + DPRINTF("Message pm=%p (leave)\n", pm); + + continue; + } + /* end if messages - check if anyone is waiting for sync */ + if (up->up_dsleep) { + up->up_dsleep = 0; + usb2_cv_broadcast(&up->up_drain); + } + up->up_msleep = 1; + usb2_cv_wait(&up->up_cv, up->up_mtx); + } + + up->up_ptr = NULL; + usb2_cv_signal(&up->up_cv); + mtx_unlock(up->up_mtx); + + USB_THREAD_EXIT(0); + return; +} + +/*------------------------------------------------------------------------* + * usb2_proc_setup + * + * This function will create a process using the given "prio" that can + * execute callbacks. The mutex pointed to by "p_mtx" will be applied + * before calling the callbacks and released after that the callback + * has returned. The structure pointed to by "up" is assumed to be + * zeroed before this function is called. + * + * Return values: + * 0: success + * Else: failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_proc_setup(struct usb2_process *up, struct mtx *p_mtx, uint8_t prio) +{ + up->up_mtx = p_mtx; + up->up_prio = prio; + + TAILQ_INIT(&up->up_qhead); + + usb2_cv_init(&up->up_cv, "WMSG"); + usb2_cv_init(&up->up_drain, "DMSG"); + + if (USB_THREAD_CREATE(&usb2_process, up, + &up->up_ptr, "USBPROC")) { + DPRINTFN(0, "Unable to create USB process."); + up->up_ptr = NULL; + goto error; + } + return (0); + +error: + usb2_proc_unsetup(up); + return (1); +} + +/*------------------------------------------------------------------------* + * usb2_proc_unsetup + * + * NOTE: If the structure pointed to by "up" is all zero, this + * function does nothing. + * + * NOTE: Messages that are pending on the process queue will not be + * removed nor called. + *------------------------------------------------------------------------*/ +void +usb2_proc_unsetup(struct usb2_process *up) +{ + if (!(up->up_mtx)) { + /* not initialised */ + return; + } + usb2_proc_drain(up); + + usb2_cv_destroy(&up->up_cv); + usb2_cv_destroy(&up->up_drain); + + /* make sure that we do not enter here again */ + up->up_mtx = NULL; + return; +} + +/*------------------------------------------------------------------------* + * usb2_proc_msignal + * + * This function will queue one of the passed USB process messages on + * the USB process queue. The first message that is not already queued + * will get queued. If both messages are already queued the one queued + * last will be removed from the queue and queued in the end. The USB + * process mutex must be locked when calling this function. This + * function exploits the fact that a process can only do one callback + * at a time. The message that was queued is returned. + *------------------------------------------------------------------------*/ +void * +usb2_proc_msignal(struct usb2_process *up, void *_pm0, void *_pm1) +{ + struct usb2_proc_msg *pm0 = _pm0; + struct usb2_proc_msg *pm1 = _pm1; + struct usb2_proc_msg *pm2; + uint32_t d; + uint8_t t; + + mtx_assert(up->up_mtx, MA_OWNED); + + t = 0; + + if (pm0->pm_qentry.tqe_prev) { + t |= 1; + } + if (pm1->pm_qentry.tqe_prev) { + t |= 2; + } + if (t == 0) { + /* + * No entries are queued. Queue "pm0" and use the existing + * message number. + */ + pm2 = pm0; + } else if (t == 1) { + /* Check if we need to increment the message number. */ + if (pm0->pm_num == up->up_msg_num) { + up->up_msg_num++; + } + pm2 = pm1; + } else if (t == 2) { + /* Check if we need to increment the message number. */ + if (pm1->pm_num == up->up_msg_num) { + up->up_msg_num++; + } + pm2 = pm0; + } else if (t == 3) { + /* + * Both entries are queued. Re-queue the entry closest to + * the end. + */ + d = (pm1->pm_num - pm0->pm_num); + + /* Check sign after subtraction */ + if (d & 0x80000000) { + pm2 = pm0; + } else { + pm2 = pm1; + } + + TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); + } else { + pm2 = NULL; /* panic - should not happen */ + } + + DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); + + /* Put message last on queue */ + + pm2->pm_num = up->up_msg_num; + TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); + + /* Check if we need to wakeup the USB process. */ + + if (up->up_msleep) { + up->up_msleep = 0; /* save "cv_signal()" calls */ + usb2_cv_signal(&up->up_cv); + } + return (pm2); +} + +/*------------------------------------------------------------------------* + * usb2_proc_is_gone + * + * Return values: + * 0: USB process is running + * Else: USB process is tearing down + *------------------------------------------------------------------------*/ +uint8_t +usb2_proc_is_gone(struct usb2_process *up) +{ + mtx_assert(up->up_mtx, MA_OWNED); + + return (up->up_gone ? 1 : 0); +} + +/*------------------------------------------------------------------------* + * usb2_proc_mwait + * + * This function will return when the USB process message pointed to + * by "pm" is no longer on a queue. This function must be called + * having "up->up_mtx" locked. + *------------------------------------------------------------------------*/ +void +usb2_proc_mwait(struct usb2_process *up, void *_pm0, void *_pm1) +{ + struct usb2_proc_msg *pm0 = _pm0; + struct usb2_proc_msg *pm1 = _pm1; + + mtx_assert(up->up_mtx, MA_OWNED); + + if (up->up_curtd == curthread) { + /* Just remove the messages from the queue. */ + if (pm0->pm_qentry.tqe_prev) { + TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); + pm0->pm_qentry.tqe_prev = NULL; + } + if (pm1->pm_qentry.tqe_prev) { + TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); + pm1->pm_qentry.tqe_prev = NULL; + } + } else + while (pm0->pm_qentry.tqe_prev || + pm1->pm_qentry.tqe_prev) { + /* check if config thread is gone */ + if (up->up_gone) + break; + up->up_dsleep = 1; + usb2_cv_wait(&up->up_drain, up->up_mtx); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_proc_drain + * + * This function will tear down an USB process, waiting for the + * currently executing command to return. + * + * NOTE: If the structure pointed to by "up" is all zero, + * this function does nothing. + *------------------------------------------------------------------------*/ +void +usb2_proc_drain(struct usb2_process *up) +{ + if (!(up->up_mtx)) { + /* not initialised */ + return; + } + if (up->up_mtx != &Giant) { + mtx_assert(up->up_mtx, MA_NOTOWNED); + } + mtx_lock(up->up_mtx); + + /* Set the gone flag */ + + up->up_gone = 1; + + while (up->up_ptr) { + + /* Check if we need to wakeup the USB process */ + + if (up->up_msleep || up->up_csleep) { + up->up_msleep = 0; + up->up_csleep = 0; + usb2_cv_signal(&up->up_cv); + } + /* Check if we are still cold booted */ + + if (cold) { + USB_THREAD_SUSPEND(up->up_ptr); + printf("WARNING: A USB process has been left suspended!\n"); + break; + } + usb2_cv_wait(&up->up_cv, up->up_mtx); + } + /* Check if someone is waiting - should not happen */ + + if (up->up_dsleep) { + up->up_dsleep = 0; + usb2_cv_broadcast(&up->up_drain); + DPRINTF("WARNING: Someone is waiting " + "for USB process drain!\n"); + } + mtx_unlock(up->up_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_proc_cwait + * + * This function will suspend the current process until + * "usb2_proc_signal()" or "usb2_proc_drain()" is called. The + * "timeout" parameter defines the maximum wait time in system + * ticks. If "timeout" is zero that means no timeout. + * + * NOTE: This function can only be called from within an USB process. + * + * Return values: + * USB_PROC_WAIT_TIMEOUT: Timeout + * USB_PROC_WAIT_NORMAL: Success + * Else: USB process is tearing down + *------------------------------------------------------------------------*/ +uint8_t +usb2_proc_cwait(struct usb2_process *up, int timeout) +{ + int error; + + mtx_assert(up->up_mtx, MA_OWNED); + + if (up->up_gone) { + return (USB_PROC_WAIT_DRAIN); + } + up->up_csleep = 1; + + if (timeout == 0) { + usb2_cv_wait(&up->up_cv, up->up_mtx); + error = 0; + } else { + error = usb2_cv_timedwait(&up->up_cv, up->up_mtx, timeout); + } + + up->up_csleep = 0; + + if (up->up_gone) { + return (USB_PROC_WAIT_DRAIN); + } + if (error == EWOULDBLOCK) { + return (USB_PROC_WAIT_TIMEOUT); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_proc_csignal + * + * This function will wakeup the given USB process. + *------------------------------------------------------------------------*/ +void +usb2_proc_csignal(struct usb2_process *up) +{ + mtx_assert(up->up_mtx, MA_OWNED); + + if (up->up_csleep) { + up->up_csleep = 0; + usb2_cv_signal(&up->up_cv); + } + return; +} diff --git a/sys/dev/usb2/core/usb2_process.h b/sys/dev/usb2/core/usb2_process.h new file mode 100644 index 000000000000..7019735f3c14 --- /dev/null +++ b/sys/dev/usb2/core/usb2_process.h @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_PROCESS_H_ +#define _USB2_PROCESS_H_ + +#include + +/* defines */ +#define USB_PRI_HIGH PI_NET +#define USB_PRI_MED PI_DISK + +#define USB_PROC_WAIT_TIMEOUT 2 +#define USB_PROC_WAIT_DRAIN 1 +#define USB_PROC_WAIT_NORMAL 0 + +/* structure prototypes */ + +struct usb2_proc_msg; + +/* typedefs */ + +typedef void (usb2_proc_callback_t)(struct usb2_proc_msg *hdr); + +/* + * The following structure defines the USB process message header. + */ +struct usb2_proc_msg { + TAILQ_ENTRY(usb2_proc_msg) pm_qentry; + usb2_proc_callback_t *pm_callback; + uint32_t pm_num; +}; + +/* + * The following structure defines the USB process. + */ +struct usb2_process { + TAILQ_HEAD(, usb2_proc_msg) up_qhead; + struct cv up_cv; + struct cv up_drain; + + struct proc *up_ptr; + struct thread *up_curtd; + struct mtx *up_mtx; + + uint32_t up_msg_num; + + uint8_t up_prio; + uint8_t up_gone; + uint8_t up_msleep; + uint8_t up_csleep; + uint8_t up_dsleep; +}; + +/* prototypes */ + +uint8_t usb2_proc_cwait(struct usb2_process *up, int timeout); +uint8_t usb2_proc_is_gone(struct usb2_process *up); +uint8_t usb2_proc_setup(struct usb2_process *up, struct mtx *p_mtx, uint8_t prio); +void usb2_proc_csignal(struct usb2_process *up); +void usb2_proc_drain(struct usb2_process *up); +void usb2_proc_mwait(struct usb2_process *up, void *pm0, void *pm1); +void usb2_proc_unsetup(struct usb2_process *up); +void *usb2_proc_msignal(struct usb2_process *up, void *pm0, void *pm1); + +#endif /* _USB2_PROCESS_H_ */ diff --git a/sys/dev/usb2/core/usb2_request.c b/sys/dev/usb2/core/usb2_request.c new file mode 100644 index 000000000000..72d9f175e555 --- /dev/null +++ b/sys/dev/usb2/core/usb2_request.c @@ -0,0 +1,1373 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. All rights reserved. + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int usb2_pr_poll_delay = USB_PORT_RESET_DELAY; +static int usb2_pr_recovery_delay = USB_PORT_RESET_RECOVERY; +static int usb2_ss_delay = 0; + +SYSCTL_INT(_hw_usb2, OID_AUTO, pr_poll_delay, CTLFLAG_RW, + &usb2_pr_poll_delay, 0, "USB port reset poll delay in ms"); +SYSCTL_INT(_hw_usb2, OID_AUTO, pr_recovery_delay, CTLFLAG_RW, + &usb2_pr_recovery_delay, 0, "USB port reset recovery delay in ms"); +SYSCTL_INT(_hw_usb2, OID_AUTO, ss_delay, CTLFLAG_RW, + &usb2_ss_delay, 0, "USB status stage delay in ms"); +#endif + +/*------------------------------------------------------------------------* + * usb2_do_request_callback + * + * This function is the USB callback for generic USB Host control + * transfers. + *------------------------------------------------------------------------*/ +void +usb2_do_request_callback(struct usb2_xfer *xfer) +{ + ; /* workaround for a bug in "indent" */ + + DPRINTF("st=%u\n", USB_GET_STATE(xfer)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + usb2_start_hardware(xfer); + break; + default: + usb2_cv_signal(xfer->udev->default_cv); + break; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_do_clear_stall_callback + * + * This function is the USB callback for generic clear stall requests. + *------------------------------------------------------------------------*/ +void +usb2_do_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct usb2_device_request req; + struct usb2_pipe *pipe; + struct usb2_pipe *pipe_end; + struct usb2_pipe *pipe_first; + uint8_t to = USB_EP_MAX; + + mtx_lock(xfer->usb2_mtx); + + /* round robin pipe clear stall */ + + pipe = xfer->udev->pipe_curr; + pipe_end = xfer->udev->pipes + USB_EP_MAX; + pipe_first = xfer->udev->pipes; + if (pipe == NULL) { + pipe = pipe_first; + } + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (pipe->edesc && + pipe->is_stalled) { + pipe->toggle_next = 0; + pipe->is_stalled = 0; + /* start up the current or next transfer, if any */ + usb2_command_wrapper(&pipe->pipe_q, + pipe->pipe_q.curr); + } + pipe++; + + case USB_ST_SETUP: +tr_setup: + if (pipe == pipe_end) { + pipe = pipe_first; + } + if (pipe->edesc && + pipe->is_stalled) { + + /* setup a clear-stall packet */ + + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + req.wIndex[0] = pipe->edesc->bEndpointAddress; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + /* copy in the transfer */ + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + /* set length */ + xfer->frlengths[0] = sizeof(req); + xfer->nframes = 1; + mtx_unlock(xfer->usb2_mtx); + + usb2_start_hardware(xfer); + + mtx_lock(xfer->usb2_mtx); + break; + } + pipe++; + if (--to) + goto tr_setup; + break; + + default: + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + goto tr_setup; + } + + /* store current pipe */ + xfer->udev->pipe_curr = pipe; + mtx_unlock(xfer->usb2_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_do_request_flags and usb2_do_request + * + * Description of arguments passed to these functions: + * + * "udev" - this is the "usb2_device" structure pointer on which the + * request should be performed. It is possible to call this function + * in both Host Side mode and Device Side mode. + * + * "mtx" - if this argument is non-NULL the mutex pointed to by it + * will get dropped and picked up during the execution of this + * function, hence this function sometimes needs to sleep. If this + * argument is NULL it has no effect. + * + * "req" - this argument must always be non-NULL and points to an + * 8-byte structure holding the USB request to be done. The USB + * request structure has a bit telling the direction of the USB + * request, if it is a read or a write. + * + * "data" - if the "wLength" part of the structure pointed to by "req" + * is non-zero this argument must point to a valid kernel buffer which + * can hold at least "wLength" bytes. If "wLength" is zero "data" can + * be NULL. + * + * "flags" - here is a list of valid flags: + * + * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than + * specified + * + * o USB_USE_POLLING: forces the transfer to complete from the + * current context by polling the interrupt handler. This flag can be + * used to perform USB transfers after that the kernel has crashed. + * + * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed + * at a later point in time. This is tunable by the "hw.usb.ss_delay" + * sysctl. This flag is mostly useful for debugging. + * + * o USB_USER_DATA_PTR: treat the "data" pointer like a userland + * pointer. + * + * "actlen" - if non-NULL the actual transfer length will be stored in + * the 16-bit unsigned integer pointed to by "actlen". This + * information is mostly useful when the "USB_SHORT_XFER_OK" flag is + * used. + * + * "timeout" - gives the timeout for the control transfer in + * milliseconds. A "timeout" value less than 50 milliseconds is + * treated like a 50 millisecond timeout. A "timeout" value greater + * than 30 seconds is treated like a 30 second timeout. This USB stack + * does not allow control requests without a timeout. + * + * NOTE: This function is thread safe. All calls to + * "usb2_do_request_flags" will be serialised by the use of an + * internal "sx_lock". + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_do_request_flags(struct usb2_device *udev, struct mtx *mtx, + struct usb2_device_request *req, void *data, uint32_t flags, + uint16_t *actlen, uint32_t timeout) +{ + struct usb2_xfer *xfer; + const void *desc; + int err = 0; + uint32_t start_ticks; + uint32_t delta_ticks; + uint32_t max_ticks; + uint16_t length; + uint16_t temp; + + if (timeout < 50) { + /* timeout is too small */ + timeout = 50; + } + if (timeout > 30000) { + /* timeout is too big */ + timeout = 30000; + } + length = UGETW(req->wLength); + + DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " + "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", + udev, req->bmRequestType, req->bRequest, + req->wValue[1], req->wValue[0], + req->wIndex[1], req->wIndex[0], + req->wLength[1], req->wLength[0]); + + /* + * Set "actlen" to a known value in case the caller does not + * check the return value: + */ + if (actlen) { + *actlen = 0; + } + if (udev->flags.usb2_mode == USB_MODE_DEVICE) { + DPRINTF("USB device mode\n"); + (usb2_temp_get_desc_p) (udev, req, &desc, &temp); + if (length > temp) { + if (!(flags & USB_SHORT_XFER_OK)) { + return (USB_ERR_SHORT_XFER); + } + length = temp; + } + if (actlen) { + *actlen = length; + } + if (length > 0) { + if (flags & USB_USER_DATA_PTR) { + if (copyout(desc, data, length)) { + return (USB_ERR_INVAL); + } + } else { + bcopy(desc, data, length); + } + } + return (0); /* success */ + } + if (mtx) { + mtx_unlock(mtx); + if (mtx != &Giant) { + mtx_assert(mtx, MA_NOTOWNED); + } + } + /* + * Grab the default sx-lock so that serialisation + * is achieved when multiple threads are involved: + */ + + sx_xlock(udev->default_sx); + + /* + * Setup a new USB transfer or use the existing one, if any: + */ + usb2_default_transfer_setup(udev); + + xfer = udev->default_xfer[0]; + if (xfer == NULL) { + /* most likely out of memory */ + err = USB_ERR_NOMEM; + goto done; + } + mtx_lock(xfer->priv_mtx); + + if (flags & USB_DELAY_STATUS_STAGE) { + xfer->flags.manual_status = 1; + } else { + xfer->flags.manual_status = 0; + } + + xfer->timeout = timeout; + + start_ticks = ticks; + + max_ticks = USB_MS_TO_TICKS(timeout); + + usb2_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); + + xfer->frlengths[0] = sizeof(*req); + xfer->nframes = 2; + + while (1) { + temp = length; + if (temp > xfer->max_data_length) { + temp = xfer->max_data_length; + } + xfer->frlengths[1] = temp; + + if (temp > 0) { + if (!(req->bmRequestType & UT_READ)) { + if (flags & USB_USER_DATA_PTR) { + mtx_unlock(xfer->priv_mtx); + err = usb2_copy_in_user(xfer->frbuffers + 1, + 0, data, temp); + mtx_lock(xfer->priv_mtx); + if (err) { + err = USB_ERR_INVAL; + break; + } + } else { + usb2_copy_in(xfer->frbuffers + 1, 0, data, temp); + } + } + xfer->nframes = 2; + } else { + if (xfer->frlengths[0] == 0) { + if (xfer->flags.manual_status) { +#if USB_DEBUG + int temp; + + temp = usb2_ss_delay; + if (temp > 5000) { + temp = 5000; + } + if (temp > 0) { + usb2_pause_mtx( + xfer->priv_mtx, temp); + } +#endif + xfer->flags.manual_status = 0; + } else { + break; + } + } + xfer->nframes = 1; + } + + usb2_transfer_start(xfer); + + while (usb2_transfer_pending(xfer)) { + if ((flags & USB_USE_POLLING) || cold) { + usb2_do_poll(udev->default_xfer, USB_DEFAULT_XFER_MAX); + } else { + usb2_cv_wait(xfer->udev->default_cv, xfer->priv_mtx); + } + } + + err = xfer->error; + + if (err) { + break; + } + /* subtract length of SETUP packet, if any */ + + if (xfer->aframes > 0) { + xfer->actlen -= xfer->frlengths[0]; + } else { + xfer->actlen = 0; + } + + /* check for short packet */ + + if (temp > xfer->actlen) { + temp = xfer->actlen; + if (!(flags & USB_SHORT_XFER_OK)) { + err = USB_ERR_SHORT_XFER; + } + length = temp; + } + if (temp > 0) { + if (req->bmRequestType & UT_READ) { + if (flags & USB_USER_DATA_PTR) { + mtx_unlock(xfer->priv_mtx); + err = usb2_copy_out_user(xfer->frbuffers + 1, + 0, data, temp); + mtx_lock(xfer->priv_mtx); + if (err) { + err = USB_ERR_INVAL; + break; + } + } else { + usb2_copy_out(xfer->frbuffers + 1, + 0, data, temp); + } + } + } + /* + * Clear "frlengths[0]" so that we don't send the setup + * packet again: + */ + xfer->frlengths[0] = 0; + + /* update length and data pointer */ + length -= temp; + data = USB_ADD_BYTES(data, temp); + + if (actlen) { + (*actlen) += temp; + } + /* check for timeout */ + + delta_ticks = ticks - start_ticks; + if (delta_ticks > max_ticks) { + if (!err) { + err = USB_ERR_TIMEOUT; + } + } + if (err) { + break; + } + } + + if (err) { + /* + * Make sure that the control endpoint is no longer + * blocked in case of a non-transfer related error: + */ + usb2_transfer_stop(xfer); + } + mtx_unlock(xfer->priv_mtx); + +done: + sx_xunlock(udev->default_sx); + + if (mtx) { + mtx_lock(mtx); + } + return ((usb2_error_t)err); +} + +/*------------------------------------------------------------------------* + * usb2_req_reset_port + * + * This function will instruct an USB HUB to perform a reset sequence + * on the specified port number. + * + * Returns: + * 0: Success. The USB device should now be at address zero. + * Else: Failure. No USB device is present and the USB port should be + * disabled. + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_reset_port(struct usb2_device *udev, struct mtx *mtx, uint8_t port) +{ + struct usb2_port_status ps; + usb2_error_t err; + uint16_t n; + +#if USB_DEBUG + uint16_t pr_poll_delay; + uint16_t pr_recovery_delay; + +#endif + err = usb2_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET); + if (err) { + goto done; + } +#if USB_DEBUG + /* range check input parameters */ + pr_poll_delay = usb2_pr_poll_delay; + if (pr_poll_delay < 1) { + pr_poll_delay = 1; + } else if (pr_poll_delay > 1000) { + pr_poll_delay = 1000; + } + pr_recovery_delay = usb2_pr_recovery_delay; + if (pr_recovery_delay > 1000) { + pr_recovery_delay = 1000; + } +#endif + n = 0; + while (1) { +#if USB_DEBUG + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, pr_poll_delay); + n += pr_poll_delay; +#else + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, USB_PORT_RESET_DELAY); + n += USB_PORT_RESET_DELAY; +#endif + err = usb2_req_get_port_status(udev, mtx, &ps, port); + if (err) { + goto done; + } + /* if the device disappeared, just give up */ + if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { + goto done; + } + /* check if reset is complete */ + if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) { + break; + } + /* check for timeout */ + if (n > 1000) { + n = 0; + break; + } + } + + /* clear port reset first */ + err = usb2_req_clear_port_feature( + udev, mtx, port, UHF_C_PORT_RESET); + if (err) { + goto done; + } + /* check for timeout */ + if (n == 0) { + err = USB_ERR_TIMEOUT; + goto done; + } +#if USB_DEBUG + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, pr_recovery_delay); +#else + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, USB_PORT_RESET_RECOVERY); +#endif + +done: + DPRINTFN(2, "port %d reset returning error=%s\n", + port, usb2_errstr(err)); + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_desc + * + * This function can be used to retrieve USB descriptors. It contains + * some additional logic like zeroing of missing descriptor bytes and + * retrying an USB descriptor in case of failure. The "min_len" + * argument specifies the minimum descriptor length. The "max_len" + * argument specifies the maximum descriptor length. If the real + * descriptor length is less than the minimum length the missing + * byte(s) will be zeroed. The length field, first byte, of the USB + * descriptor will get overwritten in case it indicates a length that + * is too big. Also the type field, second byte, of the USB descriptor + * will get forced to the correct type. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc, + uint16_t min_len, uint16_t max_len, + uint16_t id, uint8_t type, uint8_t index, + uint8_t retries) +{ + struct usb2_device_request req; + uint8_t *buf; + usb2_error_t err; + + DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", + id, type, index, max_len); + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, type, index); + USETW(req.wIndex, id); + + while (1) { + + if ((min_len < 2) || (max_len < 2)) { + err = USB_ERR_INVAL; + goto done; + } + USETW(req.wLength, min_len); + + err = usb2_do_request(udev, mtx, &req, desc); + + if (err) { + if (!retries) { + goto done; + } + retries--; + + usb2_pause_mtx(mtx, 200); + + continue; + } + buf = desc; + + if (min_len == max_len) { + + /* enforce correct type and length */ + + if (buf[0] > min_len) { + buf[0] = min_len; + } + buf[1] = type; + + goto done; + } + /* range check */ + + if (max_len > buf[0]) { + max_len = buf[0]; + } + /* zero minimum data */ + + while (min_len > max_len) { + min_len--; + buf[min_len] = 0; + } + + /* set new minimum length */ + + min_len = max_len; + } +done: + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_string_any + * + * This function will return the string given by "string_index" + * using the first language ID. The maximum length "len" includes + * the terminating zero. The "len" argument should be twice as + * big pluss 2 bytes, compared with the actual maximum string length ! + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_string_any(struct usb2_device *udev, struct mtx *mtx, char *buf, + uint16_t len, uint8_t string_index) +{ + char *s; + uint8_t *temp; + uint16_t i; + uint16_t n; + uint16_t c; + uint8_t swap; + usb2_error_t err; + + if (len == 0) { + /* should not happen */ + return (USB_ERR_NORMAL_COMPLETION); + } + buf[0] = 0; + + if (string_index == 0) { + /* this is the language table */ + return (USB_ERR_INVAL); + } + if (udev->flags.no_strings) { + return (USB_ERR_STALLED); + } + err = usb2_req_get_string_desc + (udev, mtx, buf, len, udev->langid, string_index); + if (err) { + return (err); + } + temp = (uint8_t *)buf; + + if (temp[0] < 2) { + /* string length is too short */ + return (USB_ERR_INVAL); + } + /* reserve one byte for terminating zero */ + len--; + + /* find maximum length */ + s = buf; + n = (temp[0] / 2) - 1; + if (n > len) { + n = len; + } + /* skip descriptor header */ + temp += 2; + + /* reset swap state */ + swap = 3; + + /* convert and filter */ + for (i = 0; (i != n); i++) { + c = UGETW(temp + (2 * i)); + + /* convert from Unicode, handle buggy strings */ + if (((c & 0xff00) == 0) && (swap & 1)) { + /* Little Endian, default */ + *s = c; + swap = 1; + } else if (((c & 0x00ff) == 0) && (swap & 2)) { + /* Big Endian */ + *s = c >> 8; + swap = 2; + } else { + *s = '.'; + } + + /* + * Filter by default - we don't allow greater and less than + * signs because they might confuse the dmesg printouts! + */ + if ((*s == '<') || (*s == '>') || (!isprint(*s))) { + *s = '.'; + } + s++; + } + *s = 0; + return (USB_ERR_NORMAL_COMPLETION); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_string_desc + * + * If you don't know the language ID, consider using + * "usb2_req_get_string_any()". + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_string_desc(struct usb2_device *udev, struct mtx *mtx, void *sdesc, + uint16_t max_len, uint16_t lang_id, + uint8_t string_index) +{ + return (usb2_req_get_desc(udev, mtx, sdesc, 2, max_len, lang_id, + UDESC_STRING, string_index, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_config_desc + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_config_desc(struct usb2_device *udev, struct mtx *mtx, + struct usb2_config_descriptor *d, uint8_t conf_index) +{ + usb2_error_t err; + + DPRINTFN(4, "confidx=%d\n", conf_index); + + err = usb2_req_get_desc(udev, mtx, d, sizeof(*d), + sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); + if (err) { + goto done; + } + /* Extra sanity checking */ + if (UGETW(d->wTotalLength) < sizeof(*d)) { + err = USB_ERR_INVAL; + } +done: + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_config_desc_full + * + * This function gets the complete USB configuration descriptor and + * ensures that "wTotalLength" is correct. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_config_desc_full(struct usb2_device *udev, struct mtx *mtx, + struct usb2_config_descriptor **ppcd, struct malloc_type *mtype, + uint8_t index) +{ + struct usb2_config_descriptor cd; + struct usb2_config_descriptor *cdesc; + uint16_t len; + usb2_error_t err; + + DPRINTFN(4, "index=%d\n", index); + + *ppcd = NULL; + + err = usb2_req_get_config_desc(udev, mtx, &cd, index); + if (err) { + return (err); + } + /* get full descriptor */ + len = UGETW(cd.wTotalLength); + if (len < sizeof(*cdesc)) { + /* corrupt descriptor */ + return (USB_ERR_INVAL); + } + cdesc = malloc(len, mtype, M_WAITOK); + if (cdesc == NULL) { + return (USB_ERR_NOMEM); + } + err = usb2_req_get_desc(udev, mtx, cdesc, len, len, 0, + UDESC_CONFIG, index, 3); + if (err) { + free(cdesc, mtype); + return (err); + } + /* make sure that the device is not fooling us: */ + USETW(cdesc->wTotalLength, len); + + *ppcd = cdesc; + + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_req_get_device_desc + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_device_desc(struct usb2_device *udev, struct mtx *mtx, + struct usb2_device_descriptor *d) +{ + DPRINTFN(4, "\n"); + return (usb2_req_get_desc(udev, mtx, d, sizeof(*d), + sizeof(*d), 0, UDESC_DEVICE, 0, 3)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_alt_interface_no + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, + uint8_t *alt_iface_no, uint8_t iface_index) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + req.bmRequestType = UT_READ_INTERFACE; + req.bRequest = UR_GET_INTERFACE; + USETW(req.wValue, 0); + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + return (usb2_do_request(udev, mtx, &req, alt_iface_no)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_alt_interface_no + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, + uint8_t iface_index, uint8_t alt_no) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + req.bmRequestType = UT_WRITE_INTERFACE; + req.bRequest = UR_SET_INTERFACE; + req.wValue[0] = alt_no; + req.wValue[1] = 0; + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_device_status + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_device_status(struct usb2_device *udev, struct mtx *mtx, + struct usb2_status *st) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(*st)); + return (usb2_do_request(udev, mtx, &req, st)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_hub_descriptor + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_hub_descriptor(struct usb2_device *udev, struct mtx *mtx, + struct usb2_hub_descriptor *hd, uint8_t nports) +{ + struct usb2_device_request req; + uint16_t len = (nports + 7 + (8 * 8)) / 8; + + req.bmRequestType = UT_READ_CLASS_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_HUB, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + return (usb2_do_request(udev, mtx, &req, hd)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_hub_status + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_hub_status(struct usb2_device *udev, struct mtx *mtx, + struct usb2_hub_status *st) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_CLASS_DEVICE; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(struct usb2_hub_status)); + return (usb2_do_request(udev, mtx, &req, st)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_address + * + * This function is used to set the address for an USB device. After + * port reset the USB device will respond at address zero. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_address(struct usb2_device *udev, struct mtx *mtx, uint16_t addr) +{ + struct usb2_device_request req; + + DPRINTFN(6, "setting device address=%d\n", addr); + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + /* Setting the address should not take more than 1 second ! */ + return (usb2_do_request_flags(udev, mtx, &req, NULL, + USB_DELAY_STATUS_STAGE, NULL, 1000)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_port_status + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_port_status(struct usb2_device *udev, struct mtx *mtx, + struct usb2_port_status *ps, uint8_t port) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_CLASS_OTHER; + req.bRequest = UR_GET_STATUS; + USETW(req.wValue, 0); + req.wIndex[0] = port; + req.wIndex[1] = 0; + USETW(req.wLength, sizeof *ps); + return (usb2_do_request(udev, mtx, &req, ps)); +} + +/*------------------------------------------------------------------------* + * usb2_req_clear_hub_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_clear_hub_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request 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 (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_hub_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_hub_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request 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 (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_clear_port_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_clear_port_feature(struct usb2_device *udev, struct mtx *mtx, + uint8_t port, uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, sel); + req.wIndex[0] = port; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_port_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_port_feature(struct usb2_device *udev, struct mtx *mtx, + uint8_t port, uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, sel); + req.wIndex[0] = port; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_protocol + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_protocol(struct usb2_device *udev, struct mtx *mtx, + uint8_t iface_index, uint16_t report) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", + iface, report, iface->idesc->bInterfaceNumber); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_PROTOCOL; + USETW(req.wValue, report); + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_report + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, + uint8_t iface_index, uint8_t type, uint8_t id) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + DPRINTFN(5, "len=%d\n", len); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, type, id); + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, len); + return (usb2_do_request(udev, mtx, &req, data)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_report + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_report(struct usb2_device *udev, struct mtx *mtx, void *data, + uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { + return (USB_ERR_INVAL); + } + DPRINTFN(5, "len=%d\n", len); + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_REPORT; + USETW2(req.wValue, type, id); + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, len); + return (usb2_do_request(udev, mtx, &req, data)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_idle + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_idle(struct usb2_device *udev, struct mtx *mtx, + uint8_t iface_index, uint8_t duration, uint8_t id) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + DPRINTFN(5, "%d %d\n", duration, id); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_IDLE; + USETW2(req.wValue, duration, id); + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_report_descriptor + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_report_descriptor(struct usb2_device *udev, struct mtx *mtx, + void *d, uint16_t size, uint8_t iface_index) +{ + struct usb2_interface *iface = usb2_get_iface(udev, iface_index); + struct usb2_device_request req; + + if ((iface == NULL) || (iface->idesc == NULL)) { + return (USB_ERR_INVAL); + } + req.bmRequestType = UT_READ_INTERFACE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ + req.wIndex[0] = iface->idesc->bInterfaceNumber; + req.wIndex[1] = 0; + USETW(req.wLength, size); + return (usb2_do_request(udev, mtx, &req, d)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_config + * + * This function is used to select the current configuration number in + * both USB device side mode and USB host side mode. When setting the + * configuration the function of the interfaces can change. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_config(struct usb2_device *udev, struct mtx *mtx, uint8_t conf) +{ + struct usb2_device_request req; + + DPRINTF("setting config %d\n", conf); + + /* do "set configuration" request */ + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_CONFIG; + req.wValue[0] = conf; + req.wValue[1] = 0; + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_get_config + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_get_config(struct usb2_device *udev, struct mtx *mtx, uint8_t *pconf) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_CONFIG; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 1); + return (usb2_do_request(udev, mtx, &req, pconf)); +} + +/*------------------------------------------------------------------------* + * usb2_req_re_enumerate + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) +{ + struct usb2_device_descriptor ddesc; + struct usb2_device *parent_hub; + usb2_error_t err; + uint8_t old_addr; + + old_addr = udev->address; + parent_hub = udev->parent_hub; + if (parent_hub == NULL) { + err = USB_ERR_INVAL; + goto done; + } + err = usb2_req_reset_port(parent_hub, mtx, udev->port_no); + if (err) { + DPRINTFN(0, "addr=%d, port reset failed\n", old_addr); + goto done; + } + /* + * After that the port has been reset our device should be at + * address zero: + */ + udev->address = USB_START_ADDR; + + /* + * Restore device address: + */ + err = usb2_req_set_address(udev, mtx, old_addr); + if (err) { + /* XXX ignore any errors! */ + DPRINTFN(0, "addr=%d, set address failed\n", + old_addr); + err = 0; + } + /* restore device address */ + udev->address = old_addr; + + /* allow device time to set new address */ + usb2_pause_mtx(mtx, USB_SET_ADDRESS_SETTLE); + + /* get the device descriptor */ + err = usb2_req_get_device_desc(udev, mtx, &ddesc); + if (err) { + DPRINTFN(0, "addr=%d, getting device " + "descriptor failed!\n", old_addr); + goto done; + } +done: + /* restore address */ + udev->address = old_addr; + + if (err == 0) { + /* restore configuration */ + err = usb2_req_set_config(udev, mtx, udev->curr_config_no); + /* wait a little bit, just in case */ + usb2_pause_mtx(mtx, 10); + } + return (err); +} diff --git a/sys/dev/usb2/core/usb2_request.h b/sys/dev/usb2/core/usb2_request.h new file mode 100644 index 000000000000..f2b642291135 --- /dev/null +++ b/sys/dev/usb2/core/usb2_request.h @@ -0,0 +1,61 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_REQUEST_H_ +#define _USB2_REQUEST_H_ + +usb2_error_t usb2_do_request_flags(struct usb2_device *udev, struct mtx *mtx, struct usb2_device_request *req, void *data, uint32_t flags, uint16_t *actlen, uint32_t timeout); +usb2_error_t usb2_req_clear_hub_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); +usb2_error_t usb2_req_clear_port_feature(struct usb2_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel); +usb2_error_t usb2_req_get_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, uint8_t *alt_iface_no, uint8_t iface_index); +usb2_error_t usb2_req_get_config(struct usb2_device *udev, struct mtx *mtx, uint8_t *pconf); +usb2_error_t usb2_req_get_config_desc(struct usb2_device *udev, struct mtx *mtx, struct usb2_config_descriptor *d, uint8_t conf_index); +usb2_error_t usb2_req_get_config_desc_full(struct usb2_device *udev, struct mtx *mtx, struct usb2_config_descriptor **ppcd, struct malloc_type *mtype, uint8_t conf_index); +usb2_error_t usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc, uint16_t min_len, uint16_t max_len, uint16_t id, uint8_t type, uint8_t index, uint8_t retries); +usb2_error_t usb2_req_get_device_desc(struct usb2_device *udev, struct mtx *mtx, struct usb2_device_descriptor *d); +usb2_error_t usb2_req_get_device_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_status *st); +usb2_error_t usb2_req_get_hub_descriptor(struct usb2_device *udev, struct mtx *mtx, struct usb2_hub_descriptor *hd, uint8_t nports); +usb2_error_t usb2_req_get_hub_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_hub_status *st); +usb2_error_t usb2_req_get_port_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_port_status *ps, uint8_t port); +usb2_error_t usb2_req_get_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id); +usb2_error_t usb2_req_get_report_descriptor(struct usb2_device *udev, struct mtx *mtx, void *d, uint16_t size, uint8_t iface_index); +usb2_error_t usb2_req_get_string_any(struct usb2_device *udev, struct mtx *mtx, char *buf, uint16_t len, uint8_t string_index); +usb2_error_t usb2_req_get_string_desc(struct usb2_device *udev, struct mtx *mtx, void *sdesc, uint16_t max_len, uint16_t lang_id, uint8_t string_index); +usb2_error_t usb2_req_reset_port(struct usb2_device *udev, struct mtx *mtx, uint8_t port); +usb2_error_t usb2_req_set_address(struct usb2_device *udev, struct mtx *mtx, uint16_t addr); +usb2_error_t usb2_req_set_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t alt_no); +usb2_error_t usb2_req_set_config(struct usb2_device *udev, struct mtx *mtx, uint8_t conf); +usb2_error_t usb2_req_set_hub_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); +usb2_error_t usb2_req_set_idle(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t duration, uint8_t id); +usb2_error_t usb2_req_set_port_feature(struct usb2_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel); +usb2_error_t usb2_req_set_protocol(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint16_t report); +usb2_error_t usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id); +usb2_error_t usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx); + +#define usb2_do_request(u,m,r,d) \ + usb2_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) + +#endif /* _USB2_REQUEST_H_ */ diff --git a/sys/dev/usb2/core/usb2_sw_transfer.c b/sys/dev/usb2/core/usb2_sw_transfer.c new file mode 100644 index 000000000000..bb825028a907 --- /dev/null +++ b/sys/dev/usb2/core/usb2_sw_transfer.c @@ -0,0 +1,166 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------------------------* + * usb2_sw_transfer - factored out code + * + * This function is basically used for the Virtual Root HUB, and can + * emulate control, bulk and interrupt endpoints. Data is exchanged + * using the "std->ptr" and "std->len" fields, that allows kernel + * virtual memory to be transferred. All state is kept in the + * structure pointed to by the "std" argument passed to this + * function. The "func" argument points to a function that is called + * back in the various states, so that the application using this + * function can get a chance to select the outcome. The "func" + * function is allowed to sleep, exiting all mutexes. If this function + * will sleep the "enter" and "start" methods must be marked + * non-cancelable, hence there is no extra cancelled checking in this + * function. + *------------------------------------------------------------------------*/ +void +usb2_sw_transfer(struct usb2_sw_transfer *std, + usb2_sw_transfer_func_t *func) +{ + struct usb2_xfer *xfer; + uint32_t len; + uint8_t shortpkt = 0; + + xfer = std->xfer; + if (xfer == NULL) { + /* the transfer is gone */ + DPRINTF("xfer gone\n"); + return; + } + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + std->xfer = NULL; + + /* check for control transfer */ + if (xfer->flags_int.control_xfr) { + /* check if we are transferring the SETUP packet */ + if (xfer->flags_int.control_hdr) { + + /* copy out the USB request */ + + if (xfer->frlengths[0] == sizeof(std->req)) { + usb2_copy_out(xfer->frbuffers, 0, + &std->req, sizeof(std->req)); + } else { + std->err = USB_ERR_INVAL; + goto done; + } + + xfer->aframes = 1; + + std->err = 0; + std->state = USB_SW_TR_SETUP; + + (func) (xfer, std); + + if (std->err) { + goto done; + } + } else { + /* skip the first frame in this case */ + xfer->aframes = 1; + } + } + std->err = 0; + std->state = USB_SW_TR_PRE_DATA; + + (func) (xfer, std); + + if (std->err) { + goto done; + } + /* Transfer data. Iterate accross all frames. */ + while (xfer->aframes != xfer->nframes) { + + len = xfer->frlengths[xfer->aframes]; + + if (len > std->len) { + len = std->len; + shortpkt = 1; + } + if (len > 0) { + if ((xfer->endpoint & (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN) { + usb2_copy_in(xfer->frbuffers + xfer->aframes, 0, + std->ptr, len); + } else { + usb2_copy_out(xfer->frbuffers + xfer->aframes, 0, + std->ptr, len); + } + } + std->ptr += len; + std->len -= len; + xfer->frlengths[xfer->aframes] = len; + xfer->aframes++; + + if (shortpkt) { + break; + } + } + + std->err = 0; + std->state = USB_SW_TR_POST_DATA; + + (func) (xfer, std); + + if (std->err) { + goto done; + } + /* check if the control transfer is complete */ + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) { + + std->err = 0; + std->state = USB_SW_TR_STATUS; + + (func) (xfer, std); + + if (std->err) { + goto done; + } + } +done: + DPRINTF("done err=%s\n", usb2_errstr(std->err)); + std->state = USB_SW_TR_PRE_CALLBACK; + (func) (xfer, std); + return; +} diff --git a/sys/dev/usb2/core/usb2_sw_transfer.h b/sys/dev/usb2/core/usb2_sw_transfer.h new file mode 100644 index 000000000000..c90ba3663f77 --- /dev/null +++ b/sys/dev/usb2/core/usb2_sw_transfer.h @@ -0,0 +1,61 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_SW_TRANSFER_H_ +#define _USB2_SW_TRANSFER_H_ + +/* Software transfer function state argument values */ + +enum { + USB_SW_TR_SETUP, + USB_SW_TR_STATUS, + USB_SW_TR_PRE_DATA, + USB_SW_TR_POST_DATA, + USB_SW_TR_PRE_CALLBACK, +}; + +struct usb2_sw_transfer; + +typedef void (usb2_sw_transfer_func_t)(struct usb2_xfer *, struct usb2_sw_transfer *); + +/* + * The following structure is used to keep the state of a standard + * root transfer. + */ +struct usb2_sw_transfer { + struct usb2_device_request req; + struct usb2_xfer *xfer; + uint8_t *ptr; + uint16_t len; + uint8_t state; + usb2_error_t err; +}; + +/* prototypes */ + +void usb2_sw_transfer(struct usb2_sw_transfer *std, usb2_sw_transfer_func_t *func); + +#endif /* _USB2_SW_TRANSFER_H_ */ diff --git a/sys/dev/usb2/core/usb2_transfer.c b/sys/dev/usb2/core/usb2_transfer.c new file mode 100644 index 000000000000..21676243e6eb --- /dev/null +++ b/sys/dev/usb2/core/usb2_transfer.c @@ -0,0 +1,2833 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct usb2_std_packet_size { + struct { + uint16_t min; /* inclusive */ + uint16_t max; /* inclusive */ + } range; + + uint16_t fixed[4]; +}; + +/* + * This table stores the all the allowed packet sizes based on + * endpoint type and USB speed: + */ +static const struct usb2_std_packet_size + usb2_std_packet_size[4][USB_SPEED_MAX] = { + + [UE_INTERRUPT] = { + [USB_SPEED_LOW] = {.range = {0, 8}}, + [USB_SPEED_FULL] = {.range = {0, 64}}, + [USB_SPEED_HIGH] = {.range = {0, 1024}}, + [USB_SPEED_VARIABLE] = {.range = {0, 1024}}, + }, + + [UE_CONTROL] = { + [USB_SPEED_LOW] = {.fixed = {8, 8, 8, 8}}, + [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}}, + [USB_SPEED_HIGH] = {.fixed = {64, 64, 64, 64}}, + [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 512, 512}}, + }, + + [UE_BULK] = { + [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */ + [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}}, + [USB_SPEED_HIGH] = {.fixed = {512, 512, 512, 512}}, + [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 1024, 1536}}, + }, + + [UE_ISOCHRONOUS] = { + [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */ + [USB_SPEED_FULL] = {.range = {0, 1023}}, + [USB_SPEED_HIGH] = {.range = {0, 1024}}, + [USB_SPEED_VARIABLE] = {.range = {0, 3584}}, + }, +}; + +static const struct usb2_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = { + + /* This transfer is used for generic control endpoint transfers */ + + [0] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control endpoint */ + .direction = UE_DIR_ANY, + .mh.bufsize = 1024, /* bytes */ + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &usb2_do_request_callback, + .md.bufsize = 1024, /* bytes */ + .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 0,}, + .md.callback = &usb2_handle_request_callback, + }, + + /* This transfer is used for generic clear stall only */ + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &usb2_do_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +/* function prototypes */ + +static void usb2_update_max_frame_size(struct usb2_xfer *xfer); +static uint32_t usb2_get_dma_delay(struct usb2_bus *bus); +static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *info, uint8_t needs_delay); +static void usb2_control_transfer_init(struct usb2_xfer *xfer); +static uint8_t usb2_start_hardware_sub(struct usb2_xfer *xfer); +static void usb2_callback_proc(struct usb2_proc_msg *_pm); +static void usb2_callback_ss_done_defer(struct usb2_xfer *xfer); +static void usb2_callback_wrapper(struct usb2_xfer_queue *pq); +static void usb2_dma_delay_done_cb(void *arg); +static void usb2_transfer_start_cb(void *arg); +static uint8_t usb2_callback_wrapper_sub(struct usb2_xfer *xfer); + +/*------------------------------------------------------------------------* + * usb2_update_max_frame_size + * + * This function updates the maximum frame size, hence high speed USB + * can transfer multiple consecutive packets. + *------------------------------------------------------------------------*/ +static void +usb2_update_max_frame_size(struct usb2_xfer *xfer) +{ + /* compute maximum frame size */ + + if (xfer->max_packet_count == 2) { + xfer->max_frame_size = 2 * xfer->max_packet_size; + } else if (xfer->max_packet_count == 3) { + xfer->max_frame_size = 3 * xfer->max_packet_size; + } else { + xfer->max_frame_size = xfer->max_packet_size; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_get_dma_delay + * + * The following function is called when we need to + * synchronize with DMA hardware. + * + * Returns: + * 0: no DMA delay required + * Else: milliseconds of DMA delay + *------------------------------------------------------------------------*/ +static uint32_t +usb2_get_dma_delay(struct usb2_bus *bus) +{ + uint32_t temp = 0; + + if (bus->methods->get_dma_delay) { + (bus->methods->get_dma_delay) (bus, &temp); + /* + * Round up and convert to milliseconds. Note that we use + * 1024 milliseconds per second. to save a division. + */ + temp += 0x3FF; + temp /= 0x400; + } + return (temp); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_setup_sub_malloc + * + * This function will allocate one or more DMA'able memory chunks + * according to "size", "align" and "count" arguments. "ppc" is + * pointed to a linear array of USB page caches afterwards. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb2_transfer_setup_sub_malloc(struct usb2_setup_params *parm, + struct usb2_page_cache **ppc, uint32_t size, uint32_t align, + uint32_t count) +{ + struct usb2_page_cache *pc; + struct usb2_page *pg; + void *buf; + uint32_t n_dma_pc; + uint32_t n_obj; + uint32_t x; + uint32_t y; + uint32_t r; + uint32_t z; + + USB_ASSERT(align > 1, ("Invalid alignment, 0x%08x!\n", + align)); + USB_ASSERT(size > 0, ("Invalid size = 0!\n")); + + if (count == 0) { + return (0); /* nothing to allocate */ + } + /* + * Make sure that the size is aligned properly. + */ + size = -((-size) & (-align)); + + /* + * Try multi-allocation chunks to reduce the number of DMA + * allocations, hence DMA allocations are slow. + */ + if (size >= PAGE_SIZE) { + n_dma_pc = count; + n_obj = 1; + } else { + /* compute number of objects per page */ + n_obj = (PAGE_SIZE / size); + /* + * Compute number of DMA chunks, rounded up + * to nearest one: + */ + n_dma_pc = ((count + n_obj - 1) / n_obj); + } + + if (parm->buf == NULL) { + /* for the future */ + parm->dma_page_ptr += n_dma_pc; + parm->dma_page_cache_ptr += n_dma_pc; + parm->dma_page_ptr += count; + parm->xfer_page_cache_ptr += count; + return (0); + } + for (x = 0; x != n_dma_pc; x++) { + /* need to initialize the page cache */ + parm->dma_page_cache_ptr[x].tag_parent = + &parm->curr_xfer->usb2_root->dma_parent_tag; + } + for (x = 0; x != count; x++) { + /* need to initialize the page cache */ + parm->xfer_page_cache_ptr[x].tag_parent = + &parm->curr_xfer->usb2_root->dma_parent_tag; + } + + if (ppc) { + *ppc = parm->xfer_page_cache_ptr; + } + r = count; /* set remainder count */ + z = n_obj * size; /* set allocation size */ + pc = parm->xfer_page_cache_ptr; + pg = parm->dma_page_ptr; + + for (x = 0; x != n_dma_pc; x++) { + + if (r < n_obj) { + /* compute last remainder */ + z = r * size; + n_obj = r; + } + if (usb2_pc_alloc_mem(parm->dma_page_cache_ptr, + pg, z, align)) { + return (1); /* failure */ + } + /* Set beginning of current buffer */ + buf = parm->dma_page_cache_ptr->buffer; + /* Make room for one DMA page cache and one page */ + parm->dma_page_cache_ptr++; + pg++; + + for (y = 0; (y != n_obj); y++, r--, pc++, pg++) { + + /* Load sub-chunk into DMA */ + if (usb2_pc_dmamap_create(pc, size)) { + return (1); /* failure */ + } + pc->buffer = USB_ADD_BYTES(buf, y * size); + pc->page_start = pg; + + mtx_lock(pc->tag_parent->mtx); + if (usb2_pc_load_mem(pc, size, 1 /* synchronous */ )) { + mtx_unlock(pc->tag_parent->mtx); + return (1); /* failure */ + } + mtx_unlock(pc->tag_parent->mtx); + } + } + + parm->xfer_page_cache_ptr = pc; + parm->dma_page_ptr = pg; + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_setup_sub - transfer setup subroutine + * + * This function must be called from the "xfer_setup" callback of the + * USB Host or Device controller driver when setting up an USB + * transfer. This function will setup correct packet sizes, buffer + * sizes, flags and more, that are stored in the "usb2_xfer" + * structure. + *------------------------------------------------------------------------*/ +void +usb2_transfer_setup_sub(struct usb2_setup_params *parm) +{ + enum { + REQ_SIZE = 8, + MIN_PKT = 8, + }; + struct usb2_xfer *xfer = parm->curr_xfer; + const struct usb2_config_sub *setup_sub = parm->curr_setup_sub; + struct usb2_endpoint_descriptor *edesc; + struct usb2_std_packet_size std_size; + uint32_t n_frlengths; + uint32_t n_frbuffers; + uint32_t x; + uint8_t type; + uint8_t zmps; + + /* + * Sanity check. The following parameters must be initialized before + * calling this function. + */ + if ((parm->hc_max_packet_size == 0) || + (parm->hc_max_packet_count == 0) || + (parm->hc_max_frame_size == 0)) { + parm->err = USB_ERR_INVAL; + goto done; + } + edesc = xfer->pipe->edesc; + + type = (edesc->bmAttributes & UE_XFERTYPE); + + xfer->flags = setup_sub->flags; + xfer->nframes = setup_sub->frames; + xfer->timeout = setup_sub->timeout; + xfer->callback = setup_sub->callback; + xfer->interval = setup_sub->interval; + xfer->endpoint = edesc->bEndpointAddress; + xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); + xfer->max_packet_count = 1; + /* make a shadow copy: */ + xfer->flags_int.usb2_mode = parm->udev->flags.usb2_mode; + + parm->bufsize = setup_sub->bufsize; + + if (parm->speed == USB_SPEED_HIGH) { + xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; + xfer->max_packet_size &= 0x7FF; + } + /* range check "max_packet_count" */ + + if (xfer->max_packet_count > parm->hc_max_packet_count) { + xfer->max_packet_count = parm->hc_max_packet_count; + } + /* filter "wMaxPacketSize" according to HC capabilities */ + + if ((xfer->max_packet_size > parm->hc_max_packet_size) || + (xfer->max_packet_size == 0)) { + xfer->max_packet_size = parm->hc_max_packet_size; + } + /* filter "wMaxPacketSize" according to standard sizes */ + + std_size = usb2_std_packet_size[type][parm->speed]; + + if (std_size.range.min || std_size.range.max) { + + if (xfer->max_packet_size < std_size.range.min) { + xfer->max_packet_size = std_size.range.min; + } + if (xfer->max_packet_size > std_size.range.max) { + xfer->max_packet_size = std_size.range.max; + } + } else { + + if (xfer->max_packet_size >= std_size.fixed[3]) { + xfer->max_packet_size = std_size.fixed[3]; + } else if (xfer->max_packet_size >= std_size.fixed[2]) { + xfer->max_packet_size = std_size.fixed[2]; + } else if (xfer->max_packet_size >= std_size.fixed[1]) { + xfer->max_packet_size = std_size.fixed[1]; + } else { + /* only one possibility left */ + xfer->max_packet_size = std_size.fixed[0]; + } + } + + /* compute "max_frame_size" */ + + usb2_update_max_frame_size(xfer); + + /* check interrupt interval and transfer pre-delay */ + + if (type == UE_ISOCHRONOUS) { + + uint32_t frame_limit; + + xfer->interval = 0; /* not used, must be zero */ + xfer->flags_int.isochronous_xfr = 1; /* set flag */ + + if (xfer->timeout == 0) { + /* + * set a default timeout in + * case something goes wrong! + */ + xfer->timeout = 1000 / 4; + } + if (parm->speed == USB_SPEED_HIGH) { + frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; + } else { + frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; + } + + if (xfer->nframes > frame_limit) { + /* + * this is not going to work + * cross hardware + */ + parm->err = USB_ERR_INVAL; + goto done; + } + if (xfer->nframes == 0) { + /* + * this is not a valid value + */ + parm->err = USB_ERR_ZERO_NFRAMES; + goto done; + } + } else { + + /* + * if a value is specified use that else check the endpoint + * descriptor + */ + if (xfer->interval == 0) { + + if (type == UE_INTERRUPT) { + + xfer->interval = edesc->bInterval; + + if (parm->speed == USB_SPEED_HIGH) { + xfer->interval /= 8; /* 125us -> 1ms */ + } + if (xfer->interval == 0) { + /* + * one millisecond is the smallest + * interval + */ + xfer->interval = 1; + } + } + } + } + + /* + * NOTE: we do not allow "max_packet_size" or "max_frame_size" + * to be equal to zero when setting up USB transfers, hence + * this leads to alot of extra code in the USB kernel. + */ + + if ((xfer->max_frame_size == 0) || + (xfer->max_packet_size == 0)) { + + zmps = 1; + + if ((parm->bufsize <= MIN_PKT) && + (type != UE_CONTROL) && + (type != UE_BULK)) { + + /* workaround */ + xfer->max_packet_size = MIN_PKT; + xfer->max_packet_count = 1; + parm->bufsize = 0; /* automatic setup length */ + usb2_update_max_frame_size(xfer); + + } else { + parm->err = USB_ERR_ZERO_MAXP; + goto done; + } + + } else { + zmps = 0; + } + + /* + * check if we should setup a default + * length: + */ + + if (parm->bufsize == 0) { + + parm->bufsize = xfer->max_frame_size; + + if (type == UE_ISOCHRONOUS) { + parm->bufsize *= xfer->nframes; + } + } + /* + * check if we are about to setup a proxy + * type of buffer: + */ + + if (xfer->flags.proxy_buffer) { + + /* round bufsize up */ + + parm->bufsize += (xfer->max_frame_size - 1); + + if (parm->bufsize < xfer->max_frame_size) { + /* length wrapped around */ + parm->err = USB_ERR_INVAL; + goto done; + } + /* subtract remainder */ + + parm->bufsize -= (parm->bufsize % xfer->max_frame_size); + + /* add length of USB device request structure, if any */ + + if (type == UE_CONTROL) { + parm->bufsize += REQ_SIZE; /* SETUP message */ + } + } + xfer->max_data_length = parm->bufsize; + + /* Setup "n_frlengths" and "n_frbuffers" */ + + if (type == UE_ISOCHRONOUS) { + n_frlengths = xfer->nframes; + n_frbuffers = 1; + } else { + + if (type == UE_CONTROL) { + xfer->flags_int.control_xfr = 1; + if (xfer->nframes == 0) { + if (parm->bufsize <= REQ_SIZE) { + /* + * there will never be any data + * stage + */ + xfer->nframes = 1; + } else { + xfer->nframes = 2; + } + } + } else { + if (xfer->nframes == 0) { + xfer->nframes = 1; + } + } + + n_frlengths = xfer->nframes; + n_frbuffers = xfer->nframes; + } + + /* + * check if we have room for the + * USB device request structure: + */ + + if (type == UE_CONTROL) { + + if (xfer->max_data_length < REQ_SIZE) { + /* length wrapped around or too small bufsize */ + parm->err = USB_ERR_INVAL; + goto done; + } + xfer->max_data_length -= REQ_SIZE; + } + /* setup "frlengths" */ + + xfer->frlengths = parm->xfer_length_ptr; + + parm->xfer_length_ptr += n_frlengths; + + /* setup "frbuffers" */ + + xfer->frbuffers = parm->xfer_page_cache_ptr; + + parm->xfer_page_cache_ptr += n_frbuffers; + + /* + * check if we need to setup + * a local buffer: + */ + + if (!xfer->flags.ext_buffer) { + + /* align data */ + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); + + if (parm->buf) { + + xfer->local_buffer = + USB_ADD_BYTES(parm->buf, parm->size[0]); + + usb2_set_frame_offset(xfer, 0, 0); + + if ((type == UE_CONTROL) && (n_frbuffers > 1)) { + usb2_set_frame_offset(xfer, REQ_SIZE, 1); + } + } + parm->size[0] += parm->bufsize; + + /* align data again */ + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); + } + /* + * Compute maximum buffer size + */ + + if (parm->bufsize_max < parm->bufsize) { + parm->bufsize_max = parm->bufsize; + } + if (xfer->flags_int.bdma_enable) { + /* + * Setup "dma_page_ptr". + * + * Proof for formula below: + * + * Assume there are three USB frames having length "a", "b" and + * "c". These USB frames will at maximum need "z" + * "usb2_page" structures. "z" is given by: + * + * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + + * ((c / USB_PAGE_SIZE) + 2); + * + * Constraining "a", "b" and "c" like this: + * + * (a + b + c) <= parm->bufsize + * + * We know that: + * + * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); + * + * Here is the general formula: + */ + xfer->dma_page_ptr = parm->dma_page_ptr; + parm->dma_page_ptr += (2 * n_frbuffers); + parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); + } + if (zmps) { + /* correct maximum data length */ + xfer->max_data_length = 0; + } + /* subtract USB frame remainder from "hc_max_frame_size" */ + + xfer->max_usb2_frame_size = + (parm->hc_max_frame_size - + (parm->hc_max_frame_size % xfer->max_frame_size)); + + if (xfer->max_usb2_frame_size == 0) { + parm->err = USB_ERR_INVAL; + goto done; + } + /* initialize max frame count */ + + xfer->max_frame_count = xfer->nframes; + + /* initialize frame buffers */ + + if (parm->buf) { + for (x = 0; x != n_frbuffers; x++) { + xfer->frbuffers[x].tag_parent = + &xfer->usb2_root->dma_parent_tag; + + if (xfer->flags_int.bdma_enable && + (parm->bufsize_max > 0)) { + + if (usb2_pc_dmamap_create( + xfer->frbuffers + x, + parm->bufsize_max)) { + parm->err = USB_ERR_NOMEM; + goto done; + } + } + } + } +done: + if (parm->err) { + /* + * Set some dummy values so that we avoid division by zero: + */ + xfer->max_usb2_frame_size = 1; + xfer->max_frame_size = 1; + xfer->max_packet_size = 1; + xfer->max_data_length = 0; + xfer->nframes = 0; + xfer->max_frame_count = 0; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_setup - setup an array of USB transfers + * + * NOTE: You must always call "usb2_transfer_unsetup" after calling + * "usb2_transfer_setup" if success was returned. + * + * The idea is that the USB device driver should pre-allocate all its + * transfers by one call to this function. + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_transfer_setup(struct usb2_device *udev, + const uint8_t *ifaces, struct usb2_xfer **ppxfer, + const struct usb2_config *setup_start, uint16_t n_setup, + void *priv_sc, struct mtx *priv_mtx) +{ + struct usb2_xfer dummy; + struct usb2_setup_params parm; + const struct usb2_config *setup_end = setup_start + n_setup; + const struct usb2_config *setup; + struct usb2_pipe *pipe; + struct usb2_xfer_root *info; + struct usb2_xfer *xfer; + void *buf = NULL; + uint16_t n; + uint16_t refcount; + + parm.err = 0; + refcount = 0; + info = NULL; + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "usb2_transfer_setup can sleep!"); + + /* do some checking first */ + + if (n_setup == 0) { + DPRINTFN(6, "setup array has zero length!\n"); + return (USB_ERR_INVAL); + } + if (ifaces == 0) { + DPRINTFN(6, "ifaces array is NULL!\n"); + return (USB_ERR_INVAL); + } + if (priv_mtx == NULL) { + DPRINTFN(6, "using global lock\n"); + priv_mtx = &Giant; + } + /* sanity checks */ + for (setup = setup_start, n = 0; + setup != setup_end; setup++, n++) { + if ((setup->mh.bufsize == 0xffffffff) || + (setup->md.bufsize == 0xffffffff)) { + parm.err = USB_ERR_BAD_BUFSIZE; + DPRINTF("invalid bufsize\n"); + } + if ((setup->mh.callback == NULL) && + (setup->md.callback == NULL)) { + parm.err = USB_ERR_NO_CALLBACK; + DPRINTF("no callback\n"); + } + ppxfer[n] = NULL; + } + + if (parm.err) { + goto done; + } + bzero(&parm, sizeof(parm)); + + parm.udev = udev; + parm.speed = usb2_get_speed(udev); + parm.hc_max_packet_count = 1; + + if (parm.speed >= USB_SPEED_MAX) { + parm.err = USB_ERR_INVAL; + goto done; + } + /* setup all transfers */ + + while (1) { + + if (buf) { + /* + * Initialize the "usb2_xfer_root" structure, + * which is common for all our USB transfers. + */ + info = USB_ADD_BYTES(buf, 0); + + info->memory_base = buf; + info->memory_size = parm.size[0]; + + info->dma_page_cache_start = USB_ADD_BYTES(buf, parm.size[4]); + info->dma_page_cache_end = USB_ADD_BYTES(buf, parm.size[5]); + info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm.size[5]); + info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm.size[2]); + + usb2_cv_init(&info->cv_drain, "WDRAIN"); + + info->usb2_mtx = &udev->bus->mtx; + info->priv_mtx = priv_mtx; + + usb2_dma_tag_setup(&info->dma_parent_tag, + parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, + priv_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max); + + info->bus = udev->bus; + + TAILQ_INIT(&info->done_q.head); + info->done_q.command = &usb2_callback_wrapper; + + TAILQ_INIT(&info->dma_q.head); + info->dma_q.command = &usb2_bdma_work_loop; + + info->done_m[0].hdr.pm_callback = &usb2_callback_proc; + info->done_m[0].usb2_root = info; + info->done_m[1].hdr.pm_callback = &usb2_callback_proc; + info->done_m[1].usb2_root = info; + + /* create a callback thread */ + + if (usb2_proc_setup(&info->done_p, + &udev->bus->mtx, USB_PRI_HIGH)) { + parm.err = USB_ERR_NO_INTR_THREAD; + goto done; + } + } + /* reset sizes */ + + parm.size[0] = 0; + parm.buf = buf; + parm.size[0] += sizeof(info[0]); + + for (setup = setup_start, n = 0; + setup != setup_end; setup++, n++) { + + /* select mode specific structure */ + if (udev->flags.usb2_mode == USB_MODE_HOST) { + parm.curr_setup_sub = &setup->mh; + } else { + parm.curr_setup_sub = &setup->md; + } + /* skip USB transfers without callbacks: */ + if (parm.curr_setup_sub->callback == NULL) { + continue; + } + /* see if there is a matching endpoint */ + pipe = usb2_get_pipe(udev, + ifaces[setup->if_index], setup); + + if (!pipe) { + if (parm.curr_setup_sub->flags.no_pipe_ok) { + continue; + } + parm.err = USB_ERR_NO_PIPE; + goto done; + } + /* store current setup pointer */ + parm.curr_setup = setup; + + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + if (buf) { + + /* + * Common initialization of the + * "usb2_xfer" structure. + */ + xfer = USB_ADD_BYTES(buf, parm.size[0]); + + ppxfer[n] = xfer; + xfer->udev = udev; + xfer->address = udev->address; + xfer->priv_sc = priv_sc; + xfer->priv_mtx = priv_mtx; + xfer->usb2_mtx = &udev->bus->mtx; + xfer->usb2_root = info; + info->setup_refcount++; + + usb2_callout_init_mtx(&xfer->timeout_handle, xfer->usb2_mtx, + CALLOUT_RETURNUNLOCKED); + } else { + /* + * Setup a dummy xfer, hence we are + * writing to the "usb2_xfer" + * structure pointed to by "xfer" + * before we have allocated any + * memory: + */ + xfer = &dummy; + bzero(&dummy, sizeof(dummy)); + refcount++; + } + + parm.size[0] += sizeof(xfer[0]); + + xfer->pipe = pipe; + + if (buf) { + /* + * Increment the pipe refcount. This + * basically prevents setting a new + * configuration and alternate setting + * when USB transfers are in use on + * the given interface. Search the USB + * code for "pipe->refcount" if you + * want more information. + */ + xfer->pipe->refcount++; + } + parm.methods = xfer->pipe->methods; + parm.curr_xfer = xfer; + + /* + * Call the Host or Device controller transfer setup + * routine: + */ + (udev->bus->methods->xfer_setup) (&parm); + + if (parm.err) { + goto done; + } + } + + if (buf || parm.err) { + goto done; + } + if (refcount == 0) { + /* no transfers - nothing to do ! */ + goto done; + } + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + /* store offset temporarily */ + parm.size[1] = parm.size[0]; + + /* + * The number of DMA tags required depends on + * the number of endpoints. The current estimate + * for maximum number of DMA tags per endpoint + * is two. + */ + parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX); + + /* + * DMA tags for QH, TD, Data and more. + */ + parm.dma_tag_max += 8; + + parm.dma_tag_p += parm.dma_tag_max; + + parm.size[0] += ((uint8_t *)parm.dma_tag_p) - + ((uint8_t *)0); + + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + /* store offset temporarily */ + parm.size[3] = parm.size[0]; + + parm.size[0] += ((uint8_t *)parm.dma_page_ptr) - + ((uint8_t *)0); + + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + /* store offset temporarily */ + parm.size[4] = parm.size[0]; + + parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) - + ((uint8_t *)0); + + /* store end offset temporarily */ + parm.size[5] = parm.size[0]; + + parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) - + ((uint8_t *)0); + + /* store end offset temporarily */ + + parm.size[2] = parm.size[0]; + + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + parm.size[6] = parm.size[0]; + + parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) - + ((uint8_t *)0); + + /* align data properly */ + parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + + /* allocate zeroed memory */ + buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO); + + if (buf == NULL) { + parm.err = USB_ERR_NOMEM; + DPRINTFN(0, "cannot allocate memory block for " + "configuration (%d bytes)\n", + parm.size[0]); + goto done; + } + parm.dma_tag_p = USB_ADD_BYTES(buf, parm.size[1]); + parm.dma_page_ptr = USB_ADD_BYTES(buf, parm.size[3]); + parm.dma_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[4]); + parm.xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[5]); + parm.xfer_length_ptr = USB_ADD_BYTES(buf, parm.size[6]); + } + +done: + if (buf) { + if (info->setup_refcount == 0) { + /* + * "usb2_transfer_unsetup_sub" will unlock + * "usb2_mtx" before returning ! + */ + mtx_lock(info->usb2_mtx); + + /* something went wrong */ + usb2_transfer_unsetup_sub(info, 0); + } + } + if (parm.err) { + usb2_transfer_unsetup(ppxfer, n_setup); + } + return (parm.err); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_unsetup_sub - factored out code + *------------------------------------------------------------------------*/ +static void +usb2_transfer_unsetup_sub(struct usb2_xfer_root *info, uint8_t needs_delay) +{ + struct usb2_page_cache *pc; + uint32_t temp; + + mtx_assert(info->usb2_mtx, MA_OWNED); + + /* wait for any outstanding DMA operations */ + + if (needs_delay) { + temp = usb2_get_dma_delay(info->bus); + usb2_pause_mtx(info->usb2_mtx, temp); + } + mtx_unlock(info->usb2_mtx); + + /* wait for interrupt thread to exit */ + usb2_proc_unsetup(&info->done_p); + + /* free DMA'able memory, if any */ + pc = info->dma_page_cache_start; + while (pc != info->dma_page_cache_end) { + usb2_pc_free_mem(pc); + pc++; + } + + /* free DMA maps in all "xfer->frbuffers" */ + pc = info->xfer_page_cache_start; + while (pc != info->xfer_page_cache_end) { + usb2_pc_dmamap_destroy(pc); + pc++; + } + + /* free all DMA tags */ + usb2_dma_tag_unsetup(&info->dma_parent_tag); + + usb2_cv_destroy(&info->cv_drain); + + /* + * free the "memory_base" last, hence the "info" structure is + * contained within the "memory_base"! + */ + free(info->memory_base, M_USB); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_unsetup - unsetup/free an array of USB transfers + * + * NOTE: All USB transfers in progress will get called back passing + * the error code "USB_ERR_CANCELLED" before this function + * returns. + *------------------------------------------------------------------------*/ +void +usb2_transfer_unsetup(struct usb2_xfer **pxfer, uint16_t n_setup) +{ + struct usb2_xfer *xfer; + struct usb2_xfer_root *info; + uint8_t needs_delay = 0; + + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "usb2_transfer_unsetup can sleep!"); + + while (n_setup--) { + xfer = pxfer[n_setup]; + + if (xfer) { + if (xfer->pipe) { + mtx_lock(xfer->priv_mtx); + mtx_lock(xfer->usb2_mtx); + + /* + * HINT: when you start/stop a transfer, it + * might be a good idea to directly use the + * "pxfer[]" structure: + * + * usb2_transfer_start(sc->pxfer[0]); + * usb2_transfer_stop(sc->pxfer[0]); + * + * That way, if your code has many parts that + * will not stop running under the same + * lock, in other words "priv_mtx", the + * usb2_transfer_start and + * usb2_transfer_stop functions will simply + * return when they detect a NULL pointer + * argument. + * + * To avoid any races we clear the "pxfer[]" + * pointer while holding the private mutex + * of the driver: + */ + pxfer[n_setup] = NULL; + + mtx_unlock(xfer->usb2_mtx); + mtx_unlock(xfer->priv_mtx); + + usb2_transfer_drain(xfer); + + if (xfer->flags_int.bdma_enable) { + needs_delay = 1; + } + /* + * NOTE: default pipe does not have an + * interface, even if pipe->iface_index == 0 + */ + xfer->pipe->refcount--; + + } else { + /* clear the transfer pointer */ + pxfer[n_setup] = NULL; + } + + usb2_callout_drain(&xfer->timeout_handle); + + if (xfer->usb2_root) { + info = xfer->usb2_root; + + mtx_lock(info->usb2_mtx); + + USB_ASSERT(info->setup_refcount != 0, + ("Invalid setup " + "reference count!\n")); + + info->setup_refcount--; + + if (info->setup_refcount == 0) { + usb2_transfer_unsetup_sub(info, + needs_delay); + } else { + mtx_unlock(info->usb2_mtx); + } + } + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_control_transfer_init - factored out code + * + * In USB Device Mode we have to wait for the SETUP packet which + * containst the "struct usb2_device_request" structure, before we can + * transfer any data. In USB Host Mode we already have the SETUP + * packet at the moment the USB transfer is started. This leads us to + * having to setup the USB transfer at two different places in + * time. This function just contains factored out control transfer + * initialisation code, so that we don't duplicate the code. + *------------------------------------------------------------------------*/ +static void +usb2_control_transfer_init(struct usb2_xfer *xfer) +{ + struct usb2_device_request req; + + /* copy out the USB request header */ + + usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); + + /* setup remainder */ + + xfer->flags_int.control_rem = UGETW(req.wLength); + + /* copy direction to endpoint variable */ + + xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT); + xfer->endpoint |= + (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; + + return; +} + +/*------------------------------------------------------------------------* + * usb2_start_hardware_sub + * + * This function handles initialisation of control transfers. Control + * transfers are special in that regard that they can both transmit + * and receive data. + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +usb2_start_hardware_sub(struct usb2_xfer *xfer) +{ + uint32_t len; + + /* Check for control endpoint stall */ + if (xfer->flags.stall_pipe) { + /* no longer active */ + xfer->flags_int.control_act = 0; + } + /* + * Check if there is a control + * transfer in progress: + */ + if (xfer->flags_int.control_act) { + + if (xfer->flags_int.control_hdr) { + + /* clear send header flag */ + + xfer->flags_int.control_hdr = 0; + + /* setup control transfer */ + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + usb2_control_transfer_init(xfer); + } + } + /* get data length */ + + len = xfer->sumlen; + + } else { + + /* the size of the SETUP structure is hardcoded ! */ + + if (xfer->frlengths[0] != sizeof(struct usb2_device_request)) { + DPRINTFN(0, "Wrong framelength %u != %zu\n", + xfer->frlengths[0], sizeof(struct + usb2_device_request)); + goto error; + } + /* check USB mode */ + if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { + + /* check number of frames */ + if (xfer->nframes != 1) { + /* + * We need to receive the setup + * message first so that we know the + * data direction! + */ + DPRINTF("Misconfigured transfer\n"); + goto error; + } + /* + * Set a dummy "control_rem" value. This + * variable will be overwritten later by a + * call to "usb2_control_transfer_init()" ! + */ + xfer->flags_int.control_rem = 0xFFFF; + } else { + + /* setup "endpoint" and "control_rem" */ + + usb2_control_transfer_init(xfer); + } + + /* set transfer-header flag */ + + xfer->flags_int.control_hdr = 1; + + /* get data length */ + + len = (xfer->sumlen - sizeof(struct usb2_device_request)); + } + + /* check if there is a length mismatch */ + + if (len > xfer->flags_int.control_rem) { + DPRINTFN(0, "Length greater than remaining length!\n"); + goto error; + } + /* check if we are doing a short transfer */ + + if (xfer->flags.force_short_xfer) { + xfer->flags_int.control_rem = 0; + } else { + if ((len != xfer->max_data_length) && + (len != xfer->flags_int.control_rem) && + (xfer->nframes != 1)) { + DPRINTFN(0, "Short control transfer without " + "force_short_xfer set!\n"); + goto error; + } + xfer->flags_int.control_rem -= len; + } + + /* the status part is executed when "control_act" is 0 */ + + if ((xfer->flags_int.control_rem > 0) || + (xfer->flags.manual_status)) { + /* don't execute the STATUS stage yet */ + xfer->flags_int.control_act = 1; + + /* sanity check */ + if ((!xfer->flags_int.control_hdr) && + (xfer->nframes == 1)) { + /* + * This is not a valid operation! + */ + DPRINTFN(0, "Invalid parameter " + "combination\n"); + goto error; + } + } else { + /* time to execute the STATUS stage */ + xfer->flags_int.control_act = 0; + } + return (0); /* success */ + +error: + return (1); /* failure */ +} + +/*------------------------------------------------------------------------* + * usb2_start_hardware - start USB hardware for the given transfer + * + * This function should only be called from the USB callback. + *------------------------------------------------------------------------*/ +void +usb2_start_hardware(struct usb2_xfer *xfer) +{ + uint32_t x; + + DPRINTF("xfer=%p, pipe=%p, nframes=%d, dir=%s\n", + xfer, xfer->pipe, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? + "read" : "write"); + +#if USB_DEBUG + if (USB_DEBUG_VAR > 0) { + mtx_lock(xfer->usb2_mtx); + + usb2_dump_pipe(xfer->pipe); + + mtx_unlock(xfer->usb2_mtx); + } +#endif + + mtx_assert(xfer->priv_mtx, MA_OWNED); + mtx_assert(xfer->usb2_mtx, MA_NOTOWNED); + + /* Only open the USB transfer once! */ + if (!xfer->flags_int.open) { + xfer->flags_int.open = 1; + + DPRINTF("open\n"); + + mtx_lock(xfer->usb2_mtx); + (xfer->pipe->methods->open) (xfer); + mtx_unlock(xfer->usb2_mtx); + } + /* set "transferring" flag */ + xfer->flags_int.transferring = 1; + + /* + * Check if the transfer is waiting on a queue, most + * frequently the "done_q": + */ + if (xfer->wait_queue) { + mtx_lock(xfer->usb2_mtx); + usb2_transfer_dequeue(xfer); + mtx_unlock(xfer->usb2_mtx); + } + /* clear "did_dma_delay" flag */ + xfer->flags_int.did_dma_delay = 0; + + /* clear "did_close" flag */ + xfer->flags_int.did_close = 0; + + /* clear "bdma_setup" flag */ + xfer->flags_int.bdma_setup = 0; + + /* by default we cannot cancel any USB transfer immediately */ + xfer->flags_int.can_cancel_immed = 0; + + /* clear lengths and frame counts by default */ + xfer->sumlen = 0; + xfer->actlen = 0; + xfer->aframes = 0; + + /* clear any previous errors */ + xfer->error = 0; + + /* sanity check */ + + if (xfer->nframes == 0) { + if (xfer->flags.stall_pipe) { + /* + * Special case - want to stall without transferring + * any data: + */ + DPRINTF("xfer=%p nframes=0: stall " + "or clear stall!\n", xfer); + mtx_lock(xfer->usb2_mtx); + xfer->flags_int.can_cancel_immed = 1; + /* start the transfer */ + usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); + mtx_unlock(xfer->usb2_mtx); + return; + } + mtx_lock(xfer->usb2_mtx); + usb2_transfer_done(xfer, USB_ERR_INVAL); + mtx_unlock(xfer->usb2_mtx); + return; + } + /* compute total transfer length */ + + for (x = 0; x != xfer->nframes; x++) { + xfer->sumlen += xfer->frlengths[x]; + if (xfer->sumlen < xfer->frlengths[x]) { + /* length wrapped around */ + mtx_lock(xfer->usb2_mtx); + usb2_transfer_done(xfer, USB_ERR_INVAL); + mtx_unlock(xfer->usb2_mtx); + return; + } + } + + /* clear some internal flags */ + + xfer->flags_int.short_xfer_ok = 0; + xfer->flags_int.short_frames_ok = 0; + + /* check if this is a control transfer */ + + if (xfer->flags_int.control_xfr) { + + if (usb2_start_hardware_sub(xfer)) { + mtx_lock(xfer->usb2_mtx); + usb2_transfer_done(xfer, USB_ERR_STALLED); + mtx_unlock(xfer->usb2_mtx); + return; + } + } + /* + * Setup filtered version of some transfer flags, + * in case of data read direction + */ + if (USB_GET_DATA_ISREAD(xfer)) { + + if (xfer->flags_int.control_xfr) { + + /* + * Control transfers do not support reception + * of multiple short USB frames ! + */ + + if (xfer->flags.short_xfer_ok) { + xfer->flags_int.short_xfer_ok = 1; + } + } else { + + if (xfer->flags.short_frames_ok) { + xfer->flags_int.short_xfer_ok = 1; + xfer->flags_int.short_frames_ok = 1; + } else if (xfer->flags.short_xfer_ok) { + xfer->flags_int.short_xfer_ok = 1; + } + } + } + /* + * Check if BUS-DMA support is enabled and try to load virtual + * buffers into DMA, if any: + */ + if (xfer->flags_int.bdma_enable) { + /* insert the USB transfer last in the BUS-DMA queue */ + usb2_command_wrapper(&xfer->usb2_root->dma_q, xfer); + return; + } + /* + * Enter the USB transfer into the Host Controller or + * Device Controller schedule: + */ + usb2_pipe_enter(xfer); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pipe_enter - factored out code + *------------------------------------------------------------------------*/ +void +usb2_pipe_enter(struct usb2_xfer *xfer) +{ + struct usb2_pipe *pipe; + + mtx_assert(xfer->priv_mtx, MA_OWNED); + + mtx_lock(xfer->usb2_mtx); + + pipe = xfer->pipe; + + DPRINTF("enter\n"); + + /* enter the transfer */ + (pipe->methods->enter) (xfer); + + /* check cancelability */ + if (pipe->methods->enter_is_cancelable) { + xfer->flags_int.can_cancel_immed = 1; + /* check for transfer error */ + if (xfer->error) { + /* some error has happened */ + usb2_transfer_done(xfer, 0); + mtx_unlock(xfer->usb2_mtx); + return; + } + } else { + xfer->flags_int.can_cancel_immed = 0; + } + + /* start the transfer */ + usb2_command_wrapper(&pipe->pipe_q, xfer); + mtx_unlock(xfer->usb2_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_start - start an USB transfer + * + * NOTE: Calling this function more than one time will only + * result in a single transfer start, until the USB transfer + * completes. + *------------------------------------------------------------------------*/ +void +usb2_transfer_start(struct usb2_xfer *xfer) +{ + if (xfer == NULL) { + /* transfer is gone */ + return; + } + mtx_assert(xfer->priv_mtx, MA_OWNED); + + /* mark the USB transfer started */ + + if (!xfer->flags_int.started) { + xfer->flags_int.started = 1; + } + /* check if the USB transfer callback is already transferring */ + + if (xfer->flags_int.transferring) { + return; + } + mtx_lock(xfer->usb2_mtx); + /* call the USB transfer callback */ + usb2_callback_ss_done_defer(xfer); + mtx_unlock(xfer->usb2_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_stop - stop an USB transfer + * + * NOTE: Calling this function more than one time will only + * result in a single transfer stop. + * NOTE: When this function returns it is not safe to free nor + * reuse any DMA buffers. See "usb2_transfer_drain()". + *------------------------------------------------------------------------*/ +void +usb2_transfer_stop(struct usb2_xfer *xfer) +{ + struct usb2_pipe *pipe; + + if (xfer == NULL) { + /* transfer is gone */ + return; + } + mtx_assert(xfer->priv_mtx, MA_OWNED); + + /* check if the USB transfer was ever opened */ + + if (!xfer->flags_int.open) { + /* nothing to do except clearing the "started" flag */ + xfer->flags_int.started = 0; + return; + } + /* try to stop the current USB transfer */ + + mtx_lock(xfer->usb2_mtx); + xfer->error = USB_ERR_CANCELLED;/* override any previous error */ + /* + * Clear "open" and "started" when both private and USB lock + * is locked so that we don't get a race updating "flags_int" + */ + xfer->flags_int.open = 0; + xfer->flags_int.started = 0; + + /* + * Check if we can cancel the USB transfer immediately. + */ + if (xfer->flags_int.transferring) { + if (xfer->flags_int.can_cancel_immed && + (!xfer->flags_int.did_close)) { + DPRINTF("close\n"); + /* + * The following will lead to an USB_ERR_CANCELLED + * error code being passed to the USB callback. + */ + (xfer->pipe->methods->close) (xfer); + /* only close once */ + xfer->flags_int.did_close = 1; + } else { + /* need to wait for the next done callback */ + } + } else { + DPRINTF("close\n"); + + /* close here and now */ + (xfer->pipe->methods->close) (xfer); + + /* + * Any additional DMA delay is done by + * "usb2_transfer_unsetup()". + */ + + /* + * Special case. Check if we need to restart a blocked + * pipe. + */ + pipe = xfer->pipe; + + /* + * If the current USB transfer is completing we need + * to start the next one: + */ + if (pipe->pipe_q.curr == xfer) { + usb2_command_wrapper(&pipe->pipe_q, NULL); + } + } + + mtx_unlock(xfer->usb2_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_pending + * + * This function will check if an USB transfer is pending which is a + * little bit complicated! + * Return values: + * 0: Not pending + * 1: Pending: The USB transfer will receive a callback in the future. + *------------------------------------------------------------------------*/ +uint8_t +usb2_transfer_pending(struct usb2_xfer *xfer) +{ + struct usb2_xfer_root *info; + struct usb2_xfer_queue *pq; + + mtx_assert(xfer->priv_mtx, MA_OWNED); + + if (xfer->flags_int.transferring) { + /* trivial case */ + return (1); + } + mtx_lock(xfer->usb2_mtx); + if (xfer->wait_queue) { + /* we are waiting on a queue somewhere */ + mtx_unlock(xfer->usb2_mtx); + return (1); + } + info = xfer->usb2_root; + pq = &info->done_q; + + if (pq->curr == xfer) { + /* we are currently scheduled for callback */ + mtx_unlock(xfer->usb2_mtx); + return (1); + } + /* we are not pending */ + mtx_unlock(xfer->usb2_mtx); + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_drain + * + * This function will stop the USB transfer and wait for any + * additional BUS-DMA and HW-DMA operations to complete. Buffers that + * are loaded into DMA can safely be freed or reused after that this + * function has returned. + *------------------------------------------------------------------------*/ +void +usb2_transfer_drain(struct usb2_xfer *xfer) +{ + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "usb2_transfer_drain can sleep!"); + + if (xfer == NULL) { + /* transfer is gone */ + return; + } + if (xfer->priv_mtx != &Giant) { + mtx_assert(xfer->priv_mtx, MA_NOTOWNED); + } + mtx_lock(xfer->priv_mtx); + + usb2_transfer_stop(xfer); + + while (usb2_transfer_pending(xfer)) { + xfer->flags_int.draining = 1; + /* + * Wait until the current outstanding USB + * transfer is complete ! + */ + usb2_cv_wait(&xfer->usb2_root->cv_drain, xfer->priv_mtx); + } + mtx_unlock(xfer->priv_mtx); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_frame_data + * + * This function sets the pointer of the buffer that should + * loaded directly into DMA for the given USB frame. Passing "ptr" + * equal to NULL while the corresponding "frlength" is greater + * than zero gives undefined results! + *------------------------------------------------------------------------*/ +void +usb2_set_frame_data(struct usb2_xfer *xfer, void *ptr, uint32_t frindex) +{ + /* set virtual address to load and length */ + xfer->frbuffers[frindex].buffer = ptr; + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_frame_offset + * + * This function sets the frame data buffer offset relative to the beginning + * of the USB DMA buffer allocated for this USB transfer. + *------------------------------------------------------------------------*/ +void +usb2_set_frame_offset(struct usb2_xfer *xfer, uint32_t offset, + uint32_t frindex) +{ + USB_ASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " + "when the USB buffer is external!\n")); + + /* set virtual address to load */ + xfer->frbuffers[frindex].buffer = + USB_ADD_BYTES(xfer->local_buffer, offset); + return; +} + +/*------------------------------------------------------------------------* + * usb2_callback_proc - factored out code + * + * This function performs USB callbacks. + *------------------------------------------------------------------------*/ +static void +usb2_callback_proc(struct usb2_proc_msg *_pm) +{ + struct usb2_done_msg *pm = (void *)_pm; + struct usb2_xfer_root *info = pm->usb2_root; + + /* Change locking order */ + mtx_unlock(info->usb2_mtx); + + /* + * We exploit the fact that the mutex is the same for all + * callbacks that will be called from this thread: + */ + mtx_lock(info->priv_mtx); + mtx_lock(info->usb2_mtx); + + /* Continue where we lost track */ + usb2_command_wrapper(&info->done_q, + info->done_q.curr); + + mtx_unlock(info->priv_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_callback_ss_done_defer + * + * This function will defer the start, stop and done callback to the + * correct thread. + *------------------------------------------------------------------------*/ +static void +usb2_callback_ss_done_defer(struct usb2_xfer *xfer) +{ + struct usb2_xfer_root *info = xfer->usb2_root; + struct usb2_xfer_queue *pq = &info->done_q; + + if (!mtx_owned(xfer->usb2_mtx)) { + panic("%s: called unlocked!\n", __FUNCTION__); + } + if (pq->curr != xfer) { + usb2_transfer_enqueue(pq, xfer); + } + if (!pq->recurse_1) { + + /* + * We have to postpone the callback due to the fact we + * will have a Lock Order Reversal, LOR, if we try to + * proceed ! + */ + if (usb2_proc_msignal(&info->done_p, + &info->done_m[0], &info->done_m[1])) { + /* ignore */ + } + } else { + /* clear second recurse flag */ + pq->recurse_2 = 0; + } + return; + +} + +/*------------------------------------------------------------------------* + * usb2_callback_wrapper + * + * This is a wrapper for USB callbacks. This wrapper does some + * auto-magic things like figuring out if we can call the callback + * directly from the current context or if we need to wakeup the + * interrupt process. + *------------------------------------------------------------------------*/ +static void +usb2_callback_wrapper(struct usb2_xfer_queue *pq) +{ + struct usb2_xfer *xfer = pq->curr; + struct usb2_xfer_root *info = xfer->usb2_root; + + if (!mtx_owned(xfer->usb2_mtx)) { + panic("%s: called unlocked!\n", __FUNCTION__); + } + if (!mtx_owned(xfer->priv_mtx)) { + /* + * Cases that end up here: + * + * 5) HW interrupt done callback or other source. + */ + DPRINTFN(3, "case 5\n"); + + /* + * We have to postpone the callback due to the fact we + * will have a Lock Order Reversal, LOR, if we try to + * proceed ! + */ + if (usb2_proc_msignal(&info->done_p, + &info->done_m[0], &info->done_m[1])) { + /* ignore */ + } + return; + } + /* + * Cases that end up here: + * + * 1) We are starting a transfer + * 2) We are prematurely calling back a transfer + * 3) We are stopping a transfer + * 4) We are doing an ordinary callback + */ + DPRINTFN(3, "case 1-4\n"); + /* get next USB transfer in the queue */ + info->done_q.curr = NULL; + + mtx_unlock(xfer->usb2_mtx); + mtx_assert(xfer->usb2_mtx, MA_NOTOWNED); + + /* set correct USB state for callback */ + if (!xfer->flags_int.transferring) { + xfer->usb2_state = USB_ST_SETUP; + if (!xfer->flags_int.started) { + /* we got stopped before we even got started */ + mtx_lock(xfer->usb2_mtx); + goto done; + } + } else { + + if (usb2_callback_wrapper_sub(xfer)) { + /* the callback has been deferred */ + mtx_lock(xfer->usb2_mtx); + goto done; + } + xfer->flags_int.transferring = 0; + + if (xfer->error) { + xfer->usb2_state = USB_ST_ERROR; + } else { + /* set transferred state */ + xfer->usb2_state = USB_ST_TRANSFERRED; + + /* sync DMA memory, if any */ + if (xfer->flags_int.bdma_enable && + (!xfer->flags_int.bdma_no_post_sync)) { + usb2_bdma_post_sync(xfer); + } + } + } + + /* call processing routine */ + (xfer->callback) (xfer); + + /* pickup the USB mutex again */ + mtx_lock(xfer->usb2_mtx); + + /* + * Check if we got started after that we got cancelled, but + * before we managed to do the callback. Check if we are + * draining. + */ + if ((!xfer->flags_int.open) && + (xfer->flags_int.started) && + (xfer->usb2_state == USB_ST_ERROR)) { + /* try to loop, but not recursivly */ + usb2_command_wrapper(&info->done_q, xfer); + return; + } else if (xfer->flags_int.draining && + (!xfer->flags_int.transferring)) { + /* "usb2_transfer_drain()" is waiting for end of transfer */ + xfer->flags_int.draining = 0; + usb2_cv_broadcast(&xfer->usb2_root->cv_drain); + } +done: + /* do the next callback, if any */ + usb2_command_wrapper(&info->done_q, + info->done_q.curr); + return; +} + +/*------------------------------------------------------------------------* + * usb2_dma_delay_done_cb + * + * This function is called when the DMA delay has been exectuded, and + * will make sure that the callback is called to complete the USB + * transfer. This code path is ususally only used when there is an USB + * error like USB_ERR_CANCELLED. + *------------------------------------------------------------------------*/ +static void +usb2_dma_delay_done_cb(void *arg) +{ + struct usb2_xfer *xfer = arg; + + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + DPRINTFN(3, "Completed %p\n", xfer); + + /* queue callback for execution, again */ + usb2_transfer_done(xfer, 0); + + mtx_unlock(xfer->usb2_mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_dequeue + * + * - This function is used to remove an USB transfer from a USB + * transfer queue. + * + * - This function can be called multiple times in a row. + *------------------------------------------------------------------------*/ +void +usb2_transfer_dequeue(struct usb2_xfer *xfer) +{ + struct usb2_xfer_queue *pq; + + pq = xfer->wait_queue; + if (pq) { + TAILQ_REMOVE(&pq->head, xfer, wait_entry); + xfer->wait_queue = NULL; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_enqueue + * + * - This function is used to insert an USB transfer into a USB * + * transfer queue. + * + * - This function can be called multiple times in a row. + *------------------------------------------------------------------------*/ +void +usb2_transfer_enqueue(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer) +{ + /* + * Insert the USB transfer into the queue, if it is not + * already on a USB transfer queue: + */ + if (xfer->wait_queue == NULL) { + xfer->wait_queue = pq; + TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_done + * + * - This function is used to remove an USB transfer from the busdma, + * pipe or interrupt queue. + * + * - This function is used to queue the USB transfer on the done + * queue. + * + * - This function is used to stop any USB transfer timeouts. + *------------------------------------------------------------------------*/ +void +usb2_transfer_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct usb2_xfer_queue *pq; + + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + DPRINTF("err=%s\n", usb2_errstr(error)); + + /* + * If we are not transferring then just return. + * This can happen during transfer cancel. + */ + if (!xfer->flags_int.transferring) { + DPRINTF("not transferring\n"); + return; + } + /* only set transfer error if not already set */ + if (!xfer->error) { + xfer->error = error; + } + /* stop any callouts */ + usb2_callout_stop(&xfer->timeout_handle); + + /* + * If we are waiting on a queue, just remove the USB transfer + * from the queue, if any. We should have the required locks + * locked to do the remove when this function is called. + */ + usb2_transfer_dequeue(xfer); + + if (mtx_owned(xfer->priv_mtx)) { + /* + * If the private USB lock is not locked, then we assume + * that the BUS-DMA load stage has been passed: + */ + pq = &xfer->usb2_root->dma_q; + + if (pq->curr == xfer) { + /* start the next BUS-DMA load, if any */ + usb2_command_wrapper(pq, NULL); + } + } + /* keep some statistics */ + if (xfer->error) { + xfer->udev->bus->stats_err.uds_requests + [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; + } else { + xfer->udev->bus->stats_ok.uds_requests + [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; + } + + /* call the USB transfer callback */ + usb2_callback_ss_done_defer(xfer); + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_start_cb + * + * This function is called to start the USB transfer when + * "xfer->interval" is greater than zero, and and the endpoint type is + * BULK or CONTROL. + *------------------------------------------------------------------------*/ +static void +usb2_transfer_start_cb(void *arg) +{ + struct usb2_xfer *xfer = arg; + struct usb2_pipe *pipe = xfer->pipe; + + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + DPRINTF("start\n"); + + /* start the transfer */ + (pipe->methods->start) (xfer); + + /* check cancelability */ + if (pipe->methods->start_is_cancelable) { + xfer->flags_int.can_cancel_immed = 1; + if (xfer->error) { + /* some error has happened */ + usb2_transfer_done(xfer, 0); + } + } else { + xfer->flags_int.can_cancel_immed = 0; + } + mtx_unlock(xfer->usb2_mtx); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_set_stall + * + * This function is used to set the stall flag outside the + * callback. This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_transfer_set_stall(struct usb2_xfer *xfer) +{ + if (xfer == NULL) { + /* tearing down */ + return; + } + mtx_assert(xfer->priv_mtx, MA_OWNED); + + /* avoid any races by locking the USB mutex */ + mtx_lock(xfer->usb2_mtx); + + xfer->flags.stall_pipe = 1; + + mtx_unlock(xfer->usb2_mtx); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_clear_stall + * + * This function is used to clear the stall flag outside the + * callback. This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb2_transfer_clear_stall(struct usb2_xfer *xfer) +{ + if (xfer == NULL) { + /* tearing down */ + return; + } + mtx_assert(xfer->priv_mtx, MA_OWNED); + + /* avoid any races by locking the USB mutex */ + mtx_lock(xfer->usb2_mtx); + + xfer->flags.stall_pipe = 0; + + mtx_unlock(xfer->usb2_mtx); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_pipe_start + * + * This function is used to add an USB transfer to the pipe transfer list. + *------------------------------------------------------------------------*/ +void +usb2_pipe_start(struct usb2_xfer_queue *pq) +{ + struct usb2_pipe *pipe; + struct usb2_xfer *xfer; + uint8_t type; + + xfer = pq->curr; + pipe = xfer->pipe; + + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + /* + * If the pipe is already stalled we do nothing ! + */ + if (pipe->is_stalled) { + return; + } + /* + * Check if we are supposed to stall the pipe: + */ + if (xfer->flags.stall_pipe) { + /* clear stall command */ + xfer->flags.stall_pipe = 0; + + /* + * Only stall BULK and INTERRUPT endpoints. + */ + type = (pipe->edesc->bmAttributes & UE_XFERTYPE); + if ((type == UE_BULK) || + (type == UE_INTERRUPT)) { + struct usb2_device *udev; + struct usb2_xfer_root *info; + + udev = xfer->udev; + pipe->is_stalled = 1; + + if (udev->flags.usb2_mode == USB_MODE_DEVICE) { + (udev->bus->methods->set_stall) ( + udev, NULL, pipe); + } else if (udev->default_xfer[1]) { + info = udev->default_xfer[1]->usb2_root; + if (usb2_proc_msignal(&info->done_p, + &udev->cs_msg[0], &udev->cs_msg[1])) { + /* ignore */ + } + } else { + /* should not happen */ + DPRINTFN(0, "No stall handler!\n"); + } + /* + * We get started again when the stall is cleared! + */ + return; + } + } + /* Set or clear stall complete - special case */ + if (xfer->nframes == 0) { + /* we are complete */ + xfer->aframes = 0; + usb2_transfer_done(xfer, 0); + return; + } + /* + * Handled cases: + * + * 1) Start the first transfer queued. + * + * 2) Re-start the current USB transfer. + */ + /* + * Check if there should be any + * pre transfer start delay: + */ + if (xfer->interval > 0) { + type = (pipe->edesc->bmAttributes & UE_XFERTYPE); + if ((type == UE_BULK) || + (type == UE_CONTROL)) { + usb2_transfer_timeout_ms(xfer, + &usb2_transfer_start_cb, + xfer->interval); + return; + } + } + DPRINTF("start\n"); + + /* start USB transfer */ + (pipe->methods->start) (xfer); + + /* check cancelability */ + if (pipe->methods->start_is_cancelable) { + xfer->flags_int.can_cancel_immed = 1; + if (xfer->error) { + /* some error has happened */ + usb2_transfer_done(xfer, 0); + } + } else { + xfer->flags_int.can_cancel_immed = 0; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_transfer_timeout_ms + * + * This function is used to setup a timeout on the given USB + * transfer. If the timeout has been deferred the callback given by + * "cb" will get called after "ms" milliseconds. + *------------------------------------------------------------------------*/ +void +usb2_transfer_timeout_ms(struct usb2_xfer *xfer, + void (*cb) (void *arg), uint32_t ms) +{ + mtx_assert(xfer->usb2_mtx, MA_OWNED); + + /* defer delay */ + usb2_callout_reset(&xfer->timeout_handle, + USB_MS_TO_TICKS(ms), cb, xfer); + return; +} + +/*------------------------------------------------------------------------* + * usb2_callback_wrapper_sub + * + * - This function will update variables in an USB transfer after + * that the USB transfer is complete. + * + * - This function is used to start the next USB transfer on the + * pipe transfer queue, if any. + * + * NOTE: In some special cases the USB transfer will not be removed from + * the pipe queue, but remain first. To enforce USB transfer removal call + * this function passing the error code "USB_ERR_CANCELLED". + * + * Return values: + * 0: Success. + * Else: The callback has been deferred. + *------------------------------------------------------------------------*/ +static uint8_t +usb2_callback_wrapper_sub(struct usb2_xfer *xfer) +{ + struct usb2_pipe *pipe; + uint32_t x; + + if ((!xfer->flags_int.open) && + (!xfer->flags_int.did_close)) { + DPRINTF("close\n"); + mtx_lock(xfer->usb2_mtx); + (xfer->pipe->methods->close) (xfer); + mtx_unlock(xfer->usb2_mtx); + /* only close once */ + xfer->flags_int.did_close = 1; + return (1); /* wait for new callback */ + } + /* + * If we have a non-hardware induced error we + * need to do the DMA delay! + */ + if (((xfer->error == USB_ERR_CANCELLED) || + (xfer->error == USB_ERR_TIMEOUT)) && + (!xfer->flags_int.did_dma_delay)) { + + uint32_t temp; + + /* only delay once */ + xfer->flags_int.did_dma_delay = 1; + + /* we can not cancel this delay */ + xfer->flags_int.can_cancel_immed = 0; + + temp = usb2_get_dma_delay(xfer->udev->bus); + + DPRINTFN(3, "DMA delay, %u ms, " + "on %p\n", temp, xfer); + + if (temp != 0) { + mtx_lock(xfer->usb2_mtx); + usb2_transfer_timeout_ms(xfer, + &usb2_dma_delay_done_cb, temp); + mtx_unlock(xfer->usb2_mtx); + return (1); /* wait for new callback */ + } + } + /* check actual number of frames */ + if (xfer->aframes > xfer->nframes) { + if (xfer->error == 0) { + panic("%s: actual number of frames, %d, is " + "greater than initial number of frames, %d!\n", + __FUNCTION__, xfer->aframes, xfer->nframes); + } else { + /* just set some valid value */ + xfer->aframes = xfer->nframes; + } + } + /* compute actual length */ + xfer->actlen = 0; + + for (x = 0; x != xfer->aframes; x++) { + xfer->actlen += xfer->frlengths[x]; + } + + /* + * Frames that were not transferred get zero actual length in + * case the USB device driver does not check the actual number + * of frames transferred, "xfer->aframes": + */ + for (; x < xfer->nframes; x++) { + xfer->frlengths[x] = 0; + } + + /* check actual length */ + if (xfer->actlen > xfer->sumlen) { + if (xfer->error == 0) { + panic("%s: actual length, %d, is greater than " + "initial length, %d!\n", + __FUNCTION__, xfer->actlen, xfer->sumlen); + } else { + /* just set some valid value */ + xfer->actlen = xfer->sumlen; + } + } + DPRINTFN(6, "xfer=%p pipe=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", + xfer, xfer->pipe, xfer->error, xfer->actlen, xfer->sumlen, + xfer->aframes, xfer->nframes); + + if (xfer->error) { + /* end of control transfer, if any */ + xfer->flags_int.control_act = 0; + + /* check if we should block the execution queue */ + if ((xfer->error != USB_ERR_CANCELLED) && + (xfer->flags.pipe_bof)) { + DPRINTFN(2, "xfer=%p: Block On Failure " + "on pipe=%p\n", xfer, xfer->pipe); + goto done; + } + } else { + /* check for short transfers */ + if (xfer->actlen < xfer->sumlen) { + + /* end of control transfer, if any */ + xfer->flags_int.control_act = 0; + + if (!xfer->flags_int.short_xfer_ok) { + xfer->error = USB_ERR_SHORT_XFER; + if (xfer->flags.pipe_bof) { + DPRINTFN(2, "xfer=%p: Block On Failure on " + "Short Transfer on pipe %p.\n", + xfer, xfer->pipe); + goto done; + } + } + } else { + /* + * Check if we are in the middle of a + * control transfer: + */ + if (xfer->flags_int.control_act) { + DPRINTFN(5, "xfer=%p: Control transfer " + "active on pipe=%p\n", xfer, xfer->pipe); + goto done; + } + } + } + + pipe = xfer->pipe; + + /* + * If the current USB transfer is completing we need to start the + * next one: + */ + mtx_lock(xfer->usb2_mtx); + if (pipe->pipe_q.curr == xfer) { + usb2_command_wrapper(&pipe->pipe_q, NULL); + + if (pipe->pipe_q.curr || TAILQ_FIRST(&pipe->pipe_q.head)) { + /* there is another USB transfer waiting */ + } else { + /* this is the last USB transfer */ + /* clear isochronous sync flag */ + xfer->pipe->is_synced = 0; + } + } + mtx_unlock(xfer->usb2_mtx); +done: + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_command_wrapper + * + * This function is used to execute commands non-recursivly on an USB + * transfer. + *------------------------------------------------------------------------*/ +void +usb2_command_wrapper(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer) +{ + if (xfer) { + /* + * If the transfer is not already processing, + * queue it! + */ + if (pq->curr != xfer) { + usb2_transfer_enqueue(pq, xfer); + if (pq->curr != NULL) { + /* something is already processing */ + DPRINTFN(6, "busy %p\n", pq->curr); + return; + } + } + } else { + /* Get next element in queue */ + pq->curr = NULL; + } + + if (!pq->recurse_1) { + + do { + + /* set both recurse flags */ + pq->recurse_1 = 1; + pq->recurse_2 = 1; + + if (pq->curr == NULL) { + xfer = TAILQ_FIRST(&pq->head); + if (xfer) { + TAILQ_REMOVE(&pq->head, xfer, + wait_entry); + xfer->wait_queue = NULL; + pq->curr = xfer; + } else { + break; + } + } + DPRINTFN(6, "cb %p (enter)\n", pq->curr); + (pq->command) (pq); + DPRINTFN(6, "cb %p (leave)\n", pq->curr); + + } while (!pq->recurse_2); + + /* clear first recurse flag */ + pq->recurse_1 = 0; + + } else { + /* clear second recurse flag */ + pq->recurse_2 = 0; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_default_transfer_setup + * + * This function is used to setup the default USB control endpoint + * transfer. + *------------------------------------------------------------------------*/ +void +usb2_default_transfer_setup(struct usb2_device *udev) +{ + struct usb2_xfer *xfer; + uint8_t no_resetup; + uint8_t iface_index; + +repeat: + + xfer = udev->default_xfer[0]; + if (xfer) { + mtx_lock(xfer->priv_mtx); + no_resetup = + ((xfer->address == udev->address) && + (udev->default_ep_desc.wMaxPacketSize[0] == + udev->ddesc.bMaxPacketSize)); + if (udev->flags.usb2_mode == USB_MODE_DEVICE) { + if (no_resetup) { + /* + * NOTE: checking "xfer->address" and + * starting the USB transfer must be + * atomic! + */ + usb2_transfer_start(xfer); + } + } + mtx_unlock(xfer->priv_mtx); + } else { + no_resetup = 0; + } + + if (no_resetup) { + /* + * All parameters are exactly the same like before. + * Just return. + */ + return; + } + /* + * Update wMaxPacketSize for the default control endpoint: + */ + udev->default_ep_desc.wMaxPacketSize[0] = + udev->ddesc.bMaxPacketSize; + + /* + * Unsetup any existing USB transfer: + */ + usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX); + + /* + * Try to setup a new USB transfer for the + * default control endpoint: + */ + iface_index = 0; + if (usb2_transfer_setup(udev, &iface_index, + udev->default_xfer, usb2_control_ep_cfg, USB_DEFAULT_XFER_MAX, NULL, + udev->default_mtx)) { + DPRINTFN(0, "could not setup default " + "USB transfer!\n"); + } else { + goto repeat; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_clear_data_toggle - factored out code + * + * NOTE: the intention of this function is not to reset the hardware + * data toggle. + *------------------------------------------------------------------------*/ +void +usb2_clear_data_toggle(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + DPRINTFN(5, "udev=%p pipe=%p\n", udev, pipe); + + mtx_lock(&udev->bus->mtx); + pipe->toggle_next = 0; + mtx_unlock(&udev->bus->mtx); + return; +} + +/*------------------------------------------------------------------------* + * usb2_clear_stall_callback - factored out clear stall callback + * + * Input parameters: + * xfer1: Clear Stall Control Transfer + * xfer2: Stalled USB Transfer + * + * This function is NULL safe. + * + * Return values: + * 0: In progress + * Else: Finished + * + * Clear stall config example: + * + * static const struct usb2_config my_clearstall = { + * .type = UE_CONTROL, + * .endpoint = 0, + * .direction = UE_DIR_ANY, + * .interval = 50, //50 milliseconds + * .bufsize = sizeof(struct usb2_device_request), + * .mh.timeout = 1000, //1.000 seconds + * .mh.flags = { }, + * .mh.callback = &my_clear_stall_callback, // ** + * }; + * + * ** "my_clear_stall_callback" calls "usb2_clear_stall_callback" + * passing the correct parameters. + *------------------------------------------------------------------------*/ +uint8_t +usb2_clear_stall_callback(struct usb2_xfer *xfer1, + struct usb2_xfer *xfer2) +{ + struct usb2_device_request req; + + if (xfer2 == NULL) { + /* looks like we are tearing down */ + DPRINTF("NULL input parameter\n"); + return (0); + } + mtx_assert(xfer1->priv_mtx, MA_OWNED); + mtx_assert(xfer2->priv_mtx, MA_OWNED); + + switch (USB_GET_STATE(xfer1)) { + case USB_ST_SETUP: + + /* + * pre-clear the data toggle to DATA0 ("umass.c" and + * "ata-usb.c" depends on this) + */ + + usb2_clear_data_toggle(xfer2->udev, xfer2->pipe); + + /* setup a clear-stall packet */ + + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + req.wIndex[0] = xfer2->pipe->edesc->bEndpointAddress; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + /* + * "usb2_transfer_setup_sub()" will ensure that + * we have sufficient room in the buffer for + * the request structure! + */ + + /* copy in the transfer */ + + usb2_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); + + /* set length */ + xfer1->frlengths[0] = sizeof(req); + xfer1->nframes = 1; + + usb2_start_hardware(xfer1); + return (0); + + case USB_ST_TRANSFERRED: + break; + + default: /* Error */ + if (xfer1->error == USB_ERR_CANCELLED) { + return (0); + } + break; + } + return (1); /* Clear Stall Finished */ +} + +#if (USB_NO_POLL == 0) + +/*------------------------------------------------------------------------* + * usb2_callout_poll + *------------------------------------------------------------------------*/ +static void +usb2_callout_poll(struct usb2_xfer *xfer) +{ + struct usb2_callout *co; + void (*cb) (void *); + void *arg; + struct mtx *mtx; + uint32_t delta; + + if (xfer == NULL) { + return; + } + co = &xfer->timeout_handle; + +#if __FreeBSD_version >= 800000 + mtx = (void *)(co->co.c_lock); +#else + mtx = co->co.c_mtx; +#endif + mtx_lock(mtx); + + if (usb2_callout_pending(co)) { + delta = ticks - co->co.c_time; + if (!(delta & 0x80000000)) { + + cb = co->co.c_func; + arg = co->co.c_arg; + + /* timed out */ + usb2_callout_stop(co); + + (cb) (arg); + + /* the callback should drop the mutex */ + } else { + mtx_unlock(mtx); + } + } else { + mtx_unlock(mtx); + } + return; +} + + +/*------------------------------------------------------------------------* + * usb2_do_poll + * + * This function is called from keyboard driver when in polling + * mode. + *------------------------------------------------------------------------*/ +void +usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) +{ + struct usb2_xfer *xfer; + struct usb2_xfer_root *usb2_root; + struct usb2_device *udev; + struct usb2_proc_msg *pm; + uint32_t to; + uint16_t n; + + /* compute system tick delay */ + to = ((uint32_t)(1000000)) / ((uint32_t)(hz)); + DELAY(to); + atomic_add_int((volatile int *)&ticks, 1); + + for (n = 0; n != max; n++) { + xfer = ppxfer[n]; + if (xfer) { + usb2_root = xfer->usb2_root; + udev = xfer->udev; + + /* + * Poll hardware - signal that we are polling by + * locking the private mutex: + */ + mtx_lock(xfer->priv_mtx); + (udev->bus->methods->do_poll) (udev->bus); + mtx_unlock(xfer->priv_mtx); + + /* poll clear stall start */ + mtx_lock(xfer->usb2_mtx); + pm = &udev->cs_msg[0].hdr; + (pm->pm_callback) (pm); + mtx_unlock(xfer->usb2_mtx); + + if (udev->default_xfer[1]) { + + /* poll timeout */ + usb2_callout_poll(udev->default_xfer[1]); + + /* poll clear stall done thread */ + mtx_lock(xfer->usb2_mtx); + pm = &udev->default_xfer[1]-> + usb2_root->done_m[0].hdr; + (pm->pm_callback) (pm); + mtx_unlock(xfer->usb2_mtx); + } + /* poll timeout */ + usb2_callout_poll(xfer); + + /* poll done thread */ + mtx_lock(xfer->usb2_mtx); + pm = &usb2_root->done_m[0].hdr; + (pm->pm_callback) (pm); + mtx_unlock(xfer->usb2_mtx); + } + } + return; +} + +#else + +void +usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) +{ + /* polling not supported */ + return; +} + +#endif diff --git a/sys/dev/usb2/core/usb2_transfer.h b/sys/dev/usb2/core/usb2_transfer.h new file mode 100644 index 000000000000..83920e5fc444 --- /dev/null +++ b/sys/dev/usb2/core/usb2_transfer.h @@ -0,0 +1,123 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_TRANSFER_H_ +#define _USB2_TRANSFER_H_ + +/* + * The following structure defines the messages that is used to signal + * the "done_p" USB process. + */ +struct usb2_done_msg { + struct usb2_proc_msg hdr; + struct usb2_xfer_root *usb2_root; +}; + +/* + * The following structure is used to keep information about memory + * that should be automatically freed at the moment all USB transfers + * have been freed. + */ +struct usb2_xfer_root { + struct usb2_xfer_queue dma_q; + struct usb2_xfer_queue done_q; + struct usb2_done_msg done_m[2]; + struct cv cv_drain; + + struct usb2_dma_parent_tag dma_parent_tag; + + struct usb2_process done_p; + void *memory_base; + struct mtx *priv_mtx; + struct mtx *usb2_mtx; + struct usb2_page_cache *dma_page_cache_start; + struct usb2_page_cache *dma_page_cache_end; + struct usb2_page_cache *xfer_page_cache_start; + struct usb2_page_cache *xfer_page_cache_end; + struct usb2_bus *bus; + + uint32_t memory_size; + uint32_t setup_refcount; + uint32_t page_size; + uint32_t dma_nframes; /* number of page caches to load */ + uint32_t dma_currframe; /* currect page cache number */ + uint32_t dma_frlength_0; /* length of page cache zero */ + uint8_t dma_error; /* set if virtual memory could not be + * loaded */ + uint8_t done_sleep; /* set if done thread is sleeping */ +}; + +/* + * The following structure is used when setting up an array of USB + * transfers. + */ +struct usb2_setup_params { + struct usb2_dma_tag *dma_tag_p; + struct usb2_page *dma_page_ptr; + struct usb2_page_cache *dma_page_cache_ptr; /* these will be + * auto-freed */ + struct usb2_page_cache *xfer_page_cache_ptr; /* these will not be + * auto-freed */ + struct usb2_device *udev; + struct usb2_xfer *curr_xfer; + const struct usb2_config *curr_setup; + const struct usb2_config_sub *curr_setup_sub; + const struct usb2_pipe_methods *methods; + void *buf; + uint32_t *xfer_length_ptr; + + uint32_t size[7]; + uint32_t bufsize; + uint32_t bufsize_max; + uint32_t hc_max_frame_size; + + uint16_t hc_max_packet_size; + uint8_t hc_max_packet_count; + uint8_t speed; + uint8_t dma_tag_max; + usb2_error_t err; +}; + +/* function prototypes */ + +uint8_t usb2_transfer_pending(struct usb2_xfer *xfer); +uint8_t usb2_transfer_setup_sub_malloc(struct usb2_setup_params *parm, struct usb2_page_cache **ppc, uint32_t size, uint32_t align, uint32_t count); +void usb2_command_wrapper(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer); +void usb2_pipe_enter(struct usb2_xfer *xfer); +void usb2_pipe_start(struct usb2_xfer_queue *pq); +void usb2_transfer_dequeue(struct usb2_xfer *xfer); +void usb2_transfer_done(struct usb2_xfer *xfer, usb2_error_t error); +void usb2_transfer_enqueue(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer); +void usb2_transfer_setup_sub(struct usb2_setup_params *parm); +void usb2_default_transfer_setup(struct usb2_device *udev); +void usb2_clear_data_toggle(struct usb2_device *udev, struct usb2_pipe *pipe); +void usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max); +usb2_callback_t usb2_do_request_callback; +usb2_callback_t usb2_handle_request_callback; +usb2_callback_t usb2_do_clear_stall_callback; +void usb2_transfer_timeout_ms(struct usb2_xfer *xfer, void (*cb) (void *arg), uint32_t ms); + +#endif /* _USB2_TRANSFER_H_ */ diff --git a/sys/dev/usb2/core/usb2_util.c b/sys/dev/usb2/core/usb2_util.c new file mode 100644 index 000000000000..d944e441abb7 --- /dev/null +++ b/sys/dev/usb2/core/usb2_util.c @@ -0,0 +1,354 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* function prototypes */ +#if (USB_USE_CONDVAR == 0) +static int usb2_msleep(void *chan, struct mtx *mtx, int priority, const char *wmesg, int timo); + +#endif + +/*------------------------------------------------------------------------* + * device_delete_all_children - delete all children of a device + *------------------------------------------------------------------------*/ +int +device_delete_all_children(device_t dev) +{ + device_t *devlist; + int devcount; + int error; + + error = device_get_children(dev, &devlist, &devcount); + if (error == 0) { + while (devcount-- > 0) { + error = device_delete_child(dev, devlist[devcount]); + if (error) { + break; + } + } + free(devlist, M_TEMP); + } + return (error); +} + +/*------------------------------------------------------------------------* + * device_set_usb2_desc + * + * This function can be called at probe or attach to set the USB + * device supplied textual description for the given device. + *------------------------------------------------------------------------*/ +void +device_set_usb2_desc(device_t dev) +{ + struct usb2_attach_arg *uaa; + struct usb2_device *udev; + struct usb2_interface *iface; + char *temp_p; + usb2_error_t err; + + if (dev == NULL) { + /* should not happen */ + return; + } + uaa = device_get_ivars(dev); + if (uaa == NULL) { + /* can happend if called at the wrong time */ + return; + } + udev = uaa->device; + iface = uaa->iface; + + if ((iface == NULL) || + (iface->idesc == NULL) || + (iface->idesc->iInterface == 0)) { + err = USB_ERR_INVAL; + } else { + err = 0; + } + + temp_p = (char *)udev->bus->scratch[0].data; + + if (!err) { + /* try to get the interface string ! */ + err = usb2_req_get_string_any + (udev, NULL, temp_p, + sizeof(udev->bus->scratch), iface->idesc->iInterface); + } + if (err) { + /* use default description */ + usb2_devinfo(udev, temp_p, + sizeof(udev->bus->scratch)); + } + device_set_desc_copy(dev, temp_p); + device_printf(dev, "<%s> on %s\n", temp_p, + device_get_nameunit(udev->bus->bdev)); + return; +} + +/*------------------------------------------------------------------------* + * usb2_pause_mtx - factored out code + * + * This function will delay the code by the passed number of + * milliseconds. The passed mutex "mtx" will be dropped while waiting, + * if "mtx" is not NULL. The number of milliseconds per second is 1024 + * for sake of optimisation. + *------------------------------------------------------------------------*/ +void +usb2_pause_mtx(struct mtx *mtx, uint32_t ms) +{ + if (cold) { + ms = (ms + 1) * 1024; + DELAY(ms); + + } else { + + ms = USB_MS_TO_TICKS(ms); + /* + * Add one to the number of ticks so that we don't return + * too early! + */ + ms++; + + if (mtx != NULL) + mtx_unlock(mtx); + + if (pause("USBWAIT", ms)) { + /* ignore */ + } + if (mtx != NULL) + mtx_lock(mtx); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_printBCD + * + * This function will print the version number "bcd" to the string + * pointed to by "p" having a maximum length of "p_len" bytes + * including the terminating zero. + *------------------------------------------------------------------------*/ +void +usb2_printBCD(char *p, uint16_t p_len, uint16_t bcd) +{ + if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) { + /* ignore any errors */ + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_trim_spaces + * + * This function removes spaces at the beginning and the end of the string + * pointed to by the "p" argument. + *------------------------------------------------------------------------*/ +void +usb2_trim_spaces(char *p) +{ + char *q; + char *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 */ + return; +} + +/*------------------------------------------------------------------------* + * usb2_get_devid + * + * This function returns the USB Vendor and Product ID like a 32-bit + * unsigned integer. + *------------------------------------------------------------------------*/ +uint32_t +usb2_get_devid(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + return ((uaa->info.idVendor << 16) | (uaa->info.idProduct)); +} + +/*------------------------------------------------------------------------* + * usb2_make_str_desc - convert an ASCII string into a UNICODE string + *------------------------------------------------------------------------*/ +uint8_t +usb2_make_str_desc(void *ptr, uint16_t max_len, const char *s) +{ + struct usb2_string_descriptor *p = ptr; + uint8_t totlen; + int j; + + if (max_len < 2) { + /* invalid length */ + return (0); + } + max_len = ((max_len / 2) - 1); + + j = strlen(s); + + if (j < 0) { + j = 0; + } + if (j > 126) { + j = 126; + } + if (max_len > j) { + max_len = j; + } + totlen = (max_len + 1) * 2; + + p->bLength = totlen; + p->bDescriptorType = UDESC_STRING; + + while (max_len--) { + USETW2(p->bString[max_len], 0, s[max_len]); + } + return (totlen); +} + +#if (USB_USE_CONDVAR == 0) + +/*------------------------------------------------------------------------* + * usb2_cv_init - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_init(struct cv *cv, const char *desc) +{ + cv_init(cv, desc); + return; +} + +/*------------------------------------------------------------------------* + * usb2_cv_destroy - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_destroy(struct cv *cv) +{ + cv_destroy(cv); + return; +} + +/*------------------------------------------------------------------------* + * usb2_cv_wait - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_wait(struct cv *cv, struct mtx *mtx) +{ + int err; + + err = usb2_msleep(cv, mtx, 0, cv_wmesg(cv), 0); + return; +} + +/*------------------------------------------------------------------------* + * usb2_cv_wait_sig - wrapper function + *------------------------------------------------------------------------*/ +int +usb2_cv_wait_sig(struct cv *cv, struct mtx *mtx) +{ + int err; + + err = usb2_msleep(cv, mtx, PCATCH, cv_wmesg(cv), 0); + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_cv_timedwait - wrapper function + *------------------------------------------------------------------------*/ +int +usb2_cv_timedwait(struct cv *cv, struct mtx *mtx, int timo) +{ + int err; + + if (timo == 0) + timo = 1; /* zero means no timeout */ + err = usb2_msleep(cv, mtx, 0, cv_wmesg(cv), timo); + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_cv_signal - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_signal(struct cv *cv) +{ + wakeup_one(cv); + return; +} + +/*------------------------------------------------------------------------* + * usb2_cv_broadcast - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_broadcast(struct cv *cv) +{ + wakeup(cv); + return; +} + +/*------------------------------------------------------------------------* + * usb2_msleep - wrapper function + *------------------------------------------------------------------------*/ +static int +usb2_msleep(void *chan, struct mtx *mtx, int priority, const char *wmesg, + int timo) +{ + int err; + + if (mtx == &Giant) { + err = tsleep(chan, priority, wmesg, timo); + } else { +#ifdef mtx_sleep + err = mtx_sleep(chan, mtx, priority, wmesg, timo); +#else + err = msleep(chan, mtx, priority, wmesg, timo); +#endif + } + return (err); +} + +#endif diff --git a/sys/dev/usb2/core/usb2_util.h b/sys/dev/usb2/core/usb2_util.h new file mode 100644 index 000000000000..8bbae8c5d32c --- /dev/null +++ b/sys/dev/usb2/core/usb2_util.h @@ -0,0 +1,57 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_UTIL_H_ +#define _USB2_UTIL_H_ + +int device_delete_all_children(device_t dev); +uint32_t usb2_get_devid(device_t dev); +uint8_t usb2_make_str_desc(void *ptr, uint16_t max_len, const char *s); +void device_set_usb2_desc(device_t dev); +void usb2_pause_mtx(struct mtx *mtx, uint32_t ms); +void usb2_printBCD(char *p, uint16_t p_len, uint16_t bcd); +void usb2_trim_spaces(char *p); + +#if (USB_USE_CONDVAR == 0) +void usb2_cv_init(struct cv *cv, const char *desc); +void usb2_cv_destroy(struct cv *cv); +void usb2_cv_wait(struct cv *cv, struct mtx *mtx); +int usb2_cv_wait_sig(struct cv *cv, struct mtx *mtx); +int usb2_cv_timedwait(struct cv *cv, struct mtx *mtx, int timo); +void usb2_cv_signal(struct cv *cv); +void usb2_cv_broadcast(struct cv *cv); + +#else +#define usb2_cv_init cv_init +#define usb2_cv_destroy cv_destroy +#define usb2_cv_wait cv_wait +#define usb2_cv_wait_sig cv_wait_sig +#define usb2_cv_timedwait cv_timedwait +#define usb2_cv_signal cv_signal +#define usb2_cv_broadcast cv_broadcast +#endif + +#endif /* _USB2_UTIL_H_ */ diff --git a/sys/dev/usb2/core/usbdevs b/sys/dev/usb2/core/usbdevs new file mode 100644 index 000000000000..8fa1cf1f14c2 --- /dev/null +++ b/sys/dev/usb2/core/usbdevs @@ -0,0 +1,2482 @@ +$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 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 SUNPLUS 0x04fc Sunplus +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 MICRODIA 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 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 CMOTECH Co., Ltd. +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 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 SUNPLUS 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 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 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 +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 MICRODIA 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 CMOTECH CDMA Technologies USB modem +product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem +product CMOTECH CDMA_MODEM1 0x6280 CMOTECH CDMA Technologies USB modem + +/* 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 + +/* 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 4800 0x0819 CX4800 MP scanner +product EPSON 4200 0x0820 CX4200 MP scanner +product EPSON 5000 0x082b DX-50x0 MFP scanner +product EPSON 6000 0x082e DX-60x0 MFP scanner +product EPSON DX7400 0x0838 DX7400/CX7300 scanner +product EPSON DX8400 0x0839 DX8400 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 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 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 HN210E 0x811c Ethernet HN210E +product HP2 C500 0x6002 PhotoSmart C500 + +/* 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 E270 0x1003 Huawei HSPA 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 + +/* 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 + +/* 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 + +/* 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 + +/* Microdia products */ +product MICRODIA TWINKLECAM 0x600d TwinkleCam USB camera + +/* 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 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 + +/* 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 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 U950D 0x4400 Novatel MC950D HSUPA +product NOVATEL ZEROCD 0x5010 Novatel ZeroCD +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 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 GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem + +/* 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 + +/* 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 + +/* 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 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 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 + +/* 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 + +/* 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 + +/* 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/dev/usb2/ethernet/if_aue2.c b/sys/dev/usb2/ethernet/if_aue2.c new file mode 100644 index 000000000000..3c05eaa2d74c --- /dev/null +++ b/sys/dev/usb2/ethernet/if_aue2.c @@ -0,0 +1,1567 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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 +__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 + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * 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 usb2_do_request(). Packet transfers are + * done using usb2_transfer() and friends. + */ + +/* + * NOTE: all function names beginning like "aue_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc aue_softc + +#define USB_DEBUG_VAR aue_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_DEPEND(aue, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(aue, usb2_core, 1, 1, 1); +MODULE_DEPEND(aue, ether, 1, 1, 1); +MODULE_DEPEND(aue, miibus, 1, 1, 1); + +#if USB_DEBUG +static int aue_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue"); +SYSCTL_INT(_hw_usb2_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const struct usb2_device_id aue_devs[] = { + {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, AUE_FLAG_PNA | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, 0)}, + {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY)}, + {USB_VPI(USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, 0)}, + {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, 0)}, + {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, 0)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, AUE_FLAG_PNA | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, 0)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, 0)}, + {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, 0)}, + {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, 0)}, + {USB_VPI(USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, 0)}, + {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, 0)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, AUE_FLAG_LSYS)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, 0)}, + {USB_VPI(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, 0)}, + {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, AUE_FLAG_PII)}, + {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, 0)}, + {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, AUE_FLAG_PII)}, +}; + +/* prototypes */ + +static device_probe_t aue_probe; +static device_attach_t aue_attach; +static device_detach_t aue_detach; +static device_shutdown_t aue_shutdown; + +static usb2_callback_t aue_intr_clear_stall_callback; +static usb2_callback_t aue_intr_callback; +static usb2_callback_t aue_bulk_read_clear_stall_callback; +static usb2_callback_t aue_bulk_read_callback; +static usb2_callback_t aue_bulk_write_clear_stall_callback; +static usb2_callback_t aue_bulk_write_callback; + +static void aue_cfg_do_request(struct aue_softc *sc, struct usb2_device_request *req, void *data); +static uint8_t aue_cfg_csr_read_1(struct aue_softc *sc, uint16_t reg); +static uint16_t aue_cfg_csr_read_2(struct aue_softc *sc, uint16_t reg); +static void aue_cfg_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val); +static void aue_cfg_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val); +static void aue_cfg_eeprom_getword(struct aue_softc *sc, uint8_t addr, uint8_t *dest); +static void aue_cfg_read_eeprom(struct aue_softc *sc, uint8_t *dest, uint16_t off, uint16_t len); + +static miibus_readreg_t aue_cfg_miibus_readreg; +static miibus_writereg_t aue_cfg_miibus_writereg; +static miibus_statchg_t aue_cfg_miibus_statchg; + +static usb2_config_td_command_t aue_cfg_setmulti; +static usb2_config_td_command_t aue_cfg_first_time_setup; +static usb2_config_td_command_t aue_config_copy; +static usb2_config_td_command_t aue_cfg_tick; +static usb2_config_td_command_t aue_cfg_pre_init; +static usb2_config_td_command_t aue_cfg_init; +static usb2_config_td_command_t aue_cfg_promisc_upd; +static usb2_config_td_command_t aue_cfg_ifmedia_upd; +static usb2_config_td_command_t aue_cfg_pre_stop; +static usb2_config_td_command_t aue_cfg_stop; + +static void aue_cfg_reset_pegasus_II(struct aue_softc *sc); +static void aue_cfg_reset(struct aue_softc *sc); +static void aue_start_cb(struct ifnet *ifp); +static void aue_init_cb(void *arg); +static void aue_start_transfers(struct aue_softc *sc); +static int aue_ifmedia_upd_cb(struct ifnet *ifp); +static void aue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); +static int aue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void aue_watchdog(void *arg); + +static const struct usb2_config aue_config[AUE_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (MCLBYTES + 2), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &aue_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &aue_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &aue_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &aue_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &aue_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &aue_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t aue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aue_probe), + 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_cfg_miibus_readreg), + DEVMETHOD(miibus_writereg, aue_cfg_miibus_writereg), + DEVMETHOD(miibus_statchg, aue_cfg_miibus_statchg), + + {0, 0} +}; + +static driver_t aue_driver = { + .name = "aue", + .methods = aue_methods, + .size = sizeof(struct aue_softc) +}; + +static devclass_t aue_devclass; + +DRIVER_MODULE(aue, ushub, aue_driver, aue_devclass, NULL, 0); +DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0); + +static void +aue_cfg_do_request(struct aue_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +#define AUE_CFG_SETBIT(sc, reg, x) \ + aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) | (x)) + +#define AUE_CFG_CLRBIT(sc, reg, x) \ + aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +aue_cfg_csr_read_1(struct aue_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = AUE_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + aue_cfg_do_request(sc, &req, &val); + return (val); +} + +static uint16_t +aue_cfg_csr_read_2(struct aue_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = AUE_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + aue_cfg_do_request(sc, &req, &val); + return (le16toh(val)); +} + +static void +aue_cfg_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AUE_UR_WRITEREG; + req.wValue[0] = val; + req.wValue[1] = 0; + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + aue_cfg_do_request(sc, &req, &val); + return; +} + +static void +aue_cfg_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AUE_UR_WRITEREG; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + val = htole16(val); + + aue_cfg_do_request(sc, &req, &val); + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void +aue_cfg_eeprom_getword(struct aue_softc *sc, uint8_t addr, + uint8_t *dest) +{ + uint16_t i; + + aue_cfg_csr_write_1(sc, AUE_EE_REG, addr); + aue_cfg_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); + + for (i = 0;; i++) { + + if (i < AUE_TIMEOUT) { + + if (aue_cfg_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + DPRINTF("EEPROM read timed out!\n"); + break; + } + } + + i = aue_cfg_csr_read_2(sc, AUE_EE_DATA); + + dest[0] = (i & 0xFF); + dest[1] = (i >> 8); + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void +aue_cfg_read_eeprom(struct aue_softc *sc, uint8_t *dest, + uint16_t off, uint16_t len) +{ + uint16_t i; + + for (i = 0; i < len; i++) { + aue_cfg_eeprom_getword(sc, off + i, dest + (i * 2)); + } + return; +} + +static int +aue_cfg_miibus_readreg(device_t dev, int phy, int reg) +{ + struct aue_softc *sc = device_get_softc(dev); + uint16_t i; + uint8_t do_unlock; + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + /* + * 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->sc_flags & AUE_FLAG_DUAL_PHY) { + + if (phy == 3) { + i = 0; + goto done; + } +#if 0 + if (phy != 1) { + i = 0; + goto done; + } +#endif + } + aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); + + for (i = 0;; i++) { + + if (i < AUE_TIMEOUT) { + + if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + DPRINTF("MII read timed out\n"); + break; + } + } + + i = aue_cfg_csr_read_2(sc, AUE_PHY_DATA); + +done: + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (i); +} + +static int +aue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct aue_softc *sc = device_get_softc(dev); + uint16_t i; + uint8_t do_unlock; + + if (phy == 3) { + return (0); + } + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + aue_cfg_csr_write_2(sc, AUE_PHY_DATA, data); + aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); + + for (i = 0;; i++) { + + if (i < AUE_TIMEOUT) { + if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + DPRINTF("MII write timed out\n"); + break; + } + } + + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +static void +aue_cfg_miibus_statchg(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + uint8_t do_unlock; + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { + AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + } else { + AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + } + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + } else { + AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + } + + AUE_CFG_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->sc_flags & AUE_FLAG_LSYS) { + uint16_t auxmode; + + auxmode = aue_cfg_miibus_readreg(dev, 0, 0x1b); + aue_cfg_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); + } + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return; +} + +static void +aue_cfg_setmulti(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t i; + + if ((cc->if_flags & IFF_ALLMULTI) || + (cc->if_flags & IFF_PROMISC)) { + AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + return; + } + AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + + /* clear existing ones */ + for (i = 0; i < 8; i++) { + aue_cfg_csr_write_1(sc, AUE_MAR0 + i, 0); + } + + /* now program new ones */ + for (i = 0; i < 8; i++) { + aue_cfg_csr_write_1(sc, AUE_MAR0 + i, cc->if_hash[i]); + } + return; +} + +static void +aue_cfg_reset_pegasus_II(struct aue_softc *sc) +{ + /* Magic constants taken from Linux driver. */ + aue_cfg_csr_write_1(sc, AUE_REG_1D, 0); + aue_cfg_csr_write_1(sc, AUE_REG_7B, 2); +#if 0 + if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) + aue_cfg_csr_write_1(sc, AUE_REG_81, 6); + else +#endif + aue_cfg_csr_write_1(sc, AUE_REG_81, 2); + + return; +} + +static void +aue_cfg_reset(struct aue_softc *sc) +{ + uint16_t i; + + AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); + + for (i = 0;; i++) { + + if (i < AUE_TIMEOUT) { + + if (!(aue_cfg_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + DPRINTF("reset timed out\n"); + break; + } + } + + /* + * 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_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0 | AUE_GPIO_SEL0)); + aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0 | AUE_GPIO_SEL0 | + AUE_GPIO_SEL1)); + + if (sc->sc_flags & AUE_FLAG_LSYS) { + /* Grrr. LinkSys has to be different from everyone else. */ + aue_cfg_csr_write_1(sc, AUE_GPIO0, + (AUE_GPIO_SEL0 | AUE_GPIO_SEL1)); + aue_cfg_csr_write_1(sc, AUE_GPIO0, + (AUE_GPIO_SEL0 | + AUE_GPIO_SEL1 | + AUE_GPIO_OUT0)); + } + if (sc->sc_flags & AUE_FLAG_PII) { + aue_cfg_reset_pegasus_II(sc); + } + /* wait a little while for the chip to get its brains in order: */ + usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + return; +} + +/* + * Probe for a Pegasus chip. + */ +static int +aue_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != AUE_IFACE_IDX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +aue_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct aue_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + if (uaa->info.bcdDevice >= 0x0201) { + sc->sc_flags |= AUE_FLAG_VER_2; /* XXX currently undocumented */ + } + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "aue lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = AUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, aue_config, AUE_ENDPT_MAX, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= AUE_FLAG_WAIT_LINK; + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &aue_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + aue_watchdog(sc); + + return (0); /* success */ + +detach: + aue_detach(dev); + return (ENXIO); /* failure */ +} + +static void +aue_cfg_first_time_setup(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + int error; + uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; + + /* reset the adapter */ + aue_cfg_reset(sc); + + /* set default value */ + bzero(eaddr, sizeof(eaddr)); + + /* get station address from the EEPROM */ + aue_cfg_read_eeprom(sc, eaddr, 0, 3); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("%s: could not if_alloc()\n", + sc->sc_name); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "aue", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = aue_ioctl_cb; + ifp->if_start = aue_start_cb; + ifp->if_watchdog = NULL; + ifp->if_init = aue_init_cb; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* + * XXX need Giant when accessing the device structures ! + */ + + mtx_unlock(&sc->sc_mtx); + + mtx_lock(&Giant); + + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + &aue_ifmedia_upd_cb, + &aue_ifmedia_sts_cb); + + mtx_unlock(&Giant); + + mtx_lock(&sc->sc_mtx); + + /* + * 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 (error) { + printf("%s: MII without any PHY!\n", + sc->sc_name); + if_free(ifp); + goto done; + } + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, eaddr); + + mtx_lock(&sc->sc_mtx); + +done: + return; +} + +static int +aue_detach(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + aue_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, AUE_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +aue_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AUE_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +aue_intr_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct aue_intrpkt pkt; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && + (xfer->actlen >= sizeof(pkt))) { + + usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt)); + + if (pkt.aue_txstat0) { + ifp->if_oerrors++; + } + if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL & + AUE_TXSTAT0_EXCESSCOLL)) { + ifp->if_collisions++; + } + } + case USB_ST_SETUP: + if (sc->sc_flags & AUE_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= AUE_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + } +} + +static void +aue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AUE_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +aue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "received %d bytes\n", xfer->actlen); + + if (sc->sc_flags & AUE_FLAG_VER_2) { + + if (xfer->actlen == 0) { + ifp->if_ierrors++; + goto tr_setup; + } + } else { + + if (xfer->actlen <= (4 + ETHER_CRC_LEN)) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, xfer->actlen - 4, &sc->sc_rxpkt, + sizeof(sc->sc_rxpkt)); + + /* + * turn off all the non-error bits in the rx status + * word: + */ + sc->sc_rxpkt.aue_rxstat &= AUE_RXSTAT_MASK; + + if (sc->sc_rxpkt.aue_rxstat) { + ifp->if_ierrors++; + goto tr_setup; + } + /* No errors; receive the packet. */ + xfer->actlen -= (4 + ETHER_CRC_LEN); + } + + m = usb2_ether_get_mbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen = min(xfer->actlen, m->m_len); + + usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->actlen; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & AUE_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m); + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= AUE_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +aue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AUE_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +aue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer of %d bytes complete\n", xfer->actlen); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & AUE_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + if (sc->sc_flags & AUE_FLAG_WAIT_LINK) { + /* + * don't send anything if there is no link ! + */ + goto done; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + if (sc->sc_flags & AUE_FLAG_VER_2) { + + xfer->frlengths[0] = m->m_pkthdr.len; + + usb2_m_copy_in(xfer->frbuffers, 0, + m, 0, m->m_pkthdr.len); + + } else { + + xfer->frlengths[0] = (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. + */ + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + usb2_copy_in(xfer->frbuffers, 0, buf, 2); + + usb2_m_copy_in(xfer->frbuffers, 2, + m, 0, m->m_pkthdr.len); + } + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= AUE_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +#define AUE_BITS 6 + +static void +aue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) +{ + uint8_t h; + + h = ether_crc32_le(ptr, ETHER_ADDR_LEN) & + ((1 << AUE_BITS) - 1); + cc->if_hash[(h >> 3)] |= (1 << (h & 7)); + return; +} + +static void +aue_config_copy(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + usb2_ether_cc(sc->sc_ifp, &aue_mchash, cc); + return; +} + +static void +aue_cfg_tick(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + mii_tick(mii); + + mii_pollstat(mii); + + if ((sc->sc_flags & AUE_FLAG_WAIT_LINK) && + (mii->mii_media_status & IFM_ACTIVE) && + (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { + sc->sc_flags &= ~AUE_FLAG_WAIT_LINK; + } + sc->sc_media_active = mii->mii_media_active; + sc->sc_media_status = mii->mii_media_status; + + /* start stopped transfers, if any */ + + aue_start_transfers(sc); + + return; +} + +static void +aue_start_cb(struct ifnet *ifp) +{ + struct aue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + aue_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +aue_init_cb(void *arg) +{ + struct aue_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_cfg_pre_init, &aue_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +aue_start_transfers(struct aue_softc *sc) +{ + if ((sc->sc_flags & AUE_FLAG_LL_READY) && + (sc->sc_flags & AUE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[4]); + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +aue_cfg_pre_init(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + aue_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= AUE_FLAG_HL_READY; + return; +} + +static void +aue_cfg_init(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct mii_data *mii = GET_MII(sc); + uint8_t i; + + /* + * Cancel pending I/O + */ + aue_cfg_stop(sc, cc, 0); + + /* Set MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) { + aue_cfg_csr_write_1(sc, AUE_PAR0 + i, cc->if_lladdr[i]); + } + + /* update promiscuous setting */ + aue_cfg_promisc_upd(sc, cc, 0); + + /* load the multicast filter */ + aue_cfg_setmulti(sc, cc, 0); + + /* enable RX and TX */ + aue_cfg_csr_write_1(sc, AUE_CTL0, + (AUE_CTL0_RXSTAT_APPEND | + AUE_CTL0_RX_ENB)); + + AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); + AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); + + mii_mediachg(mii); + + sc->sc_flags |= (AUE_FLAG_READ_STALL | + AUE_FLAG_WRITE_STALL | + AUE_FLAG_LL_READY); + + aue_start_transfers(sc); + return; +} + +static void +aue_cfg_promisc_upd(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* if we want promiscuous mode, set the allframes bit: */ + if (cc->if_flags & IFF_PROMISC) { + AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); + } else { + AUE_CFG_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); + } + return; +} + +/* + * Set media options. + */ +static int +aue_ifmedia_upd_cb(struct ifnet *ifp) +{ + struct aue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &aue_cfg_ifmedia_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static void +aue_cfg_ifmedia_upd(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + sc->sc_flags |= AUE_FLAG_WAIT_LINK; + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + mii_phy_reset(miisc); + } + } + mii_mediachg(mii); + + return; +} + +/* + * Report current media status. + */ +static void +aue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct aue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + ifmr->ifm_active = sc->sc_media_active; + ifmr->ifm_status = sc->sc_media_status; + + mtx_unlock(&sc->sc_mtx); + return; +} + +static int +aue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_config_copy, + &aue_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_cfg_pre_init, + &aue_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_cfg_pre_stop, + &aue_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_config_copy, + &aue_cfg_setmulti, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = GET_MII(sc); + if (mii == NULL) { + error = EINVAL; + } else { + error = ifmedia_ioctl + (ifp, (void *)data, &mii->mii_media, command); + } + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +aue_watchdog(void *arg) +{ + struct aue_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &aue_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &aue_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + * + * NOTE: can be called when "ifp" is NULL + */ +static void +aue_cfg_pre_stop(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + aue_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(AUE_FLAG_HL_READY | + AUE_FLAG_LL_READY); + + sc->sc_flags |= AUE_FLAG_WAIT_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[4]); + usb2_transfer_stop(sc->sc_xfer[5]); + return; +} + +static void +aue_cfg_stop(struct aue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + aue_cfg_csr_write_1(sc, AUE_CTL0, 0); + aue_cfg_csr_write_1(sc, AUE_CTL1, 0); + aue_cfg_reset(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 +aue_shutdown(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &aue_cfg_pre_stop, + &aue_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_aue2_reg.h b/sys/dev/usb2/ethernet/if_aue2_reg.h new file mode 100644 index 000000000000..8111b7a43f41 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_aue2_reg.h @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 1997, 1998, 1999 + * Bill Paul . 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. + */ + +#define AUE_UR_READREG 0xF0 +#define AUE_UR_WRITEREG 0xF1 + +#define AUE_CONFIG_INDEX 0 /* config number 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 usb2_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_MAX 6 + +#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 + +#define AUE_TIMEOUT 100 /* 10*ms */ +#define AUE_MIN_FRAMELEN 60 + +#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 GET_MII(sc) ((sc)->sc_miibus ? \ + device_get_softc((sc)->sc_miibus) : NULL) + +struct aue_intrpkt { + uint8_t aue_txstat0; + uint8_t aue_txstat1; + uint8_t aue_rxstat; + uint8_t aue_rxlostpkt0; + uint8_t aue_rxlostpkt1; + uint8_t aue_wakeupstat; + uint8_t aue_rsvd; +} __packed; + +struct aue_rxpkt { + uint16_t aue_pktlen; + uint8_t aue_rxstat; +} __packed; + + +struct aue_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + struct aue_rxpkt sc_rxpkt; + + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[AUE_ENDPT_MAX]; + device_t sc_miibus; + device_t sc_dev; + + uint32_t sc_unit; + uint32_t sc_media_active; + uint32_t sc_media_status; + + uint16_t sc_flags; +#define AUE_FLAG_LSYS 0x0001 /* use Linksys reset */ +#define AUE_FLAG_PNA 0x0002 /* has Home PNA */ +#define AUE_FLAG_PII 0x0004 /* Pegasus II chip */ +#define AUE_FLAG_WAIT_LINK 0x0008 /* wait for link to come up */ +#define AUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ +#define AUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ +#define AUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ +#define AUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ +#define AUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ +#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */ +#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */ + + uint8_t sc_name[16]; +}; diff --git a/sys/dev/usb2/ethernet/if_axe2.c b/sys/dev/usb2/ethernet/if_axe2.c new file mode 100644 index 000000000000..4bd4df2b2c23 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_axe2.c @@ -0,0 +1,1522 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul . 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 +__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 + * 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.) + */ + +/* + * Ax88178 and Ax88772 support backported from the OpenBSD driver. + * 2007/02/12, J.R. Oldroyd, fbsd@opal.com + * + * Manual here: + * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf + * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf + */ + +/* + * NOTE: all function names beginning like "axe_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc axe_softc + +#define USB_DEBUG_VAR axe_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_DEPEND(axe, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(axe, usb2_core, 1, 1, 1); +MODULE_DEPEND(axe, ether, 1, 1, 1); +MODULE_DEPEND(axe, miibus, 1, 1, 1); + +#if USB_DEBUG +static int axe_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe"); +SYSCTL_INT(_hw_usb2_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const struct usb2_device_id axe_devs[] = { + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, 0)}, + {USB_VPI(USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, 0)}, + {USB_VPI(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, AXE_FLAG_772)}, + {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, 0)}, + {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, AXE_FLAG_772)}, + {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, AXE_FLAG_772)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, 0)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, 0)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, AXE_FLAG_772)}, + {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, 0)}, + {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, 0)}, + {USB_VPI(USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, 0)}, + {USB_VPI(USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, 0)}, + {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, 0)}, + {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, AXE_FLAG_772)}, + {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, 0)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, AXE_FLAG_178)}, + {USB_VPI(USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, 0)}, +}; + +static device_probe_t axe_probe; +static device_attach_t axe_attach; +static device_detach_t axe_detach; +static device_shutdown_t axe_shutdown; + +static usb2_callback_t axe_intr_clear_stall_callback; +static usb2_callback_t axe_intr_callback; +static usb2_callback_t axe_bulk_read_clear_stall_callback; +static usb2_callback_t axe_bulk_read_callback; +static usb2_callback_t axe_bulk_write_clear_stall_callback; +static usb2_callback_t axe_bulk_write_callback; + +static void axe_cfg_cmd(struct axe_softc *sc, uint16_t cmd, uint16_t index, uint16_t val, void *buf); + +static miibus_readreg_t axe_cfg_miibus_readreg; +static miibus_writereg_t axe_cfg_miibus_writereg; +static miibus_statchg_t axe_cfg_miibus_statchg; + +static usb2_config_td_command_t axe_cfg_ifmedia_upd; +static usb2_config_td_command_t axe_config_copy; +static usb2_config_td_command_t axe_cfg_setmulti; +static usb2_config_td_command_t axe_cfg_first_time_setup; +static usb2_config_td_command_t axe_cfg_tick; +static usb2_config_td_command_t axe_cfg_pre_init; +static usb2_config_td_command_t axe_cfg_init; +static usb2_config_td_command_t axe_cfg_promisc_upd; +static usb2_config_td_command_t axe_cfg_pre_stop; +static usb2_config_td_command_t axe_cfg_stop; + +static int axe_ifmedia_upd_cb(struct ifnet *ifp); +static void axe_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); +static void axe_cfg_reset(struct axe_softc *sc); +static void axe_start_cb(struct ifnet *ifp); +static void axe_start_transfers(struct axe_softc *sc); +static void axe_init_cb(void *arg); +static int axe_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void axe_watchdog(void *arg); +static void axe_cfg_ax88178_init(struct axe_softc *); +static void axe_cfg_ax88772_init(struct axe_softc *); + +static const struct usb2_config axe_config[AXE_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = AXE_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &axe_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, +#if (MCLBYTES < 2048) +#error "(MCLBYTES < 2048)" +#endif + .mh.bufsize = MCLBYTES, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &axe_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &axe_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &axe_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &axe_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &axe_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t axe_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, axe_probe), + 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_cfg_miibus_readreg), + DEVMETHOD(miibus_writereg, axe_cfg_miibus_writereg), + DEVMETHOD(miibus_statchg, axe_cfg_miibus_statchg), + + {0, 0} +}; + +static driver_t axe_driver = { + .name = "axe", + .methods = axe_methods, + .size = sizeof(struct axe_softc), +}; + +static devclass_t axe_devclass; + +DRIVER_MODULE(axe, ushub, axe_driver, axe_devclass, NULL, 0); +DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0); + +static void +axe_cfg_cmd(struct axe_softc *sc, uint16_t cmd, uint16_t index, + uint16_t val, void *buf) +{ + struct usb2_device_request req; + usb2_error_t err; + uint16_t length = AXE_CMD_LEN(cmd); + + req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ? + UT_WRITE_VENDOR_DEVICE : + UT_READ_VENDOR_DEVICE); + req.bRequest = AXE_CMD_CMD(cmd); + USETW(req.wValue, val); + USETW(req.wIndex, index); + USETW(req.wLength, length); + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, &req, buf, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + + if ((req.bmRequestType & UT_READ) && length) { + bzero(buf, length); + } + } + return; +} + +static int +axe_cfg_miibus_readreg(device_t dev, int phy, int reg) +{ + struct axe_softc *sc = device_get_softc(dev); + uint16_t val; + uint8_t do_unlock; + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + +#if 0 + /* + * The chip tells us the MII address of any supported + * PHYs attached to the chip, so only read from those. + */ + + if ((sc->sc_phyaddrs[0] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[0])) { + val = 0; + goto done; + } + if ((sc->sc_phyaddrs[1] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[1])) { + val = 0; + goto done; + } +#endif + if ((sc->sc_phyaddrs[0] != 0xFF) && (sc->sc_phyaddrs[0] != phy)) { + val = 0; + goto done; + } + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cfg_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + val = le16toh(val); + + if (val && (val != 0xffff)) { + sc->sc_phyaddrs[0] = phy; + } +done: + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (val); +} + +static int +axe_cfg_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct axe_softc *sc = device_get_softc(dev); + uint8_t do_unlock; + + val = htole16(val); + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cfg_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +static void +axe_cfg_miibus_statchg(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + uint16_t val; + uint8_t do_unlock; + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + val = AXE_MEDIA_FULL_DUPLEX; + else + val = 0; + + if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { + + 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; + } + } + axe_cfg_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return; +} + +/* + * Set media options. + */ +static int +axe_ifmedia_upd_cb(struct ifnet *ifp) +{ + struct axe_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &axe_cfg_ifmedia_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static void +axe_cfg_ifmedia_upd(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + sc->sc_flags |= AXE_FLAG_WAIT_LINK; + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + mii_phy_reset(miisc); + } + } + mii_mediachg(mii); + + return; +} + +/* + * Report current media status. + */ +static void +axe_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct axe_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + ifmr->ifm_active = sc->sc_media_active; + ifmr->ifm_status = sc->sc_media_status; + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +axe_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) +{ + uint8_t h; + + h = (ether_crc32_be(ptr, ETHER_ADDR_LEN) >> 26); + cc->if_hash[(h >> 3)] |= (1 << (h & 7)); + return; +} + +static void +axe_config_copy(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + usb2_ether_cc(sc->sc_ifp, &axe_mchash, cc); + return; +} + +static void +axe_cfg_setmulti(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t rxmode; + + axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + + rxmode = le16toh(rxmode); + + if (cc->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + rxmode |= AXE_RXCMD_ALLMULTI; + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + return; + } + rxmode &= ~AXE_RXCMD_ALLMULTI; + + axe_cfg_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, cc->if_hash); + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + return; +} + +static void +axe_cfg_reset(struct axe_softc *sc) +{ + struct usb2_config_descriptor *cd; + usb2_error_t err; + + cd = usb2_get_config_descriptor(sc->sc_udev); + + err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) { + DPRINTF("reset failed (ignored)\n"); + } + /* + * wait a little while for the chip to get its brains in order: + */ + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + return; +} + +/* + * Probe for a AX88172 chip. + */ +static int +axe_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != AXE_CONFIG_IDX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != AXE_IFACE_IDX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +axe_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct axe_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "axe lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = AXE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, axe_config, AXE_ENDPT_MAX, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= AXE_FLAG_WAIT_LINK; + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &axe_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + axe_watchdog(sc); + + return (0); /* success */ + +detach: + axe_detach(dev); + return (ENXIO); /* failure */ +} + +static void +axe_cfg_ax88178_init(struct axe_softc *sc) +{ + uint16_t eeprom; + uint16_t phymode; + uint16_t gpio0; + uint8_t err; + + DPRINTF("\n"); + + axe_cfg_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); + /* XXX magic */ + axe_cfg_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); + axe_cfg_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); + + /* For big-endian machines: */ + eeprom = le16toh(eeprom); + + /* 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_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); + + if ((eeprom >> 8) != 0x01) { + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); + + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 3); + + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); + } else { + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); + + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); + } + + /* soft reset */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); + + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); + + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); + return; +} + +static void +axe_cfg_ax88772_init(struct axe_softc *sc) +{ + uint8_t err; + + DPRINTF("\n"); + + axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); + + if (sc->sc_phyaddrs[1] == AXE_INTPHY) { + /* ask for the embedded PHY */ + axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 64); + + /* power down and reset state, pin reset state */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_CLEAR, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); + + /* power down/reset state, pin operating state */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); + + /* power up, reset */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_PRL, NULL); + + /* power up, operating */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); + } else { + /* ask for external PHY */ + axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 64); + + /* power down internal PHY */ + axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + } + + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); + return; +} + +static void +axe_cfg_first_time_setup(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + int error; + uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; + + /* set default value */ + bzero(eaddr, sizeof(eaddr)); + + /* + * Load PHY indexes first. Needed by axe_xxx_init(). + */ + axe_cfg_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); + + if (sc->sc_flags & AXE_FLAG_178) { + axe_cfg_ax88178_init(sc); + } else if (sc->sc_flags & AXE_FLAG_772) { + axe_cfg_ax88772_init(sc); + } + /* + * Get station address. + */ + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) + axe_cfg_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, eaddr); + else + axe_cfg_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, eaddr); + + /* + * Fetch IPG values. + */ + axe_cfg_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); + + /* + * Work around broken adapters that appear to lie about + * their PHY addresses. + */ + sc->sc_phyaddrs[0] = sc->sc_phyaddrs[1] = 0xFF; + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("%s: could not if_alloc()\n", + sc->sc_name); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "axe", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = axe_ioctl_cb; + ifp->if_start = axe_start_cb; + ifp->if_watchdog = NULL; + ifp->if_init = axe_init_cb; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* + * XXX need Giant when accessing the device structures ! + */ + + mtx_unlock(&sc->sc_mtx); + + mtx_lock(&Giant); + + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + &axe_ifmedia_upd_cb, + &axe_ifmedia_sts_cb); + mtx_unlock(&Giant); + + mtx_lock(&sc->sc_mtx); + + if (error) { + printf("%s: MII without any PHY!\n", + sc->sc_name); + if_free(ifp); + goto done; + } + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + /* + * Call MI attach routine. + */ + + ether_ifattach(ifp, eaddr); + + mtx_lock(&sc->sc_mtx); + +done: + return; +} + +static int +axe_detach(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + axe_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, AXE_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +axe_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AXE_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +axe_intr_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* do nothing */ + + case USB_ST_SETUP: + if (sc->sc_flags & AXE_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= AXE_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + } +} + +static void +axe_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AXE_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +#if (AXE_BULK_BUF_SIZE >= 0x10000) +#error "Please update axe_bulk_read_callback()!" +#endif + +static void +axe_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + struct axe_sframe_hdr hdr; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct { /* mini-queue */ + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + uint16_t ifq_len; + } mq = { + NULL, NULL, 0 + }; + uint16_t pos; + uint16_t len; + uint16_t adjust; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + pos = 0; + + while (1) { + + if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { + + if (xfer->actlen < sizeof(hdr)) { + /* too little data */ + break; + } + usb2_copy_out(xfer->frbuffers, pos, &hdr, sizeof(hdr)); + + if ((hdr.len ^ hdr.ilen) != 0xFFFF) { + /* we lost sync */ + break; + } + xfer->actlen -= sizeof(hdr); + pos += sizeof(hdr); + + len = le16toh(hdr.len); + if (len > xfer->actlen) { + /* invalid length */ + break; + } + adjust = (len & 1); + + } else { + len = xfer->actlen; + adjust = 0; + } + + if (len < sizeof(struct ether_header)) { + ifp->if_ierrors++; + goto skip; + } + m = usb2_ether_get_mbuf(); + if (m == NULL) { + /* we are out of memory */ + break; + } + if (m->m_len > len) { + m->m_len = len; + } + usb2_copy_out(xfer->frbuffers, pos, m->m_data, m->m_len); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len; + + /* enqueue */ + _IF_ENQUEUE(&mq, m); + + skip: + + pos += len; + xfer->actlen -= len; + + if (xfer->actlen <= adjust) { + /* we are finished */ + goto tr_setup; + } + pos += adjust; + xfer->actlen -= adjust; + } + + /* count an error */ + ifp->if_ierrors++; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & AXE_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (mq.ifq_head) { + + mtx_unlock(&sc->sc_mtx); + + while (1) { + + _IF_DEQUEUE(&mq, m); + + if (m == NULL) + break; + + (ifp->if_input) (ifp, m); + } + + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= AXE_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +axe_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~AXE_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4))) +#error "Please update axe_bulk_write_callback()!" +#endif + +static void +axe_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct axe_softc *sc = xfer->priv_sc; + struct axe_sframe_hdr hdr; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint16_t pos; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & AXE_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + if (sc->sc_flags & AXE_FLAG_WAIT_LINK) { + /* + * don't send anything if there is no link ! + */ + goto done; + } + pos = 0; + + while (1) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + if (pos > 0) + break; /* send out data */ + else + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { + + hdr.len = htole16(m->m_pkthdr.len); + hdr.ilen = ~hdr.len; + + usb2_copy_in(xfer->frbuffers, pos, &hdr, sizeof(hdr)); + + pos += sizeof(hdr); + + /* + * NOTE: Some drivers force a short packet + * by appending a dummy header with zero + * length at then end of the USB transfer. + * This driver uses the + * USB_FORCE_SHORT_XFER flag instead. + */ + } + usb2_m_copy_in(xfer->frbuffers, pos, + m, 0, m->m_pkthdr.len); + + pos += m->m_pkthdr.len; + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { + if (pos > (AXE_BULK_BUF_SIZE - MCLBYTES - sizeof(hdr))) { + /* send out frame(s) */ + break; + } + } else { + /* send out frame */ + break; + } + } + + xfer->frlengths[0] = pos; + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= AXE_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +static void +axe_cfg_tick(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + mii_tick(mii); + + mii_pollstat(mii); + + if ((sc->sc_flags & AXE_FLAG_WAIT_LINK) && + (mii->mii_media_status & IFM_ACTIVE) && + (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { + sc->sc_flags &= ~AXE_FLAG_WAIT_LINK; + } + sc->sc_media_active = mii->mii_media_active; + sc->sc_media_status = mii->mii_media_status; + + /* start stopped transfers, if any */ + + axe_start_transfers(sc); + + return; +} + +static void +axe_start_cb(struct ifnet *ifp) +{ + struct axe_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + axe_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +axe_start_transfers(struct axe_softc *sc) +{ + if ((sc->sc_flags & AXE_FLAG_LL_READY) && + (sc->sc_flags & AXE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[4]); + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +axe_init_cb(void *arg) +{ + struct axe_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_cfg_pre_init, &axe_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +axe_cfg_pre_init(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + axe_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= AXE_FLAG_HL_READY; + return; +} + +static void +axe_cfg_init(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct mii_data *mii = GET_MII(sc); + uint16_t rxmode; + + /* + * Cancel pending I/O + */ + + axe_cfg_stop(sc, cc, 0); + +#if 0 + /* Set MAC address */ + axe_mac(sc, cc->if_lladdr); +#endif + + /* Set transmitter IPG values */ + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { + axe_cfg_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], + (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); + } else { + axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); + axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); + axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL); + } + + /* Enable receiver, set RX mode */ + rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { + rxmode |= AXE_178_RXCMD_MFB_2048; /* chip default */ + } else { + rxmode |= AXE_172_RXCMD_UNICAST; + } + + /* If we want promiscuous mode, set the allframes bit. */ + if (cc->if_flags & IFF_PROMISC) { + rxmode |= AXE_RXCMD_PROMISC; + } + if (cc->if_flags & IFF_BROADCAST) { + rxmode |= AXE_RXCMD_BROADCAST; + } + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + /* Load the multicast filter. */ + axe_cfg_setmulti(sc, cc, 0); + + mii_mediachg(mii); + + sc->sc_flags |= (AXE_FLAG_READ_STALL | + AXE_FLAG_WRITE_STALL | + AXE_FLAG_LL_READY); + + axe_start_transfers(sc); + + return; +} + +static void +axe_cfg_promisc_upd(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t rxmode; + + axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + + rxmode = le16toh(rxmode); + + if (cc->if_flags & IFF_PROMISC) { + rxmode |= AXE_RXCMD_PROMISC; + } else { + rxmode &= ~AXE_RXCMD_PROMISC; + } + + axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + axe_cfg_setmulti(sc, cc, 0); + + return; +} + +static int +axe_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct axe_softc *sc = ifp->if_softc; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_config_copy, + &axe_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_cfg_pre_init, + &axe_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_cfg_pre_stop, + &axe_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_config_copy, + &axe_cfg_setmulti, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = GET_MII(sc); + if (mii == NULL) { + error = EINVAL; + } else { + error = ifmedia_ioctl + (ifp, (void *)data, &mii->mii_media, command); + } + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +axe_watchdog(void *arg) +{ + struct axe_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &axe_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &axe_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +/* + * NOTE: can be called when "ifp" is NULL + */ +static void +axe_cfg_pre_stop(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + axe_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(AXE_FLAG_HL_READY | + AXE_FLAG_LL_READY); + + sc->sc_flags |= AXE_FLAG_WAIT_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[4]); + usb2_transfer_stop(sc->sc_xfer[5]); + return; +} + +static void +axe_cfg_stop(struct axe_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + axe_cfg_reset(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 = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &axe_cfg_pre_stop, + &axe_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_axe2_reg.h b/sys/dev/usb2/ethernet/if_axe2_reg.h new file mode 100644 index 000000000000..9228f7770926 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_axe2_reg.h @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul . 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, AX88178 + * and AX88772 to ethernet controllers. + */ + +/* + * 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 bits, 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_IS_WRITE(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_182_CMD_WRITE_RXTX_SRAM 0x8103 +#define AXE_172_CMD_WRITE_TX_SRAM 0x0104 +#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_178_CMD_WRITE_IPG012 0x0112 +#define AXE_172_CMD_WRITE_IPG1 0x0113 +#define AXE_178_CMD_READ_NODEID 0x6013 +#define AXE_172_CMD_WRITE_IPG2 0x0114 +#define AXE_178_CMD_WRITE_NODEID 0x6114 +#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_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_RXCMD_ENABLE 0x0080 +#define AXE_178_RXCMD_MFB_MASK 0x0300 +#define AXE_178_RXCMD_MFB_2048 0x0000 +#define AXE_178_RXCMD_MFB_4096 0x0100 +#define AXE_178_RXCMD_MFB_8192 0x0200 +#define AXE_178_RXCMD_MFB_16384 0x0300 + +#define AXE_NOPHY 0xE0 +#define AXE_INTPHY 0x10 + +#define AXE_BULK_BUF_SIZE 16384 /* bytes */ + +#define AXE_CTL_READ 0x01 +#define AXE_CTL_WRITE 0x02 + +#define AXE_CONFIG_IDX 0 /* config number 1 */ +#define AXE_IFACE_IDX 0 + +/* The interrupt endpoint is currently unused by the ASIX part. */ +#define AXE_ENDPT_MAX 6 + +struct axe_sframe_hdr { + uint16_t len; + uint16_t ilen; +} __packed; + +#define GET_MII(sc) ((sc)->sc_miibus ? \ + device_get_softc((sc)->sc_miibus) : NULL) + +struct axe_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[AXE_ENDPT_MAX]; + device_t sc_miibus; + device_t sc_dev; + + uint32_t sc_unit; + uint32_t sc_media_active; + uint32_t sc_media_status; + + uint16_t sc_flags; +#define AXE_FLAG_WAIT_LINK 0x0001 +#define AXE_FLAG_INTR_STALL 0x0002 +#define AXE_FLAG_READ_STALL 0x0004 +#define AXE_FLAG_WRITE_STALL 0x0008 +#define AXE_FLAG_LL_READY 0x0010 +#define AXE_FLAG_HL_READY 0x0020 +#define AXE_FLAG_772 0x0040 /* AX88772 */ +#define AXE_FLAG_178 0x0080 /* AX88178 */ + + uint8_t sc_ipgs[3]; + uint8_t sc_phyaddrs[2]; + + uint8_t sc_name[16]; +}; diff --git a/sys/dev/usb2/ethernet/if_cdce2.c b/sys/dev/usb2/ethernet/if_cdce2.c new file mode 100644 index 000000000000..64eedca17e99 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_cdce2.c @@ -0,0 +1,1355 @@ +/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc cdce_softc + +#define USB_DEBUG_VAR cdce_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static device_probe_t cdce_probe; +static device_attach_t cdce_attach; +static device_detach_t cdce_detach; +static device_shutdown_t cdce_shutdown; +static device_suspend_t cdce_suspend; +static device_resume_t cdce_resume; +static usb2_handle_request_t cdce_handle_request; + +static usb2_callback_t cdce_bulk_write_callback; +static usb2_callback_t cdce_bulk_read_callback; +static usb2_callback_t cdce_intr_read_callback; +static usb2_callback_t cdce_intr_write_callback; + +static void cdce_start_cb(struct ifnet *ifp); +static void cdce_start_transfers(struct cdce_softc *sc); +static uint32_t cdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len); +static void cdce_stop(struct cdce_softc *sc); +static int cdce_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void cdce_init_cb(void *arg); +static int cdce_ifmedia_upd_cb(struct ifnet *ifp); +static void cdce_ifmedia_sts_cb(struct ifnet *const ifp, struct ifmediareq *req); + +#if USB_DEBUG +static int cdce_debug = 0; +static int cdce_force_512x4 = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB cdce"); +SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0, + "cdce debug level"); +SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, force_512x4, CTLFLAG_RW, + &cdce_force_512x4, 0, "cdce force 512x4 protocol"); +#endif + +static const struct usb2_config cdce_config[CDCE_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 0, + /* Host Mode */ + .mh.frames = CDCE_512X4_FRAGS_MAX + 1, + .mh.bufsize = (CDCE_512X4_FRAMES_MAX * MCLBYTES) + sizeof(struct usb2_cdc_mf_eth_512x4_header), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, + .mh.callback = &cdce_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + /* Device Mode */ + .md.frames = CDCE_512X4_FRAGS_MAX + 1, + .md.bufsize = (CDCE_512X4_FRAMES_MAX * MCLBYTES) + sizeof(struct usb2_cdc_mf_eth_512x4_header), + .md.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .md.callback = &cdce_bulk_read_callback, + .md.timeout = 0, /* no timeout */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 0, + /* Host Mode */ + .mh.frames = CDCE_512X4_FRAGS_MAX + 1, + .mh.bufsize = (CDCE_512X4_FRAMES_MAX * MCLBYTES) + sizeof(struct usb2_cdc_mf_eth_512x4_header), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .mh.callback = &cdce_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + /* Device Mode */ + .md.frames = CDCE_512X4_FRAGS_MAX + 1, + .md.bufsize = (CDCE_512X4_FRAMES_MAX * MCLBYTES) + sizeof(struct usb2_cdc_mf_eth_512x4_header), + .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, + .md.callback = &cdce_bulk_write_callback, + .md.timeout = 10000, /* 10 seconds */ + }, + + [2] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 1, + /* Host Mode */ + .mh.bufsize = CDCE_IND_SIZE_MAX, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .mh.callback = &cdce_intr_read_callback, + .mh.timeout = 0, + /* Device Mode */ + .md.bufsize = CDCE_IND_SIZE_MAX, + .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .md.callback = &cdce_intr_write_callback, + .md.timeout = 10000, /* 10 seconds */ + }, +}; + +static device_method_t cdce_methods[] = { + /* USB interface */ + DEVMETHOD(usb2_handle_request, cdce_handle_request), + + /* Device interface */ + DEVMETHOD(device_probe, cdce_probe), + DEVMETHOD(device_attach, cdce_attach), + DEVMETHOD(device_detach, cdce_detach), + DEVMETHOD(device_suspend, cdce_suspend), + DEVMETHOD(device_resume, cdce_resume), + DEVMETHOD(device_shutdown, cdce_shutdown), + + {0, 0} +}; + +static driver_t cdce_driver = { + .name = "cdce", + .methods = cdce_methods, + .size = sizeof(struct cdce_softc), +}; + +static devclass_t cdce_devclass; + +DRIVER_MODULE(cdce, ushub, cdce_driver, cdce_devclass, NULL, 0); +MODULE_VERSION(cdce, 1); +MODULE_DEPEND(cdce, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(cdce, usb2_core, 1, 1, 1); +MODULE_DEPEND(cdce, ether, 1, 1, 1); + +static const struct usb2_device_id cdce_devs[] = { + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)}, + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)}, + + {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, +}; + +static int +cdce_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + return (usb2_lookup_id_by_uaa(cdce_devs, sizeof(cdce_devs), uaa)); +} + +static int +cdce_attach(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface *iface; + const struct usb2_cdc_union_descriptor *ud; + const struct usb2_cdc_ethernet_descriptor *ue; + const struct usb2_interface_descriptor *id; + struct ifnet *ifp; + int error; + uint8_t alt_index; + uint8_t i; + uint8_t eaddr[ETHER_ADDR_LEN]; + char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + /* search for alternate settings */ + if (uaa->usb2_mode == USB_MODE_HOST) { + + struct usb2_descriptor *desc; + struct usb2_config_descriptor *cd; + + cd = usb2_get_config_descriptor(uaa->device); + desc = (void *)(uaa->iface->idesc); + id = (void *)desc; + i = id->bInterfaceNumber; + alt_index = 0; + while ((desc = usb2_desc_foreach(cd, desc))) { + id = (void *)desc; + if ((id->bDescriptorType == UDESC_INTERFACE) && + (id->bLength >= sizeof(*id))) { + if (id->bInterfaceNumber != i) { + alt_index = 0; + break; + } + if ((id->bInterfaceClass == UICLASS_CDC) && + (id->bInterfaceSubClass == + UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL) && + (id->bInterfaceProtocol == UIPROTO_CDC_ETH_512X4)) { + + alt_index = id->bAlternateSetting; + /* + * We want this alt setting hence + * the protocol supports multi + * sub-framing ! + */ + break; + } + } + } + + if (alt_index > 0) { + + error = usb2_set_alt_interface_index(uaa->device, + uaa->info.bIfaceIndex, alt_index); + if (error) { + device_printf(dev, "Could not set alternate " + "setting, error = %s\n", usb2_errstr(error)); + return (EINVAL); + } + } + } + /* get the interface subclass we are using */ + sc->sc_iface_protocol = uaa->iface->idesc->bInterfaceProtocol; +#if USB_DEBUG + if (cdce_force_512x4) { + sc->sc_iface_protocol = UIPROTO_CDC_ETH_512X4; + } +#endif + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "cdce lock", NULL, MTX_DEF | MTX_RECURSE); + + if (sc->sc_flags & CDCE_FLAG_NO_UNION) { + sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; + sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; + sc->sc_data_iface_no = 0; /* not used */ + goto alloc_transfers; + } + ud = usb2_find_descriptor + (uaa->device, NULL, uaa->info.bIfaceIndex, + UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1); + + if ((ud == NULL) || (ud->bLength < sizeof(*ud))) { + device_printf(dev, "no union descriptor!\n"); + goto detach; + } + sc->sc_data_iface_no = ud->bSlaveInterface[0]; + + for (i = 0;; i++) { + + iface = usb2_get_iface(uaa->device, i); + + if (iface) { + + id = usb2_get_interface_descriptor(iface); + + if (id && (id->bInterfaceNumber == + sc->sc_data_iface_no)) { + sc->sc_ifaces_index[0] = i; + sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; + usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + break; + } + } else { + device_printf(dev, "no data interface found!\n"); + goto detach; + } + } + + /* + * + * + * 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. + * + * + * + * 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. + */ + +alloc_transfers: + + for (i = 0; i < 32; i++) { + + error = usb2_set_alt_interface_index + (uaa->device, sc->sc_ifaces_index[0], i); + + if (error) { + device_printf(dev, "no valid alternate " + "setting found!\n"); + goto detach; + } + error = usb2_transfer_setup + (uaa->device, sc->sc_ifaces_index, + sc->sc_xfer, cdce_config, CDCE_N_TRANSFER, + sc, &sc->sc_mtx); + + if (error == 0) { + break; + } + } + + ifmedia_init(&sc->sc_ifmedia, 0, + &cdce_ifmedia_upd_cb, + &cdce_ifmedia_sts_cb); + + ue = usb2_find_descriptor + (uaa->device, NULL, uaa->info.bIfaceIndex, + UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1); + + if ((ue == NULL) || (ue->bLength < sizeof(*ue))) { + error = USB_ERR_INVAL; + } else { + error = usb2_req_get_string_any + (uaa->device, &Giant, eaddr_str, + sizeof(eaddr_str), ue->iMacAddress); + } + + if (error) { + + /* fake MAC address */ + + device_printf(dev, "faking MAC address\n"); + eaddr[0] = 0x2a; + memcpy(&eaddr[1], &ticks, sizeof(uint32_t)); + eaddr[5] = sc->sc_unit; + + } else { + + bzero(eaddr, sizeof(eaddr)); + + for (i = 0; i < (ETHER_ADDR_LEN * 2); i++) { + + char c = eaddr_str[i]; + + if ((c >= '0') && (c <= '9')) { + c -= '0'; + } else if (c != 0) { + c -= 'A' - 10; + } else { + break; + } + + c &= 0xf; + + if ((i & 1) == 0) { + c <<= 4; + } + eaddr[i / 2] |= c; + } + + if (uaa->usb2_mode == USB_MODE_DEVICE) { + /* + * Do not use the same MAC address like the peer ! + */ + eaddr[5] ^= 0xFF; + } + } + + ifp = if_alloc(IFT_ETHER); + + if (ifp == NULL) { + device_printf(dev, "cannot if_alloc()\n"); + goto detach; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "cdce", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = cdce_ioctl_cb; + ifp->if_output = ether_output; + ifp->if_start = cdce_start_cb; + ifp->if_init = cdce_init_cb; + ifp->if_baudrate = 11000000; + if (sc->sc_iface_protocol == UIPROTO_CDC_ETH_512X4) { + IFQ_SET_MAXLEN(&ifp->if_snd, CDCE_512X4_IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = CDCE_512X4_IFQ_MAXLEN; + } else { + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + } + IFQ_SET_READY(&ifp->if_snd); + + /* no IFM type for 11Mbps USB, so go with 10baseT */ + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, 0); + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T); + + sc->sc_ifp = ifp; + + ether_ifattach(ifp, eaddr); + + /* start the interrupt transfer, if any */ + mtx_lock(&sc->sc_mtx); + usb2_transfer_start(sc->sc_xfer[2]); + mtx_unlock(&sc->sc_mtx); + + return (0); /* success */ + +detach: + cdce_detach(dev); + return (ENXIO); /* failure */ +} + +static int +cdce_detach(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + mtx_lock(&sc->sc_mtx); + + cdce_stop(sc); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + ifmedia_removeall(&sc->sc_ifmedia); + } + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cdce_start_cb(struct ifnet *ifp) +{ + struct cdce_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + cdce_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +cdce_start_transfers(struct cdce_softc *sc) +{ + if ((sc->sc_flags & CDCE_FLAG_LL_READY) && + (sc->sc_flags & CDCE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static uint32_t +cdce_m_frags(struct mbuf *m) +{ + uint32_t temp = 1; + + while ((m = m->m_next)) { + temp++; + } + return (temp); +} + +static void +cdce_fwd_mq(struct cdce_softc *sc, struct cdce_mq *mq) +{ + struct mbuf *m; + struct ifnet *ifp = sc->sc_ifp; + + if (mq->ifq_head) { + + mtx_unlock(&sc->sc_mtx); + + while (1) { + + _IF_DEQUEUE(mq, m); + + if (m == NULL) + break; + + (ifp->if_input) (ifp, m); + } + + mtx_lock(&sc->sc_mtx); + } + return; +} + +static void +cdce_free_mq(struct cdce_mq *mq) +{ + struct mbuf *m; + + if (mq->ifq_head) { + + while (1) { + + _IF_DEQUEUE(mq, m); + + if (m == NULL) + break; + + m_freem(m); + } + } + return; +} + +static void +cdce_bulk_write_512x4_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct mbuf *mt; + uint16_t x; + uint16_t y; + uint16_t flen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete: " + "%u bytes in %u fragments and %u frames\n", + xfer->actlen, xfer->nframes, sc->sc_tx_mq.ifq_len); + + /* update packet counter */ + ifp->if_opackets += sc->sc_tx_mq.ifq_len; + + /* free all previous mbufs */ + cdce_free_mq(&sc->sc_tx_mq); + + case USB_ST_SETUP: +tr_setup: + x = 0; /* number of frames */ + y = 1; /* number of fragments */ + + while (x != CDCE_512X4_FRAMES_MAX) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + break; + } + if (m->m_pkthdr.len > MCLBYTES) { + m_freem(m); + ifp->if_oerrors++; + continue; + } + if (cdce_m_frags(m) > CDCE_512X4_FRAME_FRAG_MAX) { + mt = m_defrag(m, M_DONTWAIT); + if (mt == NULL) { + m_freem(m); + ifp->if_oerrors++; + continue; + } + m = mt; + } + _IF_ENQUEUE(&sc->sc_tx_mq, m); + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + +#if (CDCE_512X4_FRAG_LENGTH_MASK < MCLBYTES) +#error "(CDCE_512X4_FRAG_LENGTH_MASK < MCLBYTES)" +#endif + do { + + flen = m->m_len & CDCE_512X4_FRAG_LENGTH_MASK; + xfer->frlengths[y] = m->m_len; + usb2_set_frame_data(xfer, m->m_data, y); + + if (m->m_next == NULL) { + flen |= CDCE_512X4_FRAG_LAST_MASK; + } + USETW(sc->sc_tx.hdr.wFragLength[y - 1], flen); + + y++; + + } while ((m = m->m_next)); + + x++; + } + + if (y == 1) { + /* no data to transmit */ + break; + } + /* fill in Signature */ + sc->sc_tx.hdr.bSig[0] = 'F'; + sc->sc_tx.hdr.bSig[1] = 'L'; + + /* + * We ensure that the header results in a short packet by + * making the length odd ! + */ + USETW(sc->sc_tx.hdr.wFragLength[y - 1], 0); + xfer->frlengths[0] = CDCE_512X4_FRAG_LENGTH_OFFSET + ((y - 1) * 2) + 1; + usb2_set_frame_data(xfer, &sc->sc_tx.hdr, 0); + xfer->nframes = y; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + /* update error counter */ + ifp->if_oerrors += sc->sc_tx_mq.ifq_len; + + /* free all previous mbufs */ + cdce_free_mq(&sc->sc_tx_mq); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} + +static void +cdce_bulk_write_std_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct mbuf *mt; + uint32_t crc; + + DPRINTFN(1, "\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete: " + "%u bytes in %u frames\n", xfer->actlen, + xfer->aframes); + + ifp->if_opackets++; + + /* free all previous mbufs */ + cdce_free_mq(&sc->sc_tx_mq); + + case USB_ST_SETUP: +tr_setup: + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + break; + } + if (sc->sc_flags & CDCE_FLAG_ZAURUS) { + /* + * Zaurus wants a 32-bit CRC appended to + * every frame + */ + + crc = cdce_m_crc32(m, 0, m->m_pkthdr.len); + crc = htole32(crc); + + if (!m_append(m, 4, (void *)&crc)) { + m_freem(m); + ifp->if_oerrors++; + goto tr_setup; + } + } + if (m->m_len != m->m_pkthdr.len) { + mt = m_defrag(m, M_DONTWAIT); + if (mt == NULL) { + m_freem(m); + ifp->if_oerrors++; + goto tr_setup; + } + m = mt; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + _IF_ENQUEUE(&sc->sc_tx_mq, m); + + xfer->frlengths[0] = m->m_len; + usb2_set_frame_data(xfer, m->m_data, 0); + xfer->nframes = 1; + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + /* free all previous mbufs */ + cdce_free_mq(&sc->sc_tx_mq); + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} + +static void +cdce_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + + /* first call - set the correct callback */ + if (sc->sc_iface_protocol == UIPROTO_CDC_ETH_512X4) { + xfer->flags.force_short_xfer = 0; + xfer->callback = &cdce_bulk_write_512x4_callback; + } else { + xfer->callback = &cdce_bulk_write_std_callback; + } + (xfer->callback) (xfer); + return; +} + +static int32_t +#ifdef __FreeBSD__ +cdce_m_crc32_cb(void *arg, void *src, uint32_t count) +#else +cdce_m_crc32_cb(void *arg, caddr_t src, uint32_t count) +#endif +{ + register uint32_t *p_crc = arg; + + *p_crc = crc32_raw(src, count, *p_crc); + return (0); +} + +static uint32_t +cdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) +{ + register int error; + uint32_t crc = 0xFFFFFFFF; + + error = m_apply(m, src_offset, src_len, &cdce_m_crc32_cb, &crc); + return (crc ^ 0xFFFFFFFF); +} + +static void +cdce_stop(struct cdce_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(CDCE_FLAG_HL_READY | + CDCE_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static int +cdce_shutdown(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + cdce_stop(sc); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static int +cdce_suspend(device_t dev) +{ + device_printf(dev, "Suspending\n"); + return (0); +} + +static int +cdce_resume(device_t dev) +{ + device_printf(dev, "Resuming\n"); + return (0); +} + +static int +cdce_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct cdce_softc *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + cdce_init_cb(sc); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + cdce_stop(sc); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, (void *)data, + &sc->sc_ifmedia, command); + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +cdce_init_cb(void *arg) +{ + struct cdce_softc *sc = arg; + struct ifnet *ifp; + + mtx_lock(&sc->sc_mtx); + + ifp = sc->sc_ifp; + + /* immediate configuration */ + + cdce_stop(sc); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= ( + CDCE_FLAG_LL_READY | + CDCE_FLAG_HL_READY); + + usb2_transfer_set_stall(sc->sc_xfer[0]); + usb2_transfer_set_stall(sc->sc_xfer[1]); + + cdce_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +cdce_bulk_read_512x4_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + void *data_ptr; + uint32_t offset; + uint16_t x; + uint16_t y; + uint16_t z; + uint16_t rx_frags; + uint16_t flen; + uint8_t fwd_mq; + uint8_t free_mq; + + fwd_mq = 0; + free_mq = 0; + rx_frags = 0; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("received %u bytes in %u frames\n", + xfer->actlen, xfer->aframes); + + /* check state */ + if (!(sc->sc_flags & CDCE_FLAG_RX_DATA)) { + + /* verify the header */ + if ((xfer->actlen < CDCE_512X4_FRAG_LENGTH_OFFSET) || + (sc->sc_rx.hdr.bSig[0] != 'F') || + (sc->sc_rx.hdr.bSig[1] != 'L')) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + rx_frags = (xfer->actlen - + CDCE_512X4_FRAG_LENGTH_OFFSET) / 2; + if (rx_frags != 0) { + /* start receiving data */ + sc->sc_flags |= CDCE_FLAG_RX_DATA; + } + DPRINTF("doing %u fragments\n", rx_frags); + + } else { + /* we are done receiving data */ + sc->sc_flags &= ~CDCE_FLAG_RX_DATA; + fwd_mq = 1; + } + + case USB_ST_SETUP: +tr_setup: + if (xfer->flags.stall_pipe) { + /* we are done */ + sc->sc_flags &= ~CDCE_FLAG_RX_DATA; + } + /* we expect a Multi Frame Ethernet Header */ + if (!(sc->sc_flags & CDCE_FLAG_RX_DATA)) { + DPRINTF("expecting length header\n"); + usb2_set_frame_data(xfer, &sc->sc_rx.hdr, 0); + xfer->frlengths[0] = sizeof(sc->sc_rx.hdr); + xfer->nframes = 1; + xfer->flags.short_xfer_ok = 1; + usb2_start_hardware(xfer); + free_mq = 1; + break; + } + /* verify number of fragments */ + if (rx_frags > CDCE_512X4_FRAGS_MAX) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + /* check if the last fragment does not complete a frame */ + x = rx_frags - 1; + flen = UGETW(sc->sc_rx.hdr.wFragLength[x]); + if (!(flen & CDCE_512X4_FRAG_LAST_MASK)) { + DPRINTF("no last frag mask\n"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + /* + * Setup a new USB transfer chain to receive all the + * IP-frame fragments, automagically defragged : + */ + x = 0; + y = 0; + while (1) { + + z = x; + offset = 0; + + /* precompute the frame length */ + while (1) { + flen = UGETW(sc->sc_rx.hdr.wFragLength[z]); + offset += (flen & CDCE_512X4_FRAG_LENGTH_MASK); + if (flen & CDCE_512X4_FRAG_LAST_MASK) { + break; + } + z++; + } + + if (offset >= sizeof(struct ether_header)) { + /* + * allocate a suitable memory buffer, if + * possible + */ + if (offset > (MCLBYTES - ETHER_ALIGN)) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } if (offset > (MHLEN - ETHER_ALIGN)) { + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + } else { + m = m_gethdr(M_DONTWAIT, MT_DATA); + } + } else { + m = NULL; /* dump it */ + } + + DPRINTFN(17, "frame %u, length = %u \n", y, offset); + + /* check if we have a buffer */ + if (m) { + m->m_data = USB_ADD_BYTES(m->m_data, ETHER_ALIGN); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = offset; + + /* enqueue */ + _IF_ENQUEUE(&sc->sc_rx_mq, m); + + data_ptr = m->m_data; + ifp->if_ipackets++; + } else { + data_ptr = sc->sc_rx.data; + ifp->if_ierrors++; + } + + /* setup the RX chain */ + offset = 0; + while (1) { + + flen = UGETW(sc->sc_rx.hdr.wFragLength[x]); + + usb2_set_frame_data(xfer, + USB_ADD_BYTES(data_ptr, offset), x); + + xfer->frlengths[x] = + (flen & CDCE_512X4_FRAG_LENGTH_MASK); + + DPRINTFN(17, "length[%u] = %u\n", + x, xfer->frlengths[x]); + + offset += xfer->frlengths[x]; + + x++; + + if (flen & CDCE_512X4_FRAG_LAST_MASK) { + break; + } + } + + y++; + + if (x == rx_frags) { + break; + } + if (y == CDCE_512X4_FRAMES_MAX) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + } + + DPRINTF("nframes = %u\n", x); + + xfer->nframes = x; + xfer->flags.short_xfer_ok = 0; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTF("error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + free_mq = 1; + break; + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! + * + * + * By safe we mean that if "usb2_transfer_stop()" is called, + * we will get a callback having the error code + * USB_ERR_CANCELLED. + */ + if (fwd_mq) { + cdce_fwd_mq(sc, &sc->sc_rx_mq); + } + if (free_mq) { + cdce_free_mq(&sc->sc_rx_mq); + } + return; +} + +static void +cdce_bulk_read_std_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct mbuf *m_rx = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("received %u bytes in %u frames\n", + xfer->actlen, xfer->aframes); + + if (sc->sc_flags & CDCE_FLAG_ZAURUS) { + + /* Strip off CRC added by Zaurus */ + if (xfer->frlengths[0] >= MAX(4, 14)) { + xfer->frlengths[0] -= 4; + } + } + _IF_DEQUEUE(&sc->sc_rx_mq, m); + + if (m) { + + if (xfer->frlengths[0] < sizeof(struct ether_header)) { + m_freem(m); + goto tr_setup; + } + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->frlengths[0]; + m_rx = m; + } + case USB_ST_SETUP: +tr_setup: + m = usb2_ether_get_mbuf(); + if (m == NULL) { + + /* + * We are out of mbufs and need to dump all the + * received data ! + */ + usb2_set_frame_data(xfer, &sc->sc_rx.data, 0); + xfer->frlengths[0] = sizeof(sc->sc_rx.data); + + } else { + usb2_set_frame_data(xfer, m->m_data, 0); + xfer->frlengths[0] = m->m_len; + _IF_ENQUEUE(&sc->sc_rx_mq, m); + } + xfer->nframes = 1; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTF("error = %s\n", + usb2_errstr(xfer->error)); + + /* free all mbufs */ + cdce_free_mq(&sc->sc_rx_mq); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + * + * By safe we mean that if "usb2_transfer_stop()" is called, + * we will get a callback having the error code + * USB_ERR_CANCELLED. + */ + if (m_rx) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m_rx); + mtx_lock(&sc->sc_mtx); + } + return; +} + +static void +cdce_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + + /* first call - set the correct callback */ + if (sc->sc_iface_protocol == UIPROTO_CDC_ETH_512X4) { + xfer->callback = &cdce_bulk_read_512x4_callback; + } else { + xfer->callback = &cdce_bulk_read_std_callback; + } + (xfer->callback) (xfer); + return; +} + +static int +cdce_ifmedia_upd_cb(struct ifnet *ifp) +{ + /* no-op, cdce has only 1 possible media type */ + return (0); +} + +static void +cdce_ifmedia_sts_cb(struct ifnet *const ifp, struct ifmediareq *req) +{ + + req->ifm_status = IFM_AVALID | IFM_ACTIVE; + req->ifm_active = IFM_ETHER | IFM_10_T; +} + +static void +cdce_intr_read_callback(struct usb2_xfer *xfer) +{ + ; /* style fix */ + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("Received %d bytes\n", + xfer->actlen); + + /* TODO: decode some indications */ + + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} + +static void +cdce_intr_write_callback(struct usb2_xfer *xfer) +{ + ; /* style fix */ + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("Transferred %d bytes\n", xfer->actlen); + + case USB_ST_SETUP: +tr_setup: +#if 0 + xfer->frlengths[0] = XXX; + usb2_start_hardware(xfer); +#endif + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} + +static int +cdce_handle_request(device_t dev, + const void *req, void **pptr, uint16_t *plen, + uint16_t offset, uint8_t is_complete) +{ + return (ENXIO); /* use builtin handler */ +} diff --git a/sys/dev/usb2/ethernet/if_cdce2_reg.h b/sys/dev/usb2/ethernet/if_cdce2_reg.h new file mode 100644 index 000000000000..3b5f45da101c --- /dev/null +++ b/sys/dev/usb2/ethernet/if_cdce2_reg.h @@ -0,0 +1,87 @@ +/*- + * 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_ + +#define CDCE_N_TRANSFER 3 /* units */ +#define CDCE_IND_SIZE_MAX 32 /* bytes */ +#define CDCE_512X4_IFQ_MAXLEN MAX((2*CDCE_512X4_FRAMES_MAX), IFQ_MAXLEN) + +union cdce_eth_rx { /* multiframe header */ + struct usb2_cdc_mf_eth_512x4_header hdr; + uint8_t data[MCLBYTES]; +} __aligned(USB_HOST_ALIGN); + +union cdce_eth_tx { /* multiframe header */ + struct usb2_cdc_mf_eth_512x4_header hdr; +} __aligned(USB_HOST_ALIGN); + +struct cdce_mq { /* mini-queue */ + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + uint16_t ifq_len; +}; + +struct cdce_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + union cdce_eth_tx sc_tx; + union cdce_eth_rx sc_rx; + struct ifmedia sc_ifmedia; + struct mtx sc_mtx; + struct cdce_mq sc_rx_mq; + struct cdce_mq sc_tx_mq; + + struct ifnet *sc_ifp; + struct usb2_xfer *sc_xfer[CDCE_N_TRANSFER]; + struct usb2_device *sc_udev; + device_t sc_dev; + + uint32_t sc_unit; + + uint16_t sc_flags; +#define CDCE_FLAG_ZAURUS 0x0001 +#define CDCE_FLAG_NO_UNION 0x0002 +#define CDCE_FLAG_LL_READY 0x0004 +#define CDCE_FLAG_HL_READY 0x0008 +#define CDCE_FLAG_RX_DATA 0x0010 + + uint8_t sc_name[16]; + uint8_t sc_data_iface_no; + uint8_t sc_ifaces_index[2]; + uint8_t sc_iface_protocol; +}; + +#endif /* _USB_IF_CDCEREG_H_ */ diff --git a/sys/dev/usb2/ethernet/if_cue2.c b/sys/dev/usb2/ethernet/if_cue2.c new file mode 100644 index 000000000000..e0e0726af405 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_cue2.c @@ -0,0 +1,965 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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 +__FBSDID("$FreeBSD$"); + +/* + * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate + * adapters and others. + * + * Written by Bill Paul + * 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. + */ + +/* + * NOTE: all function names beginning like "cue_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc cue_softc + +#define USB_DEBUG_VAR cue_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Various supported device vendors/products. + */ + +/* Belkin F5U111 adapter covered by NETMATE entry */ + +static const struct usb2_device_id cue_devs[] = { + {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE, 0)}, + {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2, 0)}, + {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK, 0)}, +}; + +/* prototypes */ + +static device_probe_t cue_probe; +static device_attach_t cue_attach; +static device_detach_t cue_detach; +static device_shutdown_t cue_shutdown; + +static usb2_callback_t cue_bulk_read_clear_stall_callback; +static usb2_callback_t cue_bulk_read_callback; +static usb2_callback_t cue_bulk_write_clear_stall_callback; +static usb2_callback_t cue_bulk_write_callback; + +static usb2_config_td_command_t cue_cfg_promisc_upd; +static usb2_config_td_command_t cue_config_copy; +static usb2_config_td_command_t cue_cfg_first_time_setup; +static usb2_config_td_command_t cue_cfg_tick; +static usb2_config_td_command_t cue_cfg_pre_init; +static usb2_config_td_command_t cue_cfg_init; +static usb2_config_td_command_t cue_cfg_pre_stop; +static usb2_config_td_command_t cue_cfg_stop; + +static void cue_cfg_do_request(struct cue_softc *sc, struct usb2_device_request *req, void *data); +static uint8_t cue_cfg_csr_read_1(struct cue_softc *sc, uint16_t reg); +static uint16_t cue_cfg_csr_read_2(struct cue_softc *sc, uint8_t reg); +static void cue_cfg_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val); +static void cue_cfg_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, uint16_t len); +static void cue_cfg_getmac(struct cue_softc *sc, void *buf); +static void cue_mchash(struct usb2_config_td_cc *cc, const uint8_t *addr); +static void cue_cfg_reset(struct cue_softc *sc); +static void cue_start_cb(struct ifnet *ifp); +static void cue_start_transfers(struct cue_softc *sc); +static void cue_init_cb(void *arg); +static int cue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void cue_watchdog(void *arg); + +#if USB_DEBUG +static int cue_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue"); +SYSCTL_INT(_hw_usb2_cue, OID_AUTO, debug, CTLFLAG_RW, &cue_debug, 0, + "Debug level"); +#endif + +static const struct usb2_config cue_config[CUE_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (MCLBYTES + 2), + .mh.flags = {.pipe_bof = 1,}, + .mh.callback = &cue_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + 2), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &cue_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &cue_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &cue_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t cue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cue_probe), + DEVMETHOD(device_attach, cue_attach), + DEVMETHOD(device_detach, cue_detach), + DEVMETHOD(device_shutdown, cue_shutdown), + + {0, 0} +}; + +static driver_t cue_driver = { + .name = "cue", + .methods = cue_methods, + .size = sizeof(struct cue_softc), +}; + +static devclass_t cue_devclass; + +DRIVER_MODULE(cue, ushub, cue_driver, cue_devclass, NULL, 0); +MODULE_DEPEND(cue, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(cue, usb2_core, 1, 1, 1); +MODULE_DEPEND(cue, ether, 1, 1, 1); + +static void +cue_cfg_do_request(struct cue_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +#define CUE_CFG_SETBIT(sc, reg, x) \ + cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) | (x)) + +#define CUE_CFG_CLRBIT(sc, reg, x) \ + cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +cue_cfg_csr_read_1(struct cue_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = CUE_CMD_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + cue_cfg_do_request(sc, &req, &val); + return (val); +} + +static uint16_t +cue_cfg_csr_read_2(struct cue_softc *sc, uint8_t reg) +{ + struct usb2_device_request req; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = CUE_CMD_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + cue_cfg_do_request(sc, &req, &val); + return (le16toh(val)); +} + +static void +cue_cfg_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = CUE_CMD_WRITEREG; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + cue_cfg_do_request(sc, &req, NULL); + return; +} + +static void +cue_cfg_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, + void *buf, uint16_t len) +{ + struct usb2_device_request req; + + 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); + + cue_cfg_do_request(sc, &req, buf); + return; +} + +static void +cue_cfg_getmac(struct cue_softc *sc, void *buf) +{ + struct usb2_device_request req; + + 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); + + cue_cfg_do_request(sc, &req, buf); + return; +} + +#define CUE_BITS 9 + +static void +cue_mchash(struct usb2_config_td_cc *cc, const uint8_t *addr) +{ + uint16_t h; + + h = ether_crc32_le(addr, ETHER_ADDR_LEN) & + ((1 << CUE_BITS) - 1); + cc->if_hash[h >> 3] |= 1 << (h & 0x7); + return; +} + +static void +cue_cfg_promisc_upd(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* if we want promiscuous mode, set the allframes bit */ + + if (cc->if_flags & IFF_PROMISC) { + CUE_CFG_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + } else { + CUE_CFG_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + } + + /* write multicast hash-bits */ + + cue_cfg_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, + cc->if_hash, CUE_MCAST_TABLE_LEN); + return; +} + +static void +cue_config_copy(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + usb2_ether_cc(sc->sc_ifp, &cue_mchash, cc); + return; +} + +static void +cue_cfg_reset(struct cue_softc *sc) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = CUE_CMD_RESET; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + cue_cfg_do_request(sc, &req, NULL); + + /* + * wait a little while for the chip to get its brains in order: + */ + + (void)usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + return; +} + +static int +cue_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != CUE_CONFIG_IDX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != CUE_IFACE_IDX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa)); +} + +static int +cue_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct cue_softc *sc = device_get_softc(dev); + uint8_t iface_index; + int32_t error; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "cue lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = CUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, cue_config, CUE_ENDPT_MAX, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &cue_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + cue_watchdog(sc); + + return (0); /* success */ + +detach: + cue_detach(dev); + return (ENXIO); /* failure */ +} + +static void +cue_cfg_first_time_setup(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + struct ifnet *ifp; + +#if 0 + /* Reset the adapter. */ + cue_cfg_reset(sc); +#endif + /* + * Get station address. + */ + cue_cfg_getmac(sc, eaddr); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("cue%d: could not if_alloc()\n", + sc->sc_unit); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "cue", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = cue_ioctl_cb; + ifp->if_start = cue_start_cb; + ifp->if_watchdog = NULL; + ifp->if_init = cue_init_cb; + ifp->if_baudrate = 10000000; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + ether_ifattach(ifp, eaddr); + + mtx_lock(&sc->sc_mtx); + +done: + return; +} + +static int +cue_detach(device_t dev) +{ + struct cue_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + cue_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, CUE_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~CUE_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +cue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m = NULL; + uint8_t buf[2]; + uint16_t len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen <= (2 + sizeof(struct ether_header))) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 2); + + len = buf[0] | (buf[1] << 8); + + xfer->actlen -= 2; + + m = usb2_ether_get_mbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen = min(xfer->actlen, m->m_len); + xfer->actlen = min(xfer->actlen, len); + + usb2_copy_out(xfer->frbuffers, 2, m->m_data, xfer->actlen); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->actlen; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & CUE_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m); + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= CUE_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +cue_cfg_tick(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if ((ifp == NULL)) { + /* not ready */ + return; + } + ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_SINGLECOLL); + ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_MULTICOLL); + ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_EXCESSCOLL); + + if (cue_cfg_csr_read_2(sc, CUE_RX_FRAMEERR)) { + ifp->if_ierrors++; + } + /* start stopped transfers, if any */ + + cue_start_transfers(sc); + + return; +} + +static void +cue_start_cb(struct ifnet *ifp) +{ + struct cue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + cue_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +cue_start_transfers(struct cue_softc *sc) +{ + if ((sc->sc_flags & CUE_FLAG_LL_READY) && + (sc->sc_flags & CUE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +cue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~CUE_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +cue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & CUE_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + xfer->frlengths[0] = (m->m_pkthdr.len + 2); + + /* the first two bytes are the frame length */ + + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + usb2_copy_in(xfer->frbuffers, 0, buf, 2); + + usb2_m_copy_in(xfer->frbuffers, 2, + m, 0, m->m_pkthdr.len); + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= CUE_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +static void +cue_init_cb(void *arg) +{ + struct cue_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_cfg_pre_init, + &cue_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +cue_cfg_pre_init(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + cue_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= CUE_FLAG_HL_READY; + + return; +} + +static void +cue_cfg_init(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint8_t i; + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + cue_cfg_stop(sc, cc, 0); +#if 0 + cue_cfg_reset(sc); +#endif + /* Set MAC address */ + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + cue_cfg_csr_write_1(sc, CUE_PAR0 - i, cc->if_lladdr[i]); + } + + /* Enable RX logic. */ + cue_cfg_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); + + /* Load the multicast filter */ + cue_cfg_promisc_upd(sc, cc, 0); + + /* + * Set the number of RX and TX buffers that we want + * to reserve inside the ASIC. + */ + cue_cfg_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); + cue_cfg_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); + + /* Set advanced operation modes. */ + cue_cfg_csr_write_1(sc, CUE_ADVANCED_OPMODES, + CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ + + /* Program the LED operation. */ + cue_cfg_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); + + sc->sc_flags |= (CUE_FLAG_READ_STALL | + CUE_FLAG_WRITE_STALL | + CUE_FLAG_LL_READY); + + cue_start_transfers(sc); + return; +} + +static int +cue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct cue_softc *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_config_copy, + &cue_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_cfg_pre_init, + &cue_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_cfg_pre_stop, + &cue_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_config_copy, + &cue_cfg_promisc_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +cue_watchdog(void *arg) +{ + struct cue_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &cue_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &cue_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +cue_cfg_pre_stop(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + cue_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(CUE_FLAG_HL_READY | + CUE_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + return; +} + +static void +cue_cfg_stop(struct cue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + cue_cfg_csr_write_1(sc, CUE_ETHCTL, 0); + cue_cfg_reset(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 = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &cue_cfg_pre_stop, + &cue_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_cue2_reg.h b/sys/dev/usb2/ethernet/if_cue2_reg.h new file mode 100644 index 000000000000..55178766d81c --- /dev/null +++ b/sys/dev/usb2/ethernet/if_cue2_reg.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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_IDX 0 /* config number 1 */ +#define CUE_IFACE_IDX 0 + +/* The interrupt endpoint is currently unused by the KLSI part. */ +#define CUE_ENDPT_MAX 4 + +struct cue_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[CUE_ENDPT_MAX]; + + uint32_t sc_unit; + + uint16_t sc_flags; +#define CUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ +#define CUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ +#define CUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ +#define CUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ +#define CUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ +}; diff --git a/sys/dev/usb2/ethernet/if_kue2.c b/sys/dev/usb2/ethernet/if_kue2.c new file mode 100644 index 000000000000..88bbd7e6b013 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_kue2.c @@ -0,0 +1,1017 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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 +__FBSDID("$FreeBSD$"); + +/* + * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. + * + * Written by Bill Paul + * 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. + */ + +/* + * NOTE: all function names beginning like "kue_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc kue_softc + +#define USB_DEBUG_VAR kue_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Various supported device vendors/products. + */ +static const struct usb2_device_id kue_devs[] = { + {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250, 0)}, + {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450, 0)}, + {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, 0)}, + {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX, 0)}, + {USB_VPI(USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101, 0)}, + {USB_VPI(USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA, 0)}, + {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C, 0)}, + {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T, 0)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T, 0)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C, 0)}, + {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45, 0)}, + {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1, 0)}, + {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2, 0)}, + {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT, 0)}, + {USB_VPI(USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA, 0)}, + {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1, 0)}, + {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101, 0)}, + {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT, 0)}, + {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN, 0)}, + {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, 0)}, + {USB_VPI(USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA, 0)}, + {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101, 0)}, + {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X, 0)}, + {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET, 0)}, + {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2, 0)}, + {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3, 0)}, + {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8, 0)}, + {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9, 0)}, + {USB_VPI(USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA, 0)}, + {USB_VPI(USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA, 0)}, + {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE, 0)}, + {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E, 0)}, + {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB, 0)}, +}; + +/* prototypes */ + +static device_probe_t kue_probe; +static device_attach_t kue_attach; +static device_detach_t kue_detach; +static device_shutdown_t kue_shutdown; + +static usb2_callback_t kue_bulk_read_clear_stall_callback; +static usb2_callback_t kue_bulk_read_callback; +static usb2_callback_t kue_bulk_write_clear_stall_callback; +static usb2_callback_t kue_bulk_write_callback; + +static usb2_config_td_command_t kue_cfg_promisc_upd; +static usb2_config_td_command_t kue_config_copy; +static usb2_config_td_command_t kue_cfg_first_time_setup; +static usb2_config_td_command_t kue_cfg_pre_init; +static usb2_config_td_command_t kue_cfg_init; +static usb2_config_td_command_t kue_cfg_tick; +static usb2_config_td_command_t kue_cfg_pre_stop; +static usb2_config_td_command_t kue_cfg_stop; + +static void kue_cfg_do_request(struct kue_softc *sc, struct usb2_device_request *req, void *data); +static void kue_cfg_setword(struct kue_softc *sc, uint8_t breq, uint16_t word); +static void kue_cfg_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, uint16_t val, void *data, uint16_t len); +static void kue_cfg_load_fw(struct kue_softc *sc); +static void kue_cfg_reset(struct kue_softc *sc); +static void kue_start_cb(struct ifnet *ifp); +static void kue_start_transfers(struct kue_softc *sc); +static void kue_init_cb(void *arg); +static int kue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void kue_watchdog(void *arg); + +#if USB_DEBUG +static int kue_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue"); +SYSCTL_INT(_hw_usb2_kue, OID_AUTO, debug, CTLFLAG_RW, &kue_debug, 0, + "Debug level"); +#endif + +static const struct usb2_config kue_config[KUE_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (MCLBYTES + 2 + 64), + .mh.flags = {.pipe_bof = 1,}, + .mh.callback = &kue_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + 2), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &kue_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &kue_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &kue_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t kue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, kue_probe), + DEVMETHOD(device_attach, kue_attach), + DEVMETHOD(device_detach, kue_detach), + DEVMETHOD(device_shutdown, kue_shutdown), + + {0, 0} +}; + +static driver_t kue_driver = { + .name = "kue", + .methods = kue_methods, + .size = sizeof(struct kue_softc), +}; + +static devclass_t kue_devclass; + +DRIVER_MODULE(kue, ushub, kue_driver, kue_devclass, NULL, 0); +MODULE_DEPEND(kue, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(kue, usb2_core, 1, 1, 1); +MODULE_DEPEND(kue, ether, 1, 1, 1); + +/* + * 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 void +kue_cfg_do_request(struct kue_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 60000); + + if (err) { + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +kue_cfg_setword(struct kue_softc *sc, uint8_t breq, uint16_t word) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = breq; + USETW(req.wValue, word); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + kue_cfg_do_request(sc, &req, NULL); + return; +} + +static void +kue_cfg_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, + uint16_t val, void *data, uint16_t len) +{ + struct usb2_device_request req; + + 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); + + kue_cfg_do_request(sc, &req, data); + return; +} + +static void +kue_cfg_load_fw(struct kue_softc *sc) +{ + struct usb2_device_descriptor *dd; + uint16_t hwrev; + + dd = usb2_get_device_descriptor(sc->sc_udev); + 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; + } + /* load code segment */ + kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_code_seg, sizeof(kue_code_seg)); + + /* load fixup segment */ + kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_fix_seg, sizeof(kue_fix_seg)); + + /* send trigger command */ + kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_trig_seg, sizeof(kue_trig_seg)); + return; +} + +static void +kue_cfg_promisc_upd(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, + cc->if_nhash, cc->if_hash, cc->if_nhash * ETHER_ADDR_LEN); + + kue_cfg_setword(sc, KUE_CMD_SET_PKT_FILTER, cc->if_rxfilt); + + return; +} + +static void +kue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) +{ + uint8_t i; + + i = cc->if_nhash; + + /* + * If there are too many addresses for the internal filter, + * switch over to allmulti mode. + */ + if (i == cc->if_mhash) { + cc->if_rxfilt |= KUE_RXFILT_ALLMULTI; + } else { + bcopy(ptr, cc->if_hash + (i * ETHER_ADDR_LEN), ETHER_ADDR_LEN); + cc->if_nhash++; + } + return; +} + +static void +kue_config_copy(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + + cc->if_mhash = sc->sc_mcfilt_max; + cc->if_rxfilt = (KUE_RXFILT_UNICAST | KUE_RXFILT_BROADCAST); + + usb2_ether_cc(sc->sc_ifp, &kue_mchash, cc); + + /* + * If we want promiscuous mode, set the all-frames bit: + */ + if (cc->if_flags & IFF_PROMISC) { + cc->if_rxfilt |= KUE_RXFILT_PROMISC; + } + if ((cc->if_flags & IFF_ALLMULTI) || + (cc->if_flags & IFF_PROMISC)) { + cc->if_rxfilt |= KUE_RXFILT_ALLMULTI; + } else if (cc->if_nhash) { + cc->if_rxfilt |= KUE_RXFILT_MULTICAST; + } + 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_cfg_reset(struct kue_softc *sc) +{ + struct usb2_config_descriptor *cd; + usb2_error_t err; + + cd = usb2_get_config_descriptor(sc->sc_udev); + + err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) { + DPRINTF("reset failed (ignored)\n"); + } + /* + * wait a little while for the chip to get its brains in order: + */ + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + return; +} + +/* + * Probe for a KLSI chip. + */ +static int +kue_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != KUE_CONFIG_IDX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != KUE_IFACE_IDX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(kue_devs, sizeof(kue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do + * setup and ethernet/BPF attach. + */ +static int +kue_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct kue_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "kue lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = KUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, kue_config, KUE_ENDPT_MAX, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &kue_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + kue_watchdog(sc); + + return (0); /* success */ + +detach: + kue_detach(dev); + return (ENXIO); /* failure */ +} + +static void +kue_cfg_first_time_setup(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + + /* load the firmware into the NIC */ + + kue_cfg_load_fw(sc); + + /* reset the adapter */ + + kue_cfg_reset(sc); + + /* read ethernet descriptor */ + kue_cfg_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, + 0, &sc->sc_desc, sizeof(sc->sc_desc)); + + sc->sc_mcfilt_max = KUE_MCFILTCNT(sc); + if (sc->sc_mcfilt_max > KUE_MCFILT_MAX) { + sc->sc_mcfilt_max = KUE_MCFILT_MAX; + } + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("kue%d: could not if_alloc()\n", + sc->sc_unit); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "kue", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = kue_ioctl_cb; + ifp->if_start = kue_start_cb; + ifp->if_watchdog = NULL; + ifp->if_init = kue_init_cb; + ifp->if_baudrate = 10000000; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + ether_ifattach(ifp, sc->sc_desc.kue_macaddr); + + mtx_lock(&sc->sc_mtx); +done: + return; +} + +static int +kue_detach(device_t dev) +{ + struct kue_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + kue_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, KUE_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +kue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~KUE_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +kue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m = NULL; + uint8_t buf[2]; + uint16_t len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen <= (2 + sizeof(struct ether_header))) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 2); + + len = buf[0] | (buf[1] << 8); + + xfer->actlen -= 2; + + m = usb2_ether_get_mbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen = min(xfer->actlen, m->m_len); + xfer->actlen = min(xfer->actlen, len); + + usb2_copy_out(xfer->frbuffers, 2, m->m_data, xfer->actlen); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->actlen; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & KUE_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m); + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= KUE_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +kue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~KUE_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +kue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint32_t total_len; + uint32_t temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & KUE_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + temp_len = (m->m_pkthdr.len + 2); + total_len = (temp_len + (64 - (temp_len % 64))); + + /* the first two bytes are the frame length */ + + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + usb2_copy_in(xfer->frbuffers, 0, buf, 2); + + usb2_m_copy_in(xfer->frbuffers, 2, + m, 0, m->m_pkthdr.len); + + usb2_bzero(xfer->frbuffers, temp_len, + total_len - temp_len); + + xfer->frlengths[0] = total_len; + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= KUE_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +static void +kue_start_cb(struct ifnet *ifp) +{ + struct kue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + kue_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +kue_start_transfers(struct kue_softc *sc) +{ + if ((sc->sc_flags & KUE_FLAG_LL_READY) && + (sc->sc_flags & KUE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +kue_init_cb(void *arg) +{ + struct kue_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_cfg_pre_init, + &kue_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +kue_cfg_pre_init(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + kue_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= KUE_FLAG_HL_READY; + + return; +} + +static void +kue_cfg_init(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* set MAC address */ + kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, + 0, cc->if_lladdr, ETHER_ADDR_LEN); + + /* I'm not sure how to tune these. */ +#if 0 + /* + * Leave this one alone for now; setting it + * wrong causes lockups on some machines/controllers. + */ + kue_cfg_setword(sc, KUE_CMD_SET_SOFS, 1); +#endif + kue_cfg_setword(sc, KUE_CMD_SET_URB_SIZE, 64); + + /* load the multicast filter */ + kue_cfg_promisc_upd(sc, cc, 0); + + sc->sc_flags |= (KUE_FLAG_READ_STALL | + KUE_FLAG_WRITE_STALL | + KUE_FLAG_LL_READY); + + kue_start_transfers(sc); + return; +} + +static int +kue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct kue_softc *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_config_copy, + &kue_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_cfg_pre_init, + &kue_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_cfg_pre_stop, + &kue_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_config_copy, + &kue_cfg_promisc_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +kue_cfg_tick(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if ((ifp == NULL)) { + /* not ready */ + return; + } + /* start stopped transfers, if any */ + + kue_start_transfers(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +kue_watchdog(void *arg) +{ + struct kue_softc *sc = arg; + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &kue_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &kue_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +static void +kue_cfg_pre_stop(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + kue_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(KUE_FLAG_HL_READY | + KUE_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + return; +} + +static void +kue_cfg_stop(struct kue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + 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 = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &kue_cfg_pre_stop, + &kue_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_kue2_fw.h b/sys/dev/usb2/ethernet/if_kue2_fw.h new file mode 100644 index 000000000000..2b055a92ed7b --- /dev/null +++ b/sys/dev/usb2/ethernet/if_kue2_fw.h @@ -0,0 +1,685 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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/dev/usb2/ethernet/if_kue2_reg.h b/sys/dev/usb2/ethernet/if_kue2_reg.h new file mode 100644 index 000000000000..989d1254d09b --- /dev/null +++ b/sys/dev/usb2/ethernet/if_kue2_reg.h @@ -0,0 +1,142 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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 { + uint8_t kue_len; + uint8_t kue_rsvd0; + uint8_t kue_rsvd1; + uint8_t kue_macaddr[ETHER_ADDR_LEN]; + uint8_t kue_etherstats[4]; + uint8_t kue_maxseg[2]; + uint8_t kue_mcastfilt[2]; + uint8_t kue_rsvd2; +} __packed; + +#define KUE_ETHERSTATS(x) UGETDW((x)->sc_desc.kue_etherstats) +#define KUE_MAXSEG(x) UGETW((x)->sc_desc.kue_maxseg) +#define KUE_MCFILTCNT(x) (UGETW((x)->sc_desc.kue_mcastfilt) & 0x7FFF) +#define KUE_MCFILT_MAX (USB_ETHER_HASH_MAX / 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_IDX 0 /* config number 1 */ +#define KUE_IFACE_IDX 0 + +/* The interrupt endpoint is currently unused by the KLSI part. */ +#define KUE_ENDPT_MAX 4 + +struct kue_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + struct kue_ether_desc sc_desc; + + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[KUE_ENDPT_MAX]; + + uint32_t sc_unit; + + uint16_t sc_mcfilt_max; + uint16_t sc_flags; +#define KUE_FLAG_READ_STALL 0x0010 /* wait for clearing of stall */ +#define KUE_FLAG_WRITE_STALL 0x0020 /* wait for clearing of stall */ +#define KUE_FLAG_LL_READY 0x0040 /* Lower Layer Ready */ +#define KUE_FLAG_HL_READY 0x0080 /* Higher Layer Ready */ +#define KUE_FLAG_INTR_STALL 0x0100 /* wait for clearing of stall */ +}; diff --git a/sys/dev/usb2/ethernet/if_rue2.c b/sys/dev/usb2/ethernet/if_rue2.c new file mode 100644 index 000000000000..72c8852ff9f2 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_rue2.c @@ -0,0 +1,1400 @@ +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama . + * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul . + * 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 . 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 +__FBSDID("$FreeBSD$"); + +/* + * RealTek RTL8150 USB to fast ethernet controller driver. + * Datasheet is available from + * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. + */ + +/* + * NOTE: all function names beginning like "rue_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc rue_softc + +#define USB_DEBUG_VAR rue_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if USB_DEBUG +static int rue_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); +SYSCTL_INT(_hw_usb2_rue, OID_AUTO, debug, CTLFLAG_RW, + &rue_debug, 0, "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ + +static const struct usb2_device_id rue_devs[] = { + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)}, + {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)}, +}; + +/* prototypes */ + +static device_probe_t rue_probe; +static device_attach_t rue_attach; +static device_detach_t rue_detach; +static device_shutdown_t rue_shutdown; + +static usb2_callback_t rue_intr_clear_stall_callback; +static usb2_callback_t rue_intr_callback; +static usb2_callback_t rue_bulk_read_clear_stall_callback; +static usb2_callback_t rue_bulk_read_callback; +static usb2_callback_t rue_bulk_write_clear_stall_callback; +static usb2_callback_t rue_bulk_write_callback; + +static usb2_config_td_command_t rue_config_copy; +static usb2_config_td_command_t rue_cfg_promisc_upd; +static usb2_config_td_command_t rue_cfg_first_time_setup; +static usb2_config_td_command_t rue_cfg_tick; +static usb2_config_td_command_t rue_cfg_pre_init; +static usb2_config_td_command_t rue_cfg_init; +static usb2_config_td_command_t rue_cfg_ifmedia_upd; +static usb2_config_td_command_t rue_cfg_pre_stop; +static usb2_config_td_command_t rue_cfg_stop; + +static void rue_cfg_do_request(struct rue_softc *sc, struct usb2_device_request *req, void *data); +static void rue_cfg_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len); +static void rue_cfg_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len); +static uint8_t rue_cfg_csr_read_1(struct rue_softc *sc, uint16_t reg); +static uint16_t rue_cfg_csr_read_2(struct rue_softc *sc, uint16_t reg); +static void rue_cfg_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val); +static void rue_cfg_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val); +static void rue_cfg_csr_write_4(struct rue_softc *sc, int reg, uint32_t val); + +static miibus_readreg_t rue_cfg_miibus_readreg; +static miibus_writereg_t rue_cfg_miibus_writereg; +static miibus_statchg_t rue_cfg_miibus_statchg; + +static void rue_cfg_reset(struct rue_softc *sc); +static void rue_start_cb(struct ifnet *ifp); +static void rue_start_transfers(struct rue_softc *sc); +static void rue_init_cb(void *arg); +static int rue_ifmedia_upd_cb(struct ifnet *ifp); +static void rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); +static int rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void rue_watchdog(void *arg); + +static const struct usb2_config rue_config[RUE_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = MCLBYTES, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &rue_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + 4), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &rue_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &rue_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &rue_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &rue_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &rue_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t rue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rue_probe), + 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_cfg_miibus_readreg), + DEVMETHOD(miibus_writereg, rue_cfg_miibus_writereg), + DEVMETHOD(miibus_statchg, rue_cfg_miibus_statchg), + + {0, 0} +}; + +static driver_t rue_driver = { + .name = "rue", + .methods = rue_methods, + .size = sizeof(struct rue_softc), +}; + +static devclass_t rue_devclass; + +DRIVER_MODULE(rue, ushub, rue_driver, rue_devclass, NULL, 0); +DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(rue, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(rue, usb2_core, 1, 1, 1); +MODULE_DEPEND(rue, ether, 1, 1, 1); +MODULE_DEPEND(rue, miibus, 1, 1, 1); + +static void +rue_cfg_do_request(struct rue_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +#define RUE_CFG_SETBIT(sc, reg, x) \ + rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) | (x)) + +#define RUE_CFG_CLRBIT(sc, reg, x) \ + rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) & ~(x)) + +static void +rue_cfg_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + rue_cfg_do_request(sc, &req, buf); + return; +} + +static void +rue_cfg_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + rue_cfg_do_request(sc, &req, buf); + return; +} + +static uint8_t +rue_cfg_csr_read_1(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val; + + rue_cfg_read_mem(sc, reg, &val, 1); + return (val); +} + +static uint16_t +rue_cfg_csr_read_2(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val[2]; + + rue_cfg_read_mem(sc, reg, &val, 2); + return (UGETW(val)); +} + +static void +rue_cfg_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) +{ + rue_cfg_write_mem(sc, reg, &val, 1); + return; +} + +static void +rue_cfg_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) +{ + uint8_t temp[2]; + + USETW(temp, val); + rue_cfg_write_mem(sc, reg, &temp, 2); + return; +} + +static void +rue_cfg_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) +{ + uint8_t temp[4]; + + USETDW(temp, val); + rue_cfg_write_mem(sc, reg, &temp, 4); + return; +} + +static int +rue_cfg_miibus_readreg(device_t dev, int phy, int reg) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t rval; + uint16_t ruereg; + uint8_t do_unlock; + + if (phy != 0) { /* RTL8150 supports PHY == 0, only */ + return (0); + } + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + 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: + rval = 0; + goto done; + default: + if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { + rval = rue_cfg_csr_read_1(sc, reg); + goto done; + } + printf("rue%d: bad phy register\n", sc->sc_unit); + rval = 0; + goto done; + } + + rval = rue_cfg_csr_read_2(sc, ruereg); +done: + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (rval); +} + +static int +rue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t ruereg; + uint8_t do_unlock; + + if (phy != 0) { /* RTL8150 supports PHY == 0, only */ + return (0); + } + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + 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: + goto done; + default: + if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { + rue_cfg_csr_write_1(sc, reg, data); + goto done; + } + printf("%s: bad phy register\n", + sc->sc_name); + goto done; + } + rue_cfg_csr_write_2(sc, ruereg, data); +done: + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +static void +rue_cfg_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); + uint16_t bmcr; + uint8_t do_unlock; + + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + RUE_CFG_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + bmcr = rue_cfg_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_cfg_csr_write_2(sc, RUE_BMCR, bmcr); + + RUE_CFG_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } +#endif + return; +} + +static void +rue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) +{ + uint8_t h; + + h = ether_crc32_be(ptr, ETHER_ADDR_LEN) >> 26; + cc->if_hash[h / 8] |= 1 << (h & 7); + cc->if_nhash = 1; + return; +} + +static void +rue_config_copy(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + usb2_ether_cc(sc->sc_ifp, &rue_mchash, cc); + return; +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +rue_cfg_promisc_upd(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t rxcfg; + + rxcfg = rue_cfg_csr_read_2(sc, RUE_RCR); + + if ((cc->if_flags & IFF_ALLMULTI) || + (cc->if_flags & IFF_PROMISC)) { + rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); + rxcfg &= ~RUE_RCR_AM; + rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); + rue_cfg_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); + rue_cfg_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); + return; + } + /* first, zero all the existing hash bits */ + rue_cfg_csr_write_4(sc, RUE_MAR0, 0); + rue_cfg_csr_write_4(sc, RUE_MAR4, 0); + + if (cc->if_nhash) + rxcfg |= RUE_RCR_AM; + else + rxcfg &= ~RUE_RCR_AM; + + rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); + + rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); + rue_cfg_write_mem(sc, RUE_MAR0, cc->if_hash, 4); + rue_cfg_write_mem(sc, RUE_MAR4, cc->if_hash + 4, 4); + return; +} + +static void +rue_cfg_reset(struct rue_softc *sc) +{ + usb2_error_t err; + uint16_t to; + + rue_cfg_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); + + for (to = 0;; to++) { + + if (to < RUE_TIMEOUT) { + + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + if (err) { + break; + } + if (!(rue_cfg_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) { + break; + } + } else { + printf("%s: reset timeout!\n", + sc->sc_name); + break; + } + } + + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + return; +} + +/* + * Probe for a RTL8150 chip. + */ +static int +rue_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != RUE_CONFIG_IDX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != RUE_IFACE_IDX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +rue_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct rue_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "rue lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = RUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rue_config, RUE_ENDPT_MAX, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= RUE_FLAG_WAIT_LINK; + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &rue_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + rue_watchdog(sc); + + return (0); /* success */ + +detach: + rue_detach(dev); + return (ENXIO); /* failure */ +} + +static void +rue_cfg_first_time_setup(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + int error; + uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; + + /* reset the adapter */ + rue_cfg_reset(sc); + + /* get station address from the EEPROM */ + rue_cfg_read_mem(sc, RUE_EEPROM_IDR0, + eaddr, ETHER_ADDR_LEN); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("%s: could not if_alloc()\n", + sc->sc_name); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "rue", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = rue_ioctl_cb; + ifp->if_start = rue_start_cb; + ifp->if_watchdog = NULL; + ifp->if_init = rue_init_cb; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* + * XXX need Giant when accessing the device structures ! + */ + + mtx_unlock(&sc->sc_mtx); + + mtx_lock(&Giant); + + /* MII setup */ + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + &rue_ifmedia_upd_cb, + &rue_ifmedia_sts_cb); + mtx_unlock(&Giant); + + mtx_lock(&sc->sc_mtx); + + if (error) { + printf("%s: MII without any PHY!\n", + sc->sc_name); + if_free(ifp); + goto done; + } + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + /* + * Call MI attach routine. + */ + + ether_ifattach(ifp, eaddr); + + mtx_lock(&sc->sc_mtx); + +done: + return; +} + +static int +rue_detach(device_t dev) +{ + struct rue_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + rue_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, RUE_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +rue_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~RUE_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +rue_intr_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct rue_intrpkt pkt; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && + (xfer->actlen >= sizeof(pkt))) { + + usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt)); + + ifp->if_ierrors += pkt.rue_rxlost_cnt; + ifp->if_ierrors += pkt.rue_crcerr_cnt; + ifp->if_collisions += pkt.rue_col_cnt; + } + case USB_ST_SETUP: + if (sc->sc_flags & RUE_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= RUE_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + } +} + +static void +rue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~RUE_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +rue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + uint16_t status; + struct mbuf *m = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < 4) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, xfer->actlen - 4, + &status, sizeof(status)); + + status = le16toh(status); + + /* check recieve packet was valid or not */ + + if ((status & RUE_RXSTAT_VALID) == 0) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen -= 4; + + if (xfer->actlen < sizeof(struct ether_header)) { + ifp->if_ierrors++; + goto tr_setup; + } + m = usb2_ether_get_mbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen = min(xfer->actlen, m->m_len); + + usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->actlen; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & RUE_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m); + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= RUE_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +rue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~RUE_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +rue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint32_t temp_len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & RUE_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + if (sc->sc_flags & RUE_FLAG_WAIT_LINK) { + /* + * don't send anything if there is no link ! + */ + goto done; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + temp_len = m->m_pkthdr.len; + + usb2_m_copy_in(xfer->frbuffers, 0, + m, 0, 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 (temp_len < RUE_MIN_FRAMELEN) { + usb2_bzero(xfer->frbuffers, temp_len, + RUE_MIN_FRAMELEN - temp_len); + temp_len = RUE_MIN_FRAMELEN; + } + xfer->frlengths[0] = temp_len; + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= RUE_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +static void +rue_cfg_tick(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + mii_tick(mii); + + mii_pollstat(mii); + + if ((sc->sc_flags & RUE_FLAG_WAIT_LINK) && + (mii->mii_media_status & IFM_ACTIVE) && + (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { + sc->sc_flags &= ~RUE_FLAG_WAIT_LINK; + } + sc->sc_media_active = mii->mii_media_active; + sc->sc_media_status = mii->mii_media_status; + + /* start stopped transfers, if any */ + + rue_start_transfers(sc); + + return; +} + +static void +rue_start_cb(struct ifnet *ifp) +{ + struct rue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + rue_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +rue_start_transfers(struct rue_softc *sc) +{ + if ((sc->sc_flags & RUE_FLAG_LL_READY) && + (sc->sc_flags & RUE_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[4]); + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +rue_init_cb(void *arg) +{ + struct rue_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_cfg_pre_init, + &rue_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +rue_cfg_pre_init(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + rue_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= RUE_FLAG_HL_READY; + + return; +} + +static void +rue_cfg_init(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct mii_data *mii = GET_MII(sc); + uint16_t rxcfg; + + /* + * Cancel pending I/O + */ + + rue_cfg_stop(sc, cc, 0); + + /* set MAC address */ + + rue_cfg_write_mem(sc, RUE_IDR0, cc->if_lladdr, ETHER_ADDR_LEN); + + /* + * Set the initial TX and RX configuration. + */ + rue_cfg_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); + + rxcfg = RUE_RCR_CONFIG; + + /* Set capture broadcast bit to capture broadcast frames. */ + if (cc->if_flags & IFF_BROADCAST) + rxcfg |= RUE_RCR_AB; + else + rxcfg &= ~RUE_RCR_AB; + + rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); + + /* Load the multicast filter */ + rue_cfg_promisc_upd(sc, cc, 0); + + /* Enable RX and TX */ + rue_cfg_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); + + mii_mediachg(mii); + + sc->sc_flags |= (RUE_FLAG_READ_STALL | + RUE_FLAG_WRITE_STALL | + RUE_FLAG_LL_READY); + + rue_start_transfers(sc); + + return; +} + +/* + * Set media options. + */ +static int +rue_ifmedia_upd_cb(struct ifnet *ifp) +{ + struct rue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, + &rue_cfg_ifmedia_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static void +rue_cfg_ifmedia_upd(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + sc->sc_flags |= RUE_FLAG_WAIT_LINK; + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + mii_phy_reset(miisc); + } + } + mii_mediachg(mii); + + return; +} + +/* + * Report current media status. + */ +static void +rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct rue_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + ifmr->ifm_active = sc->sc_media_active; + ifmr->ifm_status = sc->sc_media_status; + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static int +rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct rue_softc *sc = ifp->if_softc; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_config_copy, + &rue_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_cfg_pre_init, + &rue_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_cfg_pre_stop, + &rue_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_config_copy, + &rue_cfg_promisc_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = GET_MII(sc); + if (mii == NULL) { + error = EINVAL; + } else { + error = ifmedia_ioctl + (ifp, (void *)data, &mii->mii_media, command); + } + break; + + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static void +rue_watchdog(void *arg) +{ + struct rue_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &rue_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &rue_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +/* + * NOTE: can be called when "ifp" is NULL + */ +static void +rue_cfg_pre_stop(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + rue_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(RUE_FLAG_HL_READY | + RUE_FLAG_LL_READY); + + sc->sc_flags |= RUE_FLAG_WAIT_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[4]); + usb2_transfer_stop(sc->sc_xfer[5]); + return; +} + +static void +rue_cfg_stop(struct rue_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + rue_cfg_csr_write_1(sc, RUE_CR, 0x00); + + rue_cfg_reset(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 +rue_shutdown(device_t dev) +{ + struct rue_softc *sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &rue_cfg_pre_stop, + &rue_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_rue2_reg.h b/sys/dev/usb2/ethernet/if_rue2_reg.h new file mode 100644 index 000000000000..2c95c226fe6c --- /dev/null +++ b/sys/dev/usb2/ethernet/if_rue2_reg.h @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama . + * 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$ + */ + +#define RUE_CONFIG_IDX 0 /* config number 1 */ +#define RUE_IFACE_IDX 0 + +#define RUE_ENDPT_MAX 6 + +#define RUE_INTR_PKTLEN 0x8 + +#define RUE_TIMEOUT 50 +#define RUE_MIN_FRAMELEN 60 + +/* 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) + +#define RUE_RXSTAT_VALID (0x01 << 12) +#define RUE_RXSTAT_RUNT (0x02 << 12) +#define RUE_RXSTAT_PMATCH (0x04 << 12) +#define RUE_RXSTAT_MCAST (0x08 << 12) + +#define GET_MII(sc) ((sc)->sc_miibus ? \ + device_get_softc((sc)->sc_miibus) : NULL) + +struct rue_intrpkt { + uint8_t rue_tsr; + uint8_t rue_rsr; + uint8_t rue_gep_msr; + uint8_t rue_waksr; + uint8_t rue_txok_cnt; + uint8_t rue_rxlost_cnt; + uint8_t rue_crcerr_cnt; + uint8_t rue_col_cnt; +} __packed; + +struct rue_type { + uint16_t rue_vid; + uint16_t rue_did; +}; + +struct rue_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[RUE_ENDPT_MAX]; + device_t sc_miibus; + device_t sc_dev; + + uint32_t sc_unit; + uint32_t sc_media_active; + uint32_t sc_media_status; + + uint16_t sc_flags; +#define RUE_FLAG_WAIT_LINK 0x0001 +#define RUE_FLAG_INTR_STALL 0x0002 +#define RUE_FLAG_READ_STALL 0x0004 +#define RUE_FLAG_WRITE_STALL 0x0008 +#define RUE_FLAG_LL_READY 0x0010 +#define RUE_FLAG_HL_READY 0x0020 + + uint8_t sc_name[16]; +}; diff --git a/sys/dev/usb2/ethernet/if_udav2.c b/sys/dev/usb2/ethernet/if_udav2.c new file mode 100644 index 000000000000..ea7795d26ee1 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_udav2.c @@ -0,0 +1,1361 @@ +/* $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 . 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 + */ + +/* + * NOTE: all function names beginning like "udav_cfg_" can only + * be called from within the config thread function ! + */ + +/* + * TODO: + * Interrupt Endpoint support + * External PHYs + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#define usb2_config_td_cc usb2_ether_cc +#define usb2_config_td_softc udav_softc + +#define USB_DEBUG_VAR udav_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* prototypes */ + +static device_probe_t udav_probe; +static device_attach_t udav_attach; +static device_detach_t udav_detach; +static device_shutdown_t udav_shutdown; + +static usb2_callback_t udav_bulk_write_clear_stall_callback; +static usb2_callback_t udav_bulk_write_callback; +static usb2_callback_t udav_bulk_read_clear_stall_callback; +static usb2_callback_t udav_bulk_read_callback; +static usb2_callback_t udav_intr_clear_stall_callback; +static usb2_callback_t udav_intr_callback; + +static usb2_config_td_command_t udav_cfg_first_time_setup; +static usb2_config_td_command_t udav_cfg_pre_init; +static usb2_config_td_command_t udav_cfg_init; +static usb2_config_td_command_t udav_config_copy; +static usb2_config_td_command_t udav_cfg_promisc_upd; +static usb2_config_td_command_t udav_cfg_pre_stop; +static usb2_config_td_command_t udav_cfg_stop; +static usb2_config_td_command_t udav_cfg_ifmedia_change; +static usb2_config_td_command_t udav_cfg_tick; + +static void udav_cfg_do_request(struct udav_softc *sc, struct usb2_device_request *req, void *data); +static void udav_cfg_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len); +static void udav_cfg_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len); +static uint8_t udav_cfg_csr_read1(struct udav_softc *sc, uint16_t offset); +static void udav_cfg_csr_write1(struct udav_softc *sc, uint16_t offset, uint8_t ch); +static void udav_init_cb(void *arg); +static void udav_cfg_reset(struct udav_softc *sc); +static void udav_start_cb(struct ifnet *ifp); +static void udav_start_transfers(struct udav_softc *sc); +static int udav_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); +static void udav_watchdog(void *arg); +static int udav_ifmedia_change_cb(struct ifnet *ifp); +static void udav_ifmedia_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr); + +static miibus_readreg_t udav_cfg_miibus_readreg; +static miibus_writereg_t udav_cfg_miibus_writereg; +static miibus_statchg_t udav_cfg_miibus_statchg; + +static const struct usb2_config udav_config[UDAV_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (MCLBYTES + 2), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &udav_bulk_write_callback, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + 3), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &udav_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &udav_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &udav_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &udav_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &udav_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static device_method_t udav_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, udav_probe), + 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_cfg_miibus_readreg), + DEVMETHOD(miibus_writereg, udav_cfg_miibus_writereg), + DEVMETHOD(miibus_statchg, udav_cfg_miibus_statchg), + + {0, 0} +}; + +static driver_t udav_driver = { + .name = "udav", + .methods = udav_methods, + .size = sizeof(struct udav_softc), +}; + +static devclass_t udav_devclass; + +DRIVER_MODULE(udav, ushub, udav_driver, udav_devclass, NULL, 0); +DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(udav, usb2_ethernet, 1, 1, 1); +MODULE_DEPEND(udav, usb2_core, 1, 1, 1); +MODULE_DEPEND(udav, ether, 1, 1, 1); +MODULE_DEPEND(udav, miibus, 1, 1, 1); + +#if USB_DEBUG +static int udav_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav"); +SYSCTL_INT(_hw_usb2_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0, + "Debug level"); +#endif + +#define UDAV_CFG_SETBIT(sc, reg, x) \ + udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) | (x)) + +#define UDAV_CFG_CLRBIT(sc, reg, x) \ + udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) & ~(x)) + +static const struct usb2_device_id udav_devs[] = { + /* ShanTou DM9601 USB NIC */ + {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)}, + + /* ShanTou ST268 USB NIC */ + {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)}, + + /* Corega USB-TXC */ + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)}, +}; + +static int +udav_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa)); +} + +static int +udav_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct udav_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "udav lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = UDAV_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, udav_config, UDAV_ENDPT_MAX, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + NULL, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= UDAV_FLAG_WAIT_LINK; + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &udav_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + udav_watchdog(sc); + + return (0); /* success */ + +detach: + udav_detach(dev); + return (ENXIO); /* failure */ +} + +static void +udav_cfg_first_time_setup(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + int error; + uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; + + /* reset the adapter */ + + udav_cfg_reset(sc); + + /* get Ethernet Address */ + + udav_cfg_csr_read(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_ETHER); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + printf("%s: could not if_alloc()\n", + sc->sc_name); + goto done; + } + sc->sc_evilhack = ifp; + + ifp->if_softc = sc; + if_initname(ifp, "udav", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = udav_start_cb; + ifp->if_ioctl = udav_ioctl_cb; + ifp->if_watchdog = NULL; + ifp->if_init = udav_init_cb; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* + * XXX need Giant when accessing the device structures ! + */ + + mtx_unlock(&sc->sc_mtx); + + mtx_lock(&Giant); + + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + &udav_ifmedia_change_cb, + &udav_ifmedia_status_cb); + mtx_unlock(&Giant); + + mtx_lock(&sc->sc_mtx); + + if (error) { + printf("%s: MII without any PHY!\n", + sc->sc_name); + if_free(ifp); + goto done; + } + sc->sc_ifp = ifp; + + mtx_unlock(&sc->sc_mtx); + + /* + * Call MI attach routine. + */ + + ether_ifattach(ifp, eaddr); + + mtx_lock(&sc->sc_mtx); + +done: + return; +} + +static int +udav_detach(device_t dev) +{ + struct udav_softc *sc = device_get_softc(dev); + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + udav_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, UDAV_ENDPT_MAX); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + ether_ifdetach(ifp); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +udav_cfg_do_request(struct udav_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +#if 0 +static void +udav_cfg_mem_read(struct udav_softc *sc, uint16_t offset, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + 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); + + udav_cfg_do_request(sc, &req, buf); + return; +} + +static void +udav_cfg_mem_write(struct udav_softc *sc, uint16_t offset, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + 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); + + udav_cfg_do_request(sc, &req, buf); + return; +} + +static void +udav_cfg_mem_write1(struct udav_softc *sc, uint16_t offset, + uint8_t ch) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_MEM_WRITE1; + USETW(req.wValue, ch); + USETW(req.wIndex, offset); + USETW(req.wLength, 0x0000); + + udav_cfg_do_request(sc, &req, NULL); + return; +} + +#endif + +static void +udav_cfg_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + 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); + + udav_cfg_do_request(sc, &req, buf); + return; +} + +static void +udav_cfg_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, + uint16_t len) +{ + struct usb2_device_request req; + + 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); + + udav_cfg_do_request(sc, &req, buf); + return; +} + +static uint8_t +udav_cfg_csr_read1(struct udav_softc *sc, uint16_t offset) +{ + uint8_t val; + + udav_cfg_csr_read(sc, offset, &val, 1); + return (val); +} + +static void +udav_cfg_csr_write1(struct udav_softc *sc, uint16_t offset, + uint8_t ch) +{ + struct usb2_device_request req; + + 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); + + udav_cfg_do_request(sc, &req, NULL); + return; +} + +static void +udav_init_cb(void *arg) +{ + struct udav_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_cfg_pre_init, + &udav_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +udav_cfg_pre_init(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* immediate configuration */ + + udav_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= UDAV_FLAG_HL_READY; + + return; +} + +static void +udav_cfg_init(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct mii_data *mii = GET_MII(sc); + + /* + * Cancel pending I/O + */ + + udav_cfg_stop(sc, cc, 0); + + /* set MAC address */ + + udav_cfg_csr_write(sc, UDAV_PAR, cc->if_lladdr, ETHER_ADDR_LEN); + + /* initialize network control register */ + + /* disable loopback */ + + UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); + + /* Initialize RX control register */ + UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); + + /* load multicast filter and update promiscious mode bit */ + udav_cfg_promisc_upd(sc, cc, 0); + + /* enable RX */ + UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); + + /* clear POWER_DOWN state of internal PHY */ + UDAV_CFG_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); + UDAV_CFG_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); + + mii_mediachg(mii); + + sc->sc_flags |= (UDAV_FLAG_READ_STALL | + UDAV_FLAG_WRITE_STALL | + UDAV_FLAG_LL_READY); + + udav_start_transfers(sc); + + return; +} + +static void +udav_cfg_reset(struct udav_softc *sc) +{ + usb2_error_t err; + uint16_t to; + + /* Select PHY */ +#if 1 + /* + * XXX: force select internal phy. + * external phy routines are not tested. + */ + UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); +#else + if (sc->sc_flags & UDAV_EXT_PHY) { + UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); + } else { + UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); + } +#endif + + UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); + + for (to = 0;; to++) { + + if (to < 100) { + + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + if (err) { + break; + } + if (!(udav_cfg_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) { + break; + } + } else { + printf("%s: reset timeout!\n", + sc->sc_name); + break; + } + } + + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); + + return; +} + +#define UDAV_BITS 6 + +static void +udav_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) +{ + uint8_t h; + + h = ether_crc32_le(ptr, ETHER_ADDR_LEN) & + ((1 << UDAV_BITS) - 1); + cc->if_hash[h >> 3] |= 1 << (h & 0x7); + return; +} + +static void +udav_config_copy(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + bzero(cc, sizeof(*cc)); + usb2_ether_cc(sc->sc_ifp, &udav_mchash, cc); + return; +} + +static void +udav_cfg_promisc_upd(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint8_t rxmode; + + rxmode = udav_cfg_csr_read1(sc, UDAV_RCR); + + rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC); + + if (cc->if_flags & IFF_PROMISC) { + + rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC; + + } else if (cc->if_flags & IFF_ALLMULTI) { + + rxmode |= UDAV_RCR_ALL; + } + /* write hash value to the register */ + udav_cfg_csr_write(sc, UDAV_MAR, cc->if_hash, 8); + + /* write new mode bits */ + udav_cfg_csr_write1(sc, UDAV_RCR, rxmode); + + return; +} + +static void +udav_start_cb(struct ifnet *ifp) +{ + struct udav_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + udav_start_transfers(sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +udav_start_transfers(struct udav_softc *sc) +{ + if ((sc->sc_flags & UDAV_FLAG_LL_READY) && + (sc->sc_flags & UDAV_FLAG_HL_READY)) { + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[4]); + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +udav_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UDAV_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +udav_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint32_t extra_len; + uint32_t temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + + if (sc->sc_flags & UDAV_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + goto done; + } + if (sc->sc_flags & UDAV_FLAG_WAIT_LINK) { + /* + * don't send anything if there is no link ! + */ + goto done; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + goto done; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) { + extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len; + } else { + extra_len = 0; + } + + temp_len = (m->m_pkthdr.len + extra_len); + + /* + * the frame length is specified in the first 2 bytes of the + * buffer + */ + buf[0] = (uint8_t)(temp_len); + buf[1] = (uint8_t)(temp_len >> 8); + + temp_len += 2; + + usb2_copy_in(xfer->frbuffers, 0, buf, 2); + + usb2_m_copy_in(xfer->frbuffers, 2, + m, 0, m->m_pkthdr.len); + + if (extra_len) { + usb2_bzero(xfer->frbuffers, temp_len - extra_len, + extra_len); + } + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + xfer->frlengths[0] = temp_len; + usb2_start_hardware(xfer); + +done: + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UDAV_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + return; + + } +} + +static void +udav_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UDAV_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +udav_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + uint8_t status; + uint16_t total_len; + struct mbuf *m = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < 1) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen -= 1; + + usb2_copy_out(xfer->frbuffers, 0, &status, 1); + + if (status & UDAV_RSR_LCS) { + ifp->if_collisions++; + goto tr_setup; + } + if ((status & UDAV_RSR_ERR) || (xfer->actlen < 2)) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 1, &total_len, 2); + + total_len = le16toh(total_len); + + xfer->actlen -= 2; + + xfer->actlen = min(xfer->actlen, total_len); + + if (xfer->actlen < (sizeof(struct ether_header) + ETHER_CRC_LEN)) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen -= ETHER_CRC_LEN; + + m = usb2_ether_get_mbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto tr_setup; + } + xfer->actlen = min(xfer->actlen, m->m_len); + + usb2_copy_out(xfer->frbuffers, 3, m->m_data, xfer->actlen); + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = xfer->actlen; + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & UDAV_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "if_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + (ifp->if_input) (ifp, m); + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UDAV_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + DPRINTF("bulk read error, %s\n", + usb2_errstr(xfer->error)); + return; + + } +} + +static void +udav_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UDAV_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +udav_intr_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + case USB_ST_SETUP: + if (sc->sc_flags & UDAV_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= UDAV_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + } +} + +static int +udav_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct udav_softc *sc = ifp->if_softc; + struct mii_data *mii; + int error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_config_copy, + &udav_cfg_promisc_upd, 0, 0); + } else { + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_cfg_pre_init, + &udav_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_cfg_pre_stop, + &udav_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_config_copy, + &udav_cfg_promisc_upd, 0, 0); + mtx_unlock(&sc->sc_mtx); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = GET_MII(sc); + if (mii == NULL) { + error = EINVAL; + } else { + error = ifmedia_ioctl + (ifp, (void *)data, &mii->mii_media, cmd); + } + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return (error); +} + +static void +udav_watchdog(void *arg) +{ + struct udav_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &udav_cfg_tick, 0, 0); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &udav_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +/* + * NOTE: can be called when "ifp" is NULL + */ +static void +udav_cfg_pre_stop(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + udav_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(UDAV_FLAG_HL_READY | + UDAV_FLAG_LL_READY); + + sc->sc_flags |= UDAV_FLAG_WAIT_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[4]); + usb2_transfer_stop(sc->sc_xfer[5]); + return; +} + +/* + * NOTE: can be called when "ifp" is NULL + */ +static void +udav_cfg_stop(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + udav_cfg_reset(sc); + return; +} + +static int +udav_ifmedia_change_cb(struct ifnet *ifp) +{ + struct udav_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, + &udav_cfg_ifmedia_change, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static void +udav_cfg_ifmedia_change(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + sc->sc_flags |= UDAV_FLAG_WAIT_LINK; + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + mii_phy_reset(miisc); + } + } + mii_mediachg(mii); + + return; +} + +static void +udav_ifmedia_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct udav_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifmr->ifm_active = sc->sc_media_active; + ifmr->ifm_status = sc->sc_media_status; + } else { + ifmr->ifm_active = IFM_ETHER | IFM_NONE; + ifmr->ifm_status = 0; + } + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +udav_cfg_tick(struct udav_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mii_data *mii = GET_MII(sc); + + if ((ifp == NULL) || + (mii == NULL)) { + /* not ready */ + return; + } + mii_tick(mii); + + mii_pollstat(mii); + + if ((sc->sc_flags & UDAV_FLAG_WAIT_LINK) && + (mii->mii_media_status & IFM_ACTIVE) && + (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { + sc->sc_flags &= ~UDAV_FLAG_WAIT_LINK; + } + sc->sc_media_active = mii->mii_media_active; + sc->sc_media_status = mii->mii_media_status; + + /* start stopped transfers, if any */ + + udav_start_transfers(sc); + + return; +} + +static int +udav_cfg_miibus_readreg(device_t dev, int phy, int reg) +{ + struct udav_softc *sc = device_get_softc(dev); + uint16_t data16; + uint8_t val[2]; + uint8_t do_unlock; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) { + return (0); + } + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + /* select internal PHY and set PHY register address */ + udav_cfg_csr_write1(sc, UDAV_EPAR, + UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); + + /* select PHY operation and start read command */ + udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); + + /* XXX: should we wait? */ + + /* end read command */ + UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); + + /* retrieve the result from data registers */ + udav_cfg_csr_read(sc, UDAV_EPDRL, val, 2); + + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + data16 = (val[0] | (val[1] << 8)); + + DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n", + phy, reg, data16); + + return (data16); +} + +static int +udav_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct udav_softc *sc = device_get_softc(dev); + uint8_t val[2]; + uint8_t do_unlock; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) { + return (0); + } + /* avoid recursive locking */ + if (mtx_owned(&sc->sc_mtx)) { + do_unlock = 0; + } else { + mtx_lock(&sc->sc_mtx); + do_unlock = 1; + } + + /* select internal PHY and set PHY register address */ + udav_cfg_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_cfg_csr_write(sc, UDAV_EPDRL, val, 2); + + /* select PHY operation and start write command */ + udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); + + /* XXX: should we wait? */ + + /* end write command */ + UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); + + if (do_unlock) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +static void +udav_cfg_miibus_statchg(device_t dev) +{ + /* nothing to do */ + return; +} + +/* + * 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 = device_get_softc(dev); + + mtx_lock(&sc->sc_mtx); + + usb2_config_td_queue_command + (&sc->sc_config_td, &udav_cfg_pre_stop, + &udav_cfg_stop, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/ethernet/if_udav2_reg.h b/sys/dev/usb2/ethernet/if_udav2_reg.h new file mode 100644 index 000000000000..aa211e6be319 --- /dev/null +++ b/sys/dev/usb2/ethernet/if_udav2_reg.h @@ -0,0 +1,166 @@ +/* $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 . 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_INDEX 0 /* config number 1 */ + +#define UDAV_ENDPT_MAX 6 /* units */ + +/* 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 */ + +#define GET_MII(sc) ((sc)->sc_miibus ? \ + device_get_softc((sc)->sc_miibus) : NULL) + +struct udav_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct usb2_config_td sc_config_td; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[UDAV_ENDPT_MAX]; + device_t sc_miibus; + device_t sc_dev; + + uint32_t sc_unit; + uint32_t sc_media_active; + uint32_t sc_media_status; + + uint16_t sc_flags; +#define UDAV_FLAG_WAIT_LINK 0x0001 +#define UDAV_FLAG_INTR_STALL 0x0002 +#define UDAV_FLAG_READ_STALL 0x0004 +#define UDAV_FLAG_WRITE_STALL 0x0008 +#define UDAV_FLAG_LL_READY 0x0010 +#define UDAV_FLAG_HL_READY 0x0020 +#define UDAV_FLAG_EXT_PHY 0x0040 + + uint8_t sc_name[16]; +}; diff --git a/sys/dev/usb2/ethernet/usb2_ethernet.c b/sys/dev/usb2/ethernet/usb2_ethernet.c new file mode 100644 index 000000000000..6651e0b5a176 --- /dev/null +++ b/sys/dev/usb2/ethernet/usb2_ethernet.c @@ -0,0 +1,101 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_ethernet, 1); +MODULE_DEPEND(usb2_ethernet, usb2_core, 1, 1, 1); + +/*------------------------------------------------------------------------* + * usb2_ether_get_mbuf - get a new ethernet aligned mbuf + *------------------------------------------------------------------------*/ +struct mbuf * +usb2_ether_get_mbuf(void) +{ + struct mbuf *m; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m) { + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, ETHER_ALIGN); + } + return (m); +} + +/*------------------------------------------------------------------------* + * usb2_ether_cc - common ethernet config copy + *------------------------------------------------------------------------*/ +void +usb2_ether_cc(struct ifnet *ifp, usb2_ether_mchash_t *fhash, + struct usb2_ether_cc *cc) +{ + struct ifmultiaddr *ifma; + uint8_t i; + + if (ifp == NULL) { + /* Nothing to do */ + return; + } + /* Copy interface flags */ + + cc->if_flags = ifp->if_flags; + + /* Copy link layer address */ + + for (i = 0; i != ETHER_ADDR_LEN; i++) { + cc->if_lladdr[i] = IF_LLADDR(ifp)[i]; + } + + /* Check hash filter disable bits */ + + if ((ifp->if_flags & IFF_ALLMULTI) || + (ifp->if_flags & IFF_PROMISC)) { + + memset(cc->if_hash, 0xFF, sizeof(cc->if_hash)); + + } else if (fhash) { + + /* Compute hash bits for multicast filter */ + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) { + continue; + } + fhash(cc, LLADDR((struct sockaddr_dl *) + (ifma->ifma_addr))); + } + IF_ADDR_UNLOCK(ifp); + + /* Compute hash bits for broadcast address */ + + if (ifp->if_flags & IFF_BROADCAST) { + fhash(cc, ifp->if_broadcastaddr); + } + } + return; +} diff --git a/sys/dev/usb2/ethernet/usb2_ethernet.h b/sys/dev/usb2/ethernet/usb2_ethernet.h new file mode 100644 index 000000000000..e6e039b552d9 --- /dev/null +++ b/sys/dev/usb2/ethernet/usb2_ethernet.h @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_ETHERNET_H_ +#define _USB2_ETHERNET_H_ + +#include "opt_inet.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "miibus_if.h" + +#include +#include + +#define USB_ETHER_HASH_MAX 64 /* bytes */ + +struct usb2_ether_cc { + uint32_t if_flags; + uint16_t if_rxfilt; + uint8_t if_lladdr[ETHER_ADDR_LEN]; + uint8_t if_mhash; + uint8_t if_nhash; + uint8_t if_hash[USB_ETHER_HASH_MAX]; +}; + +typedef void (usb2_ether_mchash_t)(struct usb2_ether_cc *cc, const uint8_t *ptr); + +struct mbuf *usb2_ether_get_mbuf(void); +void usb2_ether_cc(struct ifnet *ifp, usb2_ether_mchash_t *fhash, struct usb2_ether_cc *cc); + +#endif /* _USB2_ETHERNET_H_ */ diff --git a/sys/dev/usb2/image/usb2_image.c b/sys/dev/usb2/image/usb2_image.c new file mode 100644 index 000000000000..5f6badbbe2c3 --- /dev/null +++ b/sys/dev/usb2/image/usb2_image.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_image, 1); +MODULE_DEPEND(usb2_image, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/image/usb2_image.h b/sys/dev/usb2/image/usb2_image.h new file mode 100644 index 000000000000..ce1526ed77d8 --- /dev/null +++ b/sys/dev/usb2/image/usb2_image.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_IMAGE_H_ +#define _USB2_IMAGE_H_ + +#endif /* _USB2_IMAGE_H_ */ diff --git a/sys/dev/usb2/image/uscanner2.c b/sys/dev/usb2/image/uscanner2.c new file mode 100644 index 000000000000..4dcdde3636f6 --- /dev/null +++ b/sys/dev/usb2/image/uscanner2.c @@ -0,0 +1,642 @@ +/* $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 +__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 +#include +#include +#include + +#define USB_DEBUG_VAR uscanner_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USB_DEBUG +static int uscanner_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner"); +SYSCTL_INT(_hw_usb2_uscanner, OID_AUTO, uscanner, CTLFLAG_RW, &uscanner_debug, + 0, "uscanner debug level"); +#endif + +/* + * uscanner transfers macros definition. + */ +#define USCANNER_BSIZE (1 << 15) +#define USCANNER_IFQ_MAXLEN 2 +#define USCANNER_N_TRANSFER 4 + +/* + * Transfers stallings handling flags definition. + */ +#define USCANNER_FLAG_READ_STALL 0x01 +#define USCANNER_FLAG_WRITE_STALL 0x02 + +/* + * uscanner_info flags definition. + */ +#define USCANNER_FLAG_KEEP_OPEN 0x04 + +struct uscanner_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + + struct usb2_xfer *sc_xfer[USCANNER_N_TRANSFER]; + + uint8_t sc_flags; /* Used to prevent stalls */ +}; + +/* + * Prototypes for driver handling routines (sorted by use). + */ +static device_probe_t uscanner_probe; +static device_attach_t uscanner_attach; +static device_detach_t uscanner_detach; + +/* + * Prototypes for xfer transfer callbacks. + */ +static usb2_callback_t uscanner_read_callback; +static usb2_callback_t uscanner_read_clear_stall_callback; +static usb2_callback_t uscanner_write_callback; +static usb2_callback_t uscanner_write_clear_stall_callback; + +/* + * Prototypes for the character device handling routines. + */ +static usb2_fifo_close_t uscanner_close; +static usb2_fifo_cmd_t uscanner_start_read; +static usb2_fifo_cmd_t uscanner_start_write; +static usb2_fifo_cmd_t uscanner_stop_read; +static usb2_fifo_cmd_t uscanner_stop_write; +static usb2_fifo_open_t uscanner_open; + +static struct usb2_fifo_methods uscanner_fifo_methods = { + .f_close = &uscanner_close, + .f_open = &uscanner_open, + .f_start_read = &uscanner_start_read, + .f_start_write = &uscanner_start_write, + .f_stop_read = &uscanner_stop_read, + .f_stop_write = &uscanner_stop_write, + .basename[0] = "uscanner", +}; + +/* + * xfer transfers array. Resolve-stalling callbacks are marked as control + * transfers. + */ +static const struct usb2_config uscanner_config[USCANNER_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = USCANNER_BSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,.force_short_xfer = 1,}, + .mh.callback = &uscanner_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = USCANNER_BSIZE, + .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &uscanner_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uscanner_write_clear_stall_callback, + .mh.timeout = 1000, + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uscanner_read_clear_stall_callback, + .mh.timeout = 1000, + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t uscanner_devclass; + +static device_method_t uscanner_methods[] = { + DEVMETHOD(device_probe, uscanner_probe), + DEVMETHOD(device_attach, uscanner_attach), + DEVMETHOD(device_detach, uscanner_detach), + {0, 0} +}; + +static driver_t uscanner_driver = { + .name = "uscanner", + .methods = uscanner_methods, + .size = sizeof(struct uscanner_softc), +}; + +DRIVER_MODULE(uscanner, ushub, uscanner_driver, uscanner_devclass, NULL, 0); +MODULE_DEPEND(uscanner, usb2_image, 1, 1, 1); +MODULE_DEPEND(uscanner, usb2_core, 1, 1, 1); + +/* + * USB scanners device IDs + */ +static const struct usb2_device_id uscanner_devs[] = { + /* Acer */ + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U, 0)}, + {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U, 0)}, + /* AGFA */ + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26, 0)}, + {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52, 0)}, + /* Avision */ + {USB_VPI(USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U, 0)}, + /* Canon */ + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U, 0)}, + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U, 0)}, + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U, 0)}, + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U, 0)}, + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U, 0)}, + {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25, 0)}, + /* Epson */ + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200, USCANNER_FLAG_KEEP_OPEN)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F, USCANNER_FLAG_KEEP_OPEN)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500, USCANNER_FLAG_KEEP_OPEN)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000, 0)}, + {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400, 0)}, + /* HP */ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2200C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3300C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4100C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4200C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4300C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4670V, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_S20, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5200C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5300C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5400C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6200C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6300C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_82x0C, 0)}, + /* Kye */ + {USB_VPI(USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO, 0)}, + /* Microtek */ + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2, 0)}, + {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL, 0)}, + /* Minolta */ + {USB_VPI(USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400, 0)}, + /* Mustek */ + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS, 0)}, + {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS, 0)}, + /* National */ + {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200, 0)}, + {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400, 0)}, + /* Nikon */ + {USB_VPI(USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40, 0)}, + /* Primax */ + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600, 0)}, + {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600, 0)}, + /* Scanlogic */ + {USB_VPI(USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX, 0)}, + /* Ultima */ + {USB_VPI(USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS, 0)}, + /* UMAX */ + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U, 0)}, + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U, 0)}, + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U, 0)}, + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U, 0)}, + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U, 0)}, + {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400, 0)}, + /* Visioneer */ + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100, 0)}, + {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600, 0)} +}; + +/* + * uscanner device probing method. + */ +static int +uscanner_probe(device_t dev) +{ + struct usb2_attach_arg *uaa; + + DPRINTFN(11, "\n"); + + uaa = device_get_ivars(dev); + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* Give other class drivers a chance for multifunctional scanners. */ + if (uaa->use_generic == 0) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uscanner_devs, sizeof(uscanner_devs), uaa)); +} + +/* + * uscanner device attaching method. + */ +static int +uscanner_attach(device_t dev) +{ + struct usb2_attach_arg *uaa; + struct uscanner_softc *sc; + int unit; + int error; + + uaa = device_get_ivars(dev); + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + /* + * A first path softc structure filling. sc_fifo and + * sc_xfer are initialised later. + */ + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + mtx_init(&sc->sc_mtx, "uscanner mutex", NULL, MTX_DEF | MTX_RECURSE); + + /* + * Announce the device: + */ + device_set_usb2_desc(dev); + + /* + * Setup the transfer. + */ + if ((error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, + uscanner_config, USCANNER_N_TRANSFER, sc, &sc->sc_mtx))) { + device_printf(dev, "could not setup transfers, " + "error=%s\n", usb2_errstr(error)); + goto detach; + } + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &uscanner_fifo_methods, &sc->sc_fifo, + unit, 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + return (0); + +detach: + uscanner_detach(dev); + return (ENOMEM); +} + +/* + * uscanner device detaching method. + */ +static int +uscanner_detach(device_t dev) +{ + struct uscanner_softc *sc; + + sc = device_get_softc(dev); + + usb2_fifo_detach(&sc->sc_fifo); + usb2_transfer_unsetup(sc->sc_xfer, USCANNER_N_TRANSFER); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +/* + * Reading callback. Implemented as an "in" bulk transfer. + */ +static void +uscanner_read_callback(struct usb2_xfer *xfer) +{ + struct uscanner_softc *sc; + struct usb2_fifo *f; + + sc = xfer->priv_sc; + f = sc->sc_fifo.fp[USB_FIFO_RX]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_fifo_put_data(f, xfer->frbuffers, 0, + xfer->actlen, 1); + + case USB_ST_SETUP: + /* + * If reading is in stall, just jump to clear stall callback and + * solve the situation. + */ + if (sc->sc_flags & USCANNER_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + break; + } + if (usb2_fifo_put_bytes_max(f) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= USCANNER_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + break; + } + return; +} + +/* + * Removing stall on reading callback. + */ +static void +uscanner_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uscanner_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/* + * Writing callback. Implemented as an "out" bulk transfer. + */ +static void +uscanner_write_callback(struct usb2_xfer *xfer) +{ + struct uscanner_softc *sc; + struct usb2_fifo *f; + uint32_t actlen; + + sc = xfer->priv_sc; + f = sc->sc_fifo.fp[USB_FIFO_TX]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + /* + * If writing is in stall, just jump to clear stall callback and + * solve the situation. + */ + if (sc->sc_flags & USCANNER_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + break; + } + /* + * Write datas, setup and perform hardware transfer. + */ + if (usb2_fifo_get_data(f, xfer->frbuffers, 0, + xfer->max_data_length, &actlen, 0)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + break; + } + return; +} + +/* + * Removing stall on writing callback. + */ +static void +uscanner_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uscanner_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/* + * uscanner character device opening method. + */ +static int +uscanner_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct uscanner_softc *sc; + + sc = fifo->priv_sc0; + + if (!(sc->sc_flags & USCANNER_FLAG_KEEP_OPEN)) { + if (fflags & FWRITE) { + sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; + } + if (fflags & FREAD) { + sc->sc_flags |= USCANNER_FLAG_READ_STALL; + } + } + if (fflags & FREAD) { + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[1]->max_data_length, + USCANNER_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + if (fflags & FWRITE) { + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[0]->max_data_length, + USCANNER_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + return (0); +} + +static void +uscanner_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & (FREAD | FWRITE)) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +/* + * uscanner character device start reading method. + */ +static void +uscanner_start_read(struct usb2_fifo *fifo) +{ + struct uscanner_softc *sc; + + sc = fifo->priv_sc0; + usb2_transfer_start(sc->sc_xfer[1]); +} + +/* + * uscanner character device start writing method. + */ +static void +uscanner_start_write(struct usb2_fifo *fifo) +{ + struct uscanner_softc *sc; + + sc = fifo->priv_sc0; + usb2_transfer_start(sc->sc_xfer[0]); +} + +/* + * uscanner character device stop reading method. + */ +static void +uscanner_stop_read(struct usb2_fifo *fifo) +{ + struct uscanner_softc *sc; + + sc = fifo->priv_sc0; + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); +} + +/* + * uscanner character device stop writing method. + */ +static void +uscanner_stop_write(struct usb2_fifo *fifo) +{ + struct uscanner_softc *sc; + + sc = fifo->priv_sc0; + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); +} diff --git a/sys/dev/usb2/include/Makefile b/sys/dev/usb2/include/Makefile new file mode 100644 index 000000000000..a8f35009ea6d --- /dev/null +++ b/sys/dev/usb2/include/Makefile @@ -0,0 +1,14 @@ +# +# $FreeBSD$ +# + + +S=${.CURDIR}/../../.. + +all: + awk -f $S/tools/usbdevs2h.awk $S/dev/usb2/core/usbdevs -d ; mv usbdevs_data.h usb2_devtable.h + awk -f $S/tools/usbdevs2h.awk $S/dev/usb2/core/usbdevs -h ; mv usbdevs.h usb2_devid.h + +clean: + rm -f usb2_devtable.h usb2_devid.h + diff --git a/sys/dev/usb2/include/ufm2_ioctl.h b/sys/dev/usb2/include/ufm2_ioctl.h new file mode 100644 index 000000000000..921b3d4f37a7 --- /dev/null +++ b/sys/dev/usb2/include/ufm2_ioctl.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 + +#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/dev/usb2/include/urio2_ioctl.h b/sys/dev/usb2/include/urio2_ioctl.h new file mode 100644 index 000000000000..713647cf9773 --- /dev/null +++ b/sys/dev/usb2/include/urio2_ioctl.h @@ -0,0 +1,41 @@ +/*- + ---------------------------------------------------------------------- + + 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 + + ---------------------------------------------------------------------- */ + +/* $FreeBSD$ */ + +#include + +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/dev/usb2/include/usb2_cdc.h b/sys/dev/usb2/include/usb2_cdc.h new file mode 100644 index 000000000000..8031990ff0cc --- /dev/null +++ b/sys/dev/usb2/include/usb2_cdc.h @@ -0,0 +1,205 @@ +/* $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 _USB_CDC_H_ +#define _USB_CDC_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 + +struct usb2_cdc_header_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdCDC; +} __packed; + +struct usb2_cdc_cm_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmCapabilities; +#define USB_CDC_CM_DOES_CM 0x01 +#define USB_CDC_CM_OVER_DATA 0x02 + uByte bDataInterface; +} __packed; + +struct usb2_cdc_acm_descriptor { + 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 +} __packed; + +struct usb2_cdc_union_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bMasterInterface; + uByte bSlaveInterface[1]; +} __packed; + +struct usb2_cdc_ethernet_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte iMacAddress; + uDWord bmEthernetStatistics; + uWord wMaxSegmentSize; + uWord wNumberMCFilters; + uByte bNumberPowerFilters; +} __packed; + +#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 + +struct usb2_cdc_abstract_state { + uWord wState; +#define UCDC_IDLE_SETTING 0x0001 +#define UCDC_DATA_MULTIPLEXED 0x0002 +} __packed; + +#define UCDC_ABSTRACT_STATE_LENGTH 2 + +struct usb2_cdc_line_state { + 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; +} __packed; + +#define UCDC_LINE_STATE_LENGTH 7 + +struct usb2_cdc_notification { + 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]; +} __packed; + +#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 + +/* 512x4 Multi Frame Ethernet Header */ +struct usb2_cdc_mf_eth_512x4_header { + uByte bSig[2]; /* "FL" - Frag List */ + uByte bReserved[4]; + uWord wFragLength[511 * 4]; +#define CDCE_512X4_FRAG_LENGTH_OFFSET 6 /* bytes */ +#define CDCE_512X4_FRAG_LAST_MASK 0x8000 +#define CDCE_512X4_FRAG_LENGTH_MASK 0x1FFF /* bytes */ +#define CDCE_512X4_FRAME_FRAG_MAX 4 /* fragments */ +#define CDCE_512X4_FRAMES_MAX 511 /* frames */ +#define CDCE_512X4_FRAGS_MAX (511 * 4) /* fragments */ + uWord wPadding; /* used to make transfer short */ +} __packed; + +#endif /* _USB_CDC_H_ */ diff --git a/sys/dev/usb2/include/usb2_defs.h b/sys/dev/usb2/include/usb2_defs.h new file mode 100644 index 000000000000..d791f4dd84d4 --- /dev/null +++ b/sys/dev/usb2/include/usb2_defs.h @@ -0,0 +1,68 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_DEFS_H_ +#define _USB2_DEFS_H_ + +/* Definition of some USB constants */ + +#define USB_BUS_MAX 256 /* units */ +#define USB_DEV_MAX 128 /* units */ +#define USB_IFACE_MAX 32 /* units */ +#define USB_EP_MAX (2*16) /* hardcoded */ +#define USB_FIFO_MAX (4 * USB_EP_MAX) + +#define USB_MAX_DEVICES USB_DEV_MAX /* including virtual root HUB and + * address zero */ +#define USB_MAX_ENDPOINTS USB_EP_MAX /* 2 directions on 16 endpoints */ + +#define USB_UNCONFIG_INDEX 0xFF /* internal use only */ +#define USB_IFACE_INDEX_ANY 0xFF /* internal use only */ + +#define USB_START_ADDR 0 /* default USB device BUS address + * after USB bus reset */ + +#define USB_CONTROL_ENDPOINT 0 /* default control endpoint */ + +#define USB_FRAMES_PER_SECOND_FS 1000 /* full speed */ +#define USB_FRAMES_PER_SECOND_HS 8000 /* high speed */ + +#define USB_FS_BYTES_PER_HS_UFRAME 188 /* bytes */ +#define USB_HS_MICRO_FRAMES_MAX 8 /* units */ + +/* sanity checks */ + +#if (USB_FIFO_MAX < USB_EP_MAX) +#error "Misconfigured limits #1" +#endif +#if (USB_FIFO_MAX & 1) +#error "Misconfigured limits #2" +#endif +#if (USB_EP_MAX < (2*16)) +#error "Misconfigured limits #3" +#endif + +#endif /* _USB2_DEFS_H_ */ diff --git a/sys/dev/usb2/include/usb2_devid.h b/sys/dev/usb2/include/usb2_devid.h new file mode 100644 index 000000000000..7f59f5465e06 --- /dev/null +++ b/sys/dev/usb2/include/usb2_devid.h @@ -0,0 +1,2489 @@ +/* $FreeBSD$ */ + +/* + * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * FreeBSD: src/sys/dev/usb/usbdevs,v 1.372 2008/09/19 09:04:06 kevlo Exp + */ +/* $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. + */ + +#define USB_VENDOR_UNKNOWN1 0x0053 /* Unknown vendor */ +#define USB_VENDOR_UNKNOWN2 0x0105 /* Unknown vendor */ +#define USB_VENDOR_EGALAX2 0x0123 /* eGalax, Inc. */ +#define USB_VENDOR_HUMAX 0x02ad /* HUMAX */ +#define USB_VENDOR_LTS 0x0386 /* LTS */ +#define USB_VENDOR_BWCT 0x03da /* Bernd Walter Computer Technology */ +#define USB_VENDOR_AOX 0x03e8 /* AOX */ +#define USB_VENDOR_THESYS 0x03e9 /* Thesys */ +#define USB_VENDOR_DATABROADCAST 0x03ea /* Data Broadcasting */ +#define USB_VENDOR_ATMEL 0x03eb /* Atmel */ +#define USB_VENDOR_IWATSU 0x03ec /* Iwatsu America */ +#define USB_VENDOR_MITSUMI 0x03ee /* Mitsumi */ +#define USB_VENDOR_HP 0x03f0 /* Hewlett Packard */ +#define USB_VENDOR_GENOA 0x03f1 /* Genoa */ +#define USB_VENDOR_OAK 0x03f2 /* Oak */ +#define USB_VENDOR_ADAPTEC 0x03f3 /* Adaptec */ +#define USB_VENDOR_DIEBOLD 0x03f4 /* Diebold */ +#define USB_VENDOR_SIEMENSELECTRO 0x03f5 /* Siemens Electromechanical */ +#define USB_VENDOR_EPSONIMAGING 0x03f8 /* Epson Imaging */ +#define USB_VENDOR_KEYTRONIC 0x03f9 /* KeyTronic */ +#define USB_VENDOR_OPTI 0x03fb /* OPTi */ +#define USB_VENDOR_ELITEGROUP 0x03fc /* Elitegroup */ +#define USB_VENDOR_XILINX 0x03fd /* Xilinx */ +#define USB_VENDOR_FARALLON 0x03fe /* Farallon Communications */ +#define USB_VENDOR_NATIONAL 0x0400 /* National Semiconductor */ +#define USB_VENDOR_NATIONALREG 0x0401 /* National Registry */ +#define USB_VENDOR_ACERLABS 0x0402 /* Acer Labs */ +#define USB_VENDOR_FTDI 0x0403 /* Future Technology Devices */ +#define USB_VENDOR_NCR 0x0404 /* NCR */ +#define USB_VENDOR_SYNOPSYS2 0x0405 /* Synopsys */ +#define USB_VENDOR_FUJITSUICL 0x0406 /* Fujitsu-ICL */ +#define USB_VENDOR_FUJITSU2 0x0407 /* Fujitsu Personal Systems */ +#define USB_VENDOR_QUANTA 0x0408 /* Quanta */ +#define USB_VENDOR_NEC 0x0409 /* NEC */ +#define USB_VENDOR_KODAK 0x040a /* Eastman Kodak */ +#define USB_VENDOR_WELTREND 0x040b /* Weltrend */ +#define USB_VENDOR_VIA 0x040d /* VIA */ +#define USB_VENDOR_MCCI 0x040e /* MCCI */ +#define USB_VENDOR_MELCO 0x0411 /* Melco */ +#define USB_VENDOR_LEADTEK 0x0413 /* Leadtek */ +#define USB_VENDOR_WINBOND 0x0416 /* Winbond */ +#define USB_VENDOR_PHOENIX 0x041a /* Phoenix */ +#define USB_VENDOR_CREATIVE 0x041e /* Creative Labs */ +#define USB_VENDOR_NOKIA 0x0421 /* Nokia */ +#define USB_VENDOR_ADI 0x0422 /* ADI Systems */ +#define USB_VENDOR_CATC 0x0423 /* Computer Access Technology */ +#define USB_VENDOR_SMC2 0x0424 /* Standard Microsystems */ +#define USB_VENDOR_MOTOROLA_HK 0x0425 /* Motorola HK */ +#define USB_VENDOR_GRAVIS 0x0428 /* Advanced Gravis Computer */ +#define USB_VENDOR_CIRRUSLOGIC 0x0429 /* Cirrus Logic */ +#define USB_VENDOR_INNOVATIVE 0x042c /* Innovative Semiconductors */ +#define USB_VENDOR_MOLEX 0x042f /* Molex */ +#define USB_VENDOR_SUN 0x0430 /* Sun Microsystems */ +#define USB_VENDOR_UNISYS 0x0432 /* Unisys */ +#define USB_VENDOR_TAUGA 0x0436 /* Taugagreining HF */ +#define USB_VENDOR_AMD 0x0438 /* Advanced Micro Devices */ +#define USB_VENDOR_LEXMARK 0x043d /* Lexmark International */ +#define USB_VENDOR_LG 0x043e /* LG Electronics */ +#define USB_VENDOR_NANAO 0x0440 /* NANAO */ +#define USB_VENDOR_GATEWAY 0x0443 /* Gateway 2000 */ +#define USB_VENDOR_NMB 0x0446 /* NMB */ +#define USB_VENDOR_ALPS 0x044e /* Alps Electric */ +#define USB_VENDOR_THRUST 0x044f /* Thrustmaster */ +#define USB_VENDOR_TI 0x0451 /* Texas Instruments */ +#define USB_VENDOR_ANALOGDEVICES 0x0456 /* Analog Devices */ +#define USB_VENDOR_SIS 0x0457 /* Silicon Integrated Systems Corp. */ +#define USB_VENDOR_KYE 0x0458 /* KYE Systems */ +#define USB_VENDOR_DIAMOND2 0x045a /* Diamond (Supra) */ +#define USB_VENDOR_RENESAS 0x045b /* Renesas */ +#define USB_VENDOR_MICROSOFT 0x045e /* Microsoft */ +#define USB_VENDOR_PRIMAX 0x0461 /* Primax Electronics */ +#define USB_VENDOR_MGE 0x0463 /* MGE UPS Systems */ +#define USB_VENDOR_AMP 0x0464 /* AMP */ +#define USB_VENDOR_CHERRY 0x046a /* Cherry Mikroschalter */ +#define USB_VENDOR_MEGATRENDS 0x046b /* American Megatrends */ +#define USB_VENDOR_LOGITECH 0x046d /* Logitech */ +#define USB_VENDOR_BTC 0x046e /* Behavior Tech. Computer */ +#define USB_VENDOR_PHILIPS 0x0471 /* Philips */ +#define USB_VENDOR_SUN2 0x0472 /* Sun Microsystems (offical) */ +#define USB_VENDOR_SANYO 0x0474 /* Sanyo Electric */ +#define USB_VENDOR_SEAGATE 0x0477 /* Seagate */ +#define USB_VENDOR_CONNECTIX 0x0478 /* Connectix */ +#define USB_VENDOR_SEMTECH 0x047a /* Semtech */ +#define USB_VENDOR_KENSINGTON 0x047d /* Kensington */ +#define USB_VENDOR_LUCENT 0x047e /* Lucent */ +#define USB_VENDOR_PLANTRONICS 0x047f /* Plantronics */ +#define USB_VENDOR_KYOCERA 0x0482 /* Kyocera Wireless Corp. */ +#define USB_VENDOR_STMICRO 0x0483 /* STMicroelectronics */ +#define USB_VENDOR_FOXCONN 0x0489 /* Foxconn */ +#define USB_VENDOR_YAMAHA 0x0499 /* YAMAHA */ +#define USB_VENDOR_COMPAQ 0x049f /* Compaq */ +#define USB_VENDOR_HITACHI 0x04a4 /* Hitachi */ +#define USB_VENDOR_ACERP 0x04a5 /* Acer Peripherals */ +#define USB_VENDOR_DAVICOM 0x04a6 /* Davicom */ +#define USB_VENDOR_VISIONEER 0x04a7 /* Visioneer */ +#define USB_VENDOR_CANON 0x04a9 /* Canon */ +#define USB_VENDOR_NIKON 0x04b0 /* Nikon */ +#define USB_VENDOR_PAN 0x04b1 /* Pan International */ +#define USB_VENDOR_IBM 0x04b3 /* IBM */ +#define USB_VENDOR_CYPRESS 0x04b4 /* Cypress Semiconductor */ +#define USB_VENDOR_ROHM 0x04b5 /* ROHM */ +#define USB_VENDOR_COMPAL 0x04b7 /* Compal */ +#define USB_VENDOR_EPSON 0x04b8 /* Seiko Epson */ +#define USB_VENDOR_RAINBOW 0x04b9 /* Rainbow Technologies */ +#define USB_VENDOR_IODATA 0x04bb /* I-O Data */ +#define USB_VENDOR_TDK 0x04bf /* TDK */ +#define USB_VENDOR_3COMUSR 0x04c1 /* U.S. Robotics */ +#define USB_VENDOR_METHODE 0x04c2 /* Methode Electronics Far East */ +#define USB_VENDOR_MAXISWITCH 0x04c3 /* Maxi Switch */ +#define USB_VENDOR_LOCKHEEDMER 0x04c4 /* Lockheed Martin Energy Research */ +#define USB_VENDOR_FUJITSU 0x04c5 /* Fujitsu */ +#define USB_VENDOR_TOSHIBAAM 0x04c6 /* Toshiba America */ +#define USB_VENDOR_MICROMACRO 0x04c7 /* Micro Macro Technologies */ +#define USB_VENDOR_KONICA 0x04c8 /* Konica */ +#define USB_VENDOR_LITEON 0x04ca /* Lite-On Technology */ +#define USB_VENDOR_FUJIPHOTO 0x04cb /* Fuji Photo Film */ +#define USB_VENDOR_PHILIPSSEMI 0x04cc /* Philips Semiconductors */ +#define USB_VENDOR_TATUNG 0x04cd /* Tatung Co. Of America */ +#define USB_VENDOR_SCANLOGIC 0x04ce /* ScanLogic */ +#define USB_VENDOR_MYSON 0x04cf /* Myson Technology */ +#define USB_VENDOR_DIGI2 0x04d0 /* Digi */ +#define USB_VENDOR_ITTCANON 0x04d1 /* ITT Canon */ +#define USB_VENDOR_ALTEC 0x04d2 /* Altec Lansing */ +#define USB_VENDOR_LSI 0x04d4 /* LSI */ +#define USB_VENDOR_MENTORGRAPHICS 0x04d6 /* Mentor Graphics */ +#define USB_VENDOR_ITUNERNET 0x04d8 /* I-Tuner Networks */ +#define USB_VENDOR_HOLTEK 0x04d9 /* Holtek Semiconductor, Inc. */ +#define USB_VENDOR_PANASONIC 0x04da /* Panasonic (Matsushita) */ +#define USB_VENDOR_HUANHSIN 0x04dc /* Huan Hsin */ +#define USB_VENDOR_SHARP 0x04dd /* Sharp */ +#define USB_VENDOR_IIYAMA 0x04e1 /* Iiyama */ +#define USB_VENDOR_SHUTTLE 0x04e6 /* Shuttle Technology */ +#define USB_VENDOR_ELO 0x04e7 /* Elo TouchSystems */ +#define USB_VENDOR_SAMSUNG 0x04e8 /* Samsung Electronics */ +#define USB_VENDOR_NORTHSTAR 0x04eb /* Northstar */ +#define USB_VENDOR_TOKYOELECTRON 0x04ec /* Tokyo Electron */ +#define USB_VENDOR_ANNABOOKS 0x04ed /* Annabooks */ +#define USB_VENDOR_JVC 0x04f1 /* JVC */ +#define USB_VENDOR_CHICONY 0x04f2 /* Chicony Electronics */ +#define USB_VENDOR_ELAN 0x04f3 /* Elan */ +#define USB_VENDOR_NEWNEX 0x04f7 /* Newnex */ +#define USB_VENDOR_BROTHER 0x04f9 /* Brother Industries */ +#define USB_VENDOR_DALLAS 0x04fa /* Dallas Semiconductor */ +#define USB_VENDOR_SUNPLUS 0x04fc /* Sunplus */ +#define USB_VENDOR_PFU 0x04fe /* PFU */ +#define USB_VENDOR_FUJIKURA 0x0501 /* Fujikura/DDK */ +#define USB_VENDOR_ACER 0x0502 /* Acer */ +#define USB_VENDOR_3COM 0x0506 /* 3Com */ +#define USB_VENDOR_HOSIDEN 0x0507 /* Hosiden Corporation */ +#define USB_VENDOR_AZTECH 0x0509 /* Aztech Systems */ +#define USB_VENDOR_BELKIN 0x050d /* Belkin Components */ +#define USB_VENDOR_KAWATSU 0x050f /* Kawatsu Semiconductor */ +#define USB_VENDOR_FCI 0x0514 /* FCI */ +#define USB_VENDOR_LONGWELL 0x0516 /* Longwell */ +#define USB_VENDOR_COMPOSITE 0x0518 /* Composite */ +#define USB_VENDOR_STAR 0x0519 /* Star Micronics */ +#define USB_VENDOR_APC 0x051d /* American Power Conversion */ +#define USB_VENDOR_SCIATLANTA 0x051e /* Scientific Atlanta */ +#define USB_VENDOR_TSM 0x0520 /* TSM */ +#define USB_VENDOR_CONNECTEK 0x0522 /* Advanced Connectek USA */ +#define USB_VENDOR_NETCHIP 0x0525 /* NetChip Technology */ +#define USB_VENDOR_ALTRA 0x0527 /* ALTRA */ +#define USB_VENDOR_ATI 0x0528 /* ATI Technologies */ +#define USB_VENDOR_AKS 0x0529 /* Aladdin Knowledge Systems */ +#define USB_VENDOR_TEKOM 0x052b /* Tekom */ +#define USB_VENDOR_CANONDEV 0x052c /* Canon */ +#define USB_VENDOR_WACOMTECH 0x0531 /* Wacom */ +#define USB_VENDOR_INVENTEC 0x0537 /* Inventec */ +#define USB_VENDOR_SHYHSHIUN 0x0539 /* Shyh Shiun Terminals */ +#define USB_VENDOR_PREHWERKE 0x053a /* Preh Werke Gmbh & Co. KG */ +#define USB_VENDOR_SYNOPSYS 0x053f /* Synopsys */ +#define USB_VENDOR_UNIACCESS 0x0540 /* Universal Access */ +#define USB_VENDOR_VIEWSONIC 0x0543 /* ViewSonic */ +#define USB_VENDOR_XIRLINK 0x0545 /* Xirlink */ +#define USB_VENDOR_ANCHOR 0x0547 /* Anchor Chips */ +#define USB_VENDOR_SONY 0x054c /* Sony */ +#define USB_VENDOR_FUJIXEROX 0x0550 /* Fuji Xerox */ +#define USB_VENDOR_VISION 0x0553 /* VLSI Vision */ +#define USB_VENDOR_ASAHIKASEI 0x0556 /* Asahi Kasei Microsystems */ +#define USB_VENDOR_ATEN 0x0557 /* ATEN International */ +#define USB_VENDOR_SAMSUNG2 0x055d /* Samsung Electronics */ +#define USB_VENDOR_MUSTEK 0x055f /* Mustek Systems */ +#define USB_VENDOR_TELEX 0x0562 /* Telex Communications */ +#define USB_VENDOR_CHINON 0x0564 /* Chinon */ +#define USB_VENDOR_PERACOM 0x0565 /* Peracom Networks */ +#define USB_VENDOR_ALCOR2 0x0566 /* Alcor Micro */ +#define USB_VENDOR_XYRATEX 0x0567 /* Xyratex */ +#define USB_VENDOR_WACOM 0x056a /* WACOM */ +#define USB_VENDOR_ETEK 0x056c /* e-TEK Labs */ +#define USB_VENDOR_EIZO 0x056d /* EIZO */ +#define USB_VENDOR_ELECOM 0x056e /* Elecom */ +#define USB_VENDOR_CONEXANT 0x0572 /* Conexant */ +#define USB_VENDOR_HAUPPAUGE 0x0573 /* Hauppauge Computer Works */ +#define USB_VENDOR_BAFO 0x0576 /* BAFO/Quality Computer Accessories */ +#define USB_VENDOR_YEDATA 0x057b /* Y-E Data */ +#define USB_VENDOR_AVM 0x057c /* AVM */ +#define USB_VENDOR_QUICKSHOT 0x057f /* Quickshot */ +#define USB_VENDOR_ROLAND 0x0582 /* Roland */ +#define USB_VENDOR_ROCKFIRE 0x0583 /* Rockfire */ +#define USB_VENDOR_RATOC 0x0584 /* RATOC Systems */ +#define USB_VENDOR_ZYXEL 0x0586 /* ZyXEL Communication */ +#define USB_VENDOR_INFINEON 0x058b /* Infineon */ +#define USB_VENDOR_MICREL 0x058d /* Micrel */ +#define USB_VENDOR_ALCOR 0x058f /* Alcor Micro */ +#define USB_VENDOR_OMRON 0x0590 /* OMRON */ +#define USB_VENDOR_ZORAN 0x0595 /* Zoran Microelectronics */ +#define USB_VENDOR_NIIGATA 0x0598 /* Niigata */ +#define USB_VENDOR_IOMEGA 0x059b /* Iomega */ +#define USB_VENDOR_ATREND 0x059c /* A-Trend Technology */ +#define USB_VENDOR_AID 0x059d /* Advanced Input Devices */ +#define USB_VENDOR_LACIE 0x059f /* LaCie */ +#define USB_VENDOR_FUJIFILM 0x05a2 /* Fuji Film */ +#define USB_VENDOR_ARC 0x05a3 /* ARC */ +#define USB_VENDOR_ORTEK 0x05a4 /* Ortek */ +#define USB_VENDOR_BOSE 0x05a7 /* Bose */ +#define USB_VENDOR_OMNIVISION 0x05a9 /* OmniVision */ +#define USB_VENDOR_INSYSTEM 0x05ab /* In-System Design */ +#define USB_VENDOR_APPLE 0x05ac /* Apple Computer */ +#define USB_VENDOR_YCCABLE 0x05ad /* Y.C. Cable */ +#define USB_VENDOR_DIGITALPERSONA 0x05ba /* DigitalPersona */ +#define USB_VENDOR_3G 0x05bc /* 3G Green Green Globe */ +#define USB_VENDOR_RAFI 0x05bd /* RAFI */ +#define USB_VENDOR_TYCO 0x05be /* Tyco */ +#define USB_VENDOR_KAWASAKI 0x05c1 /* Kawasaki */ +#define USB_VENDOR_DIGI 0x05c5 /* Digi International */ +#define USB_VENDOR_QUALCOMM2 0x05c6 /* Qualcomm */ +#define USB_VENDOR_QTRONIX 0x05c7 /* Qtronix */ +#define USB_VENDOR_FOXLINK 0x05c8 /* Foxlink */ +#define USB_VENDOR_RICOH 0x05ca /* Ricoh */ +#define USB_VENDOR_ELSA 0x05cc /* ELSA */ +#define USB_VENDOR_SCIWORX 0x05ce /* sci-worx */ +#define USB_VENDOR_BRAINBOXES 0x05d1 /* Brainboxes Limited */ +#define USB_VENDOR_ULTIMA 0x05d8 /* Ultima */ +#define USB_VENDOR_AXIOHM 0x05d9 /* Axiohm Transaction Solutions */ +#define USB_VENDOR_MICROTEK 0x05da /* Microtek */ +#define USB_VENDOR_SUNTAC 0x05db /* SUN Corporation */ +#define USB_VENDOR_LEXAR 0x05dc /* Lexar Media */ +#define USB_VENDOR_ADDTRON 0x05dd /* Addtron */ +#define USB_VENDOR_SYMBOL 0x05e0 /* Symbol Technologies */ +#define USB_VENDOR_SYNTEK 0x05e1 /* Syntek */ +#define USB_VENDOR_GENESYS 0x05e3 /* Genesys Logic */ +#define USB_VENDOR_FUJI 0x05e5 /* Fuji Electric */ +#define USB_VENDOR_KEITHLEY 0x05e6 /* Keithley Instruments */ +#define USB_VENDOR_EIZONANAO 0x05e7 /* EIZO Nanao */ +#define USB_VENDOR_KLSI 0x05e9 /* Kawasaki LSI */ +#define USB_VENDOR_FFC 0x05eb /* FFC */ +#define USB_VENDOR_ANKO 0x05ef /* Anko Electronic */ +#define USB_VENDOR_PIENGINEERING 0x05f3 /* P.I. Engineering */ +#define USB_VENDOR_AOC 0x05f6 /* AOC International */ +#define USB_VENDOR_CHIC 0x05fe /* Chic Technology */ +#define USB_VENDOR_BARCO 0x0600 /* Barco Display Systems */ +#define USB_VENDOR_BRIDGE 0x0607 /* Bridge Information */ +#define USB_VENDOR_SOLIDYEAR 0x060b /* Solid Year */ +#define USB_VENDOR_BIORAD 0x0614 /* Bio-Rad Laboratories */ +#define USB_VENDOR_MACALLY 0x0618 /* Macally */ +#define USB_VENDOR_ACTLABS 0x061c /* Act Labs */ +#define USB_VENDOR_ALARIS 0x0620 /* Alaris */ +#define USB_VENDOR_APEX 0x0624 /* Apex */ +#define USB_VENDOR_CREATIVE3 0x062a /* Creative Labs */ +#define USB_VENDOR_VIVITAR 0x0636 /* Vivitar */ +#define USB_VENDOR_GUNZE 0x0637 /* Gunze Electronics USA */ +#define USB_VENDOR_AVISION 0x0638 /* Avision */ +#define USB_VENDOR_TEAC 0x0644 /* TEAC */ +#define USB_VENDOR_SGI 0x065e /* Silicon Graphics */ +#define USB_VENDOR_SANWASUPPLY 0x0663 /* Sanwa Supply */ +#define USB_VENDOR_LINKSYS 0x066b /* Linksys */ +#define USB_VENDOR_ACERSA 0x066e /* Acer Semiconductor America */ +#define USB_VENDOR_SIGMATEL 0x066f /* Sigmatel */ +#define USB_VENDOR_DRAYTEK 0x0675 /* DrayTek */ +#define USB_VENDOR_AIWA 0x0677 /* Aiwa */ +#define USB_VENDOR_ACARD 0x0678 /* ACARD Technology */ +#define USB_VENDOR_PROLIFIC 0x067b /* Prolific Technology */ +#define USB_VENDOR_SIEMENS 0x067c /* Siemens */ +#define USB_VENDOR_AVANCELOGIC 0x0680 /* Avance Logic */ +#define USB_VENDOR_SIEMENS2 0x0681 /* Siemens */ +#define USB_VENDOR_MINOLTA 0x0686 /* Minolta */ +#define USB_VENDOR_CHPRODUCTS 0x068e /* CH Products */ +#define USB_VENDOR_HAGIWARA 0x0693 /* Hagiwara Sys-Com */ +#define USB_VENDOR_CTX 0x0698 /* Chuntex */ +#define USB_VENDOR_ASKEY 0x069a /* Askey Computer */ +#define USB_VENDOR_SAITEK 0x06a3 /* Saitek */ +#define USB_VENDOR_ALCATELT 0x06b9 /* Alcatel Telecom */ +#define USB_VENDOR_AGFA 0x06bd /* AGFA-Gevaert */ +#define USB_VENDOR_ASIAMD 0x06be /* Asia Microelectronic Development */ +#define USB_VENDOR_BIZLINK 0x06c4 /* Bizlink International */ +#define USB_VENDOR_KEYSPAN 0x06cd /* Keyspan / InnoSys Inc. */ +#define USB_VENDOR_AASHIMA 0x06d6 /* Aashima Technology */ +#define USB_VENDOR_MULTITECH 0x06e0 /* MultiTech */ +#define USB_VENDOR_ADS 0x06e1 /* ADS Technologies */ +#define USB_VENDOR_ALCATELM 0x06e4 /* Alcatel Microelectronics */ +#define USB_VENDOR_SIRIUS 0x06ea /* Sirius Technologies */ +#define USB_VENDOR_GUILLEMOT 0x06f8 /* Guillemot */ +#define USB_VENDOR_BOSTON 0x06fd /* Boston Acoustics */ +#define USB_VENDOR_SMC 0x0707 /* Standard Microsystems */ +#define USB_VENDOR_PUTERCOM 0x0708 /* Putercom */ +#define USB_VENDOR_MCT 0x0711 /* MCT */ +#define USB_VENDOR_IMATION 0x0718 /* Imation */ +#define USB_VENDOR_SONYERICSSON 0x0731 /* Sony Ericsson */ +#define USB_VENDOR_EICON 0x0734 /* Eicon Networks */ +#define USB_VENDOR_SYNTECH 0x0745 /* Syntech Information */ +#define USB_VENDOR_DIGITALSTREAM 0x074e /* Digital Stream */ +#define USB_VENDOR_AUREAL 0x0755 /* Aureal Semiconductor */ +#define USB_VENDOR_MIDIMAN 0x0763 /* Midiman */ +#define USB_VENDOR_CYBERPOWER 0x0764 /* Cyber Power Systems, Inc. */ +#define USB_VENDOR_SURECOM 0x0769 /* Surecom Technology */ +#define USB_VENDOR_LINKSYS2 0x077b /* Linksys */ +#define USB_VENDOR_GRIFFIN 0x077d /* Griffin Technology */ +#define USB_VENDOR_SANDISK 0x0781 /* SanDisk */ +#define USB_VENDOR_JENOPTIK 0x0784 /* Jenoptik */ +#define USB_VENDOR_LOGITEC 0x0789 /* Logitec */ +#define USB_VENDOR_BRIMAX 0x078e /* Brimax */ +#define USB_VENDOR_AXIS 0x0792 /* Axis Communications */ +#define USB_VENDOR_ABL 0x0794 /* ABL Electronics */ +#define USB_VENDOR_SAGEM 0x079b /* Sagem */ +#define USB_VENDOR_SUNCOMM 0x079c /* Sun Communications, Inc. */ +#define USB_VENDOR_ALFADATA 0x079d /* Alfadata Computer */ +#define USB_VENDOR_NATIONALTECH 0x07a2 /* National Technical Systems */ +#define USB_VENDOR_ONNTO 0x07a3 /* Onnto */ +#define USB_VENDOR_BE 0x07a4 /* Be */ +#define USB_VENDOR_ADMTEK 0x07a6 /* ADMtek */ +#define USB_VENDOR_COREGA 0x07aa /* Corega */ +#define USB_VENDOR_FREECOM 0x07ab /* Freecom */ +#define USB_VENDOR_MICROTECH 0x07af /* Microtech */ +#define USB_VENDOR_GENERALINSTMNTS 0x07b2 /* General Instruments (Motorola) */ +#define USB_VENDOR_OLYMPUS 0x07b4 /* Olympus */ +#define USB_VENDOR_ABOCOM 0x07b8 /* AboCom Systems */ +#define USB_VENDOR_KEISOKUGIKEN 0x07c1 /* Keisokugiken */ +#define USB_VENDOR_ONSPEC 0x07c4 /* OnSpec */ +#define USB_VENDOR_APG 0x07c5 /* APG Cash Drawer */ +#define USB_VENDOR_BUG 0x07c8 /* B.U.G. */ +#define USB_VENDOR_ALLIEDTELESYN 0x07c9 /* Allied Telesyn International */ +#define USB_VENDOR_AVERMEDIA 0x07ca /* AVerMedia Technologies */ +#define USB_VENDOR_SIIG 0x07cc /* SIIG */ +#define USB_VENDOR_CASIO 0x07cf /* CASIO */ +#define USB_VENDOR_DLINK2 0x07d1 /* D-Link */ +#define USB_VENDOR_APTIO 0x07d2 /* Aptio Products */ +#define USB_VENDOR_ARASAN 0x07da /* Arasan Chip Systems */ +#define USB_VENDOR_ALLIEDCABLE 0x07e6 /* Allied Cable */ +#define USB_VENDOR_STSN 0x07ef /* STSN */ +#define USB_VENDOR_CENTURY 0x07f7 /* Century Corp */ +#define USB_VENDOR_ZOOM 0x0803 /* Zoom Telephonics */ +#define USB_VENDOR_PCS 0x0810 /* Personal Communication Systems */ +#define USB_VENDOR_BROADLOGIC 0x0827 /* BroadLogic */ +#define USB_VENDOR_HANDSPRING 0x082d /* Handspring */ +#define USB_VENDOR_PALM 0x0830 /* Palm Computing */ +#define USB_VENDOR_SOURCENEXT 0x0833 /* SOURCENEXT */ +#define USB_VENDOR_ACTIONSTAR 0x0835 /* Action Star Enterprise */ +#define USB_VENDOR_SAMSUNG_TECHWIN 0x0839 /* Samsung Techwin */ +#define USB_VENDOR_ACCTON 0x083a /* Accton Technology */ +#define USB_VENDOR_DIAMOND 0x0841 /* Diamond */ +#define USB_VENDOR_NETGEAR 0x0846 /* BayNETGEAR */ +#define USB_VENDOR_TOPRE 0x0853 /* Topre Corporation */ +#define USB_VENDOR_ACTIVEWIRE 0x0854 /* ActiveWire */ +#define USB_VENDOR_BBELECTRONICS 0x0856 /* B&B Electronics */ +#define USB_VENDOR_PORTGEAR 0x085a /* PortGear */ +#define USB_VENDOR_NETGEAR2 0x0864 /* Netgear */ +#define USB_VENDOR_SYSTEMTALKS 0x086e /* System Talks */ +#define USB_VENDOR_METRICOM 0x0870 /* Metricom */ +#define USB_VENDOR_ADESSOKBTEK 0x087c /* ADESSO/Kbtek America */ +#define USB_VENDOR_JATON 0x087d /* Jaton */ +#define USB_VENDOR_APT 0x0880 /* APT Technologies */ +#define USB_VENDOR_BOCARESEARCH 0x0885 /* Boca Research */ +#define USB_VENDOR_ANDREA 0x08a8 /* Andrea Electronics */ +#define USB_VENDOR_BURRBROWN 0x08bb /* Burr-Brown Japan */ +#define USB_VENDOR_2WIRE 0x08c8 /* 2Wire */ +#define USB_VENDOR_AIPTEK 0x08ca /* AIPTEK International */ +#define USB_VENDOR_SMARTBRIDGES 0x08d1 /* SmartBridges */ +#define USB_VENDOR_BILLIONTON 0x08dd /* Billionton Systems */ +#define USB_VENDOR_EXTENDED 0x08e9 /* Extended Systems */ +#define USB_VENDOR_MSYSTEMS 0x08ec /* M-Systems */ +#define USB_VENDOR_AUTHENTEC 0x08ff /* AuthenTec */ +#define USB_VENDOR_AUDIOTECHNICA 0x0909 /* Audio-Technica */ +#define USB_VENDOR_TRUMPION 0x090a /* Trumpion Microelectronics */ +#define USB_VENDOR_FEIYA 0x090c /* Feiya */ +#define USB_VENDOR_ALATION 0x0910 /* Alation Systems */ +#define USB_VENDOR_GLOBESPAN 0x0915 /* Globespan */ +#define USB_VENDOR_CONCORDCAMERA 0x0919 /* Concord Camera */ +#define USB_VENDOR_GARMIN 0x091e /* Garmin International */ +#define USB_VENDOR_GOHUBS 0x0921 /* GoHubs */ +#define USB_VENDOR_XEROX 0x0924 /* Xerox */ +#define USB_VENDOR_BIOMETRIC 0x0929 /* American Biometric Company */ +#define USB_VENDOR_TOSHIBA 0x0930 /* Toshiba */ +#define USB_VENDOR_PLEXTOR 0x093b /* Plextor */ +#define USB_VENDOR_INTREPIDCS 0x093c /* Intrepid */ +#define USB_VENDOR_YANO 0x094f /* Yano */ +#define USB_VENDOR_KINGSTON 0x0951 /* Kingston Technology */ +#define USB_VENDOR_BLUEWATER 0x0956 /* BlueWater Systems */ +#define USB_VENDOR_AGILENT 0x0957 /* Agilent Technologies */ +#define USB_VENDOR_GUDE 0x0959 /* Gude ADS */ +#define USB_VENDOR_PORTSMITH 0x095a /* Portsmith */ +#define USB_VENDOR_ACERW 0x0967 /* Acer */ +#define USB_VENDOR_ADIRONDACK 0x0976 /* Adirondack Wire & Cable */ +#define USB_VENDOR_BECKHOFF 0x0978 /* Beckhoff */ +#define USB_VENDOR_MINDSATWORK 0x097a /* Minds At Work */ +#define USB_VENDOR_POINTCHIPS 0x09a6 /* PointChips */ +#define USB_VENDOR_INTERSIL 0x09aa /* Intersil */ +#define USB_VENDOR_ALTIUS 0x09b3 /* Altius Solutions */ +#define USB_VENDOR_ARRIS 0x09c1 /* Arris Interactive */ +#define USB_VENDOR_ACTIVCARD 0x09c3 /* ACTIVCARD */ +#define USB_VENDOR_ACTISYS 0x09c4 /* ACTiSYS */ +#define USB_VENDOR_NOVATEL2 0x09d7 /* Novatel Wireless */ +#define USB_VENDOR_AFOURTECH 0x09da /* A-FOUR TECH */ +#define USB_VENDOR_AIMEX 0x09dc /* AIMEX */ +#define USB_VENDOR_ADDONICS 0x09df /* Addonics Technologies */ +#define USB_VENDOR_AKAI 0x09e8 /* AKAI professional M.I. */ +#define USB_VENDOR_ARESCOM 0x09f5 /* ARESCOM */ +#define USB_VENDOR_BAY 0x09f9 /* Bay Associates */ +#define USB_VENDOR_ALTERA 0x09fb /* Altera */ +#define USB_VENDOR_CSR 0x0a12 /* Cambridge Silicon Radio */ +#define USB_VENDOR_TREK 0x0a16 /* Trek Technology */ +#define USB_VENDOR_ASAHIOPTICAL 0x0a17 /* Asahi Optical */ +#define USB_VENDOR_BOCASYSTEMS 0x0a43 /* Boca Systems */ +#define USB_VENDOR_SHANTOU 0x0a46 /* ShanTou */ +#define USB_VENDOR_MEDIAGEAR 0x0a48 /* MediaGear */ +#define USB_VENDOR_BROADCOM 0x0a5c /* Broadcom */ +#define USB_VENDOR_GREENHOUSE 0x0a6b /* GREENHOUSE */ +#define USB_VENDOR_GEOCAST 0x0a79 /* Geocast Network Systems */ +#define USB_VENDOR_IDQUANTIQUE 0x0aba /* id Quantique */ +#define USB_VENDOR_ZYDAS 0x0ace /* Zydas Technology Corporation */ +#define USB_VENDOR_NEODIO 0x0aec /* Neodio */ +#define USB_VENDOR_OPTION 0x0af0 /* Option N.V: */ +#define USB_VENDOR_ASUS 0x0b05 /* ASUSTeK Computer */ +#define USB_VENDOR_TODOS 0x0b0c /* Todos Data System */ +#define USB_VENDOR_SIIG2 0x0b39 /* SIIG */ +#define USB_VENDOR_TEKRAM 0x0b3b /* Tekram Technology */ +#define USB_VENDOR_HAL 0x0b41 /* HAL Corporation */ +#define USB_VENDOR_EMS 0x0b43 /* EMS Production */ +#define USB_VENDOR_NEC2 0x0b62 /* NEC */ +#define USB_VENDOR_ATI2 0x0b6f /* ATI */ +#define USB_VENDOR_ZEEVO 0x0b7a /* Zeevo, Inc. */ +#define USB_VENDOR_KURUSUGAWA 0x0b7e /* Kurusugawa Electronics, Inc. */ +#define USB_VENDOR_ASIX 0x0b95 /* ASIX Electronics */ +#define USB_VENDOR_O2MICRO 0x0b97 /* O2 Micro, Inc. */ +#define USB_VENDOR_USR 0x0baf /* U.S. Robotics */ +#define USB_VENDOR_AMBIT 0x0bb2 /* Ambit Microsystems */ +#define USB_VENDOR_HTC 0x0bb4 /* HTC */ +#define USB_VENDOR_REALTEK 0x0bda /* Realtek */ +#define USB_VENDOR_ADDONICS2 0x0bf6 /* Addonics Technology */ +#define USB_VENDOR_FSC 0x0bf8 /* Fujitsu Siemens Computers */ +#define USB_VENDOR_AGATE 0x0c08 /* Agate Technologies */ +#define USB_VENDOR_DMI 0x0c0b /* DMI */ +#define USB_VENDOR_MICRODIA 0x0c45 /* Chicony */ +#define USB_VENDOR_SEALEVEL 0x0c52 /* Sealevel System */ +#define USB_VENDOR_LUWEN 0x0c76 /* Luwen */ +#define USB_VENDOR_KYOCERA2 0x0c88 /* Kyocera Wireless Corp. */ +#define USB_VENDOR_ZCOM 0x0cde /* Z-Com */ +#define USB_VENDOR_ATHEROS2 0x0cf3 /* Atheros Communications */ +#define USB_VENDOR_TANGTOP 0x0d3d /* Tangtop */ +#define USB_VENDOR_SMC3 0x0d5c /* Standard Microsystems */ +#define USB_VENDOR_ADDON 0x0d7d /* Add-on Technology */ +#define USB_VENDOR_ACDC 0x0d7e /* American Computer & Digital Components */ +#define USB_VENDOR_ABC 0x0d8c /* ABC */ +#define USB_VENDOR_CONCEPTRONIC 0x0d8e /* Conceptronic */ +#define USB_VENDOR_SKANHEX 0x0d96 /* Skanhex Technology, Inc. */ +#define USB_VENDOR_MSI 0x0db0 /* Micro Star International */ +#define USB_VENDOR_ELCON 0x0db7 /* ELCON Systemtechnik */ +#define USB_VENDOR_NETAC 0x0dd8 /* Netac */ +#define USB_VENDOR_SITECOMEU 0x0df6 /* Sitecom Europe */ +#define USB_VENDOR_MOBILEACTION 0x0df7 /* Mobile Action */ +#define USB_VENDOR_SPEEDDRAGON 0x0e55 /* Speed Dragon Multimedia */ +#define USB_VENDOR_HAWKING 0x0e66 /* Hawking */ +#define USB_VENDOR_FOSSIL 0x0e67 /* Fossil, Inc */ +#define USB_VENDOR_GMATE 0x0e7e /* G.Mate, Inc */ +#define USB_VENDOR_OTI 0x0ea0 /* Ours Technology */ +#define USB_VENDOR_PILOTECH 0x0eaf /* Pilotech */ +#define USB_VENDOR_NOVATECH 0x0eb0 /* NovaTech */ +#define USB_VENDOR_ITEGNO 0x0eba /* iTegno */ +#define USB_VENDOR_WINMAXGROUP 0x0ed1 /* WinMaxGroup */ +#define USB_VENDOR_TOD 0x0ede /* TOD */ +#define USB_VENDOR_EGALAX 0x0eef /* eGalax, Inc. */ +#define USB_VENDOR_AIRPRIME 0x0f3d /* AirPrime, Inc. */ +#define USB_VENDOR_MICROTUNE 0x0f4d /* Microtune */ +#define USB_VENDOR_VTECH 0x0f88 /* VTech */ +#define USB_VENDOR_FALCOM 0x0f94 /* Falcom Wireless Communications GmbH */ +#define USB_VENDOR_RIM 0x0fca /* Research In Motion */ +#define USB_VENDOR_DYNASTREAM 0x0fcf /* Dynastream Innovations */ +#define USB_VENDOR_QUALCOMM 0x1004 /* Qualcomm */ +#define USB_VENDOR_DESKNOTE 0x1019 /* Desknote */ +#define USB_VENDOR_GIGABYTE 0x1044 /* GIGABYTE */ +#define USB_VENDOR_WESTERN 0x1058 /* Western Digital */ +#define USB_VENDOR_MOTOROLA 0x1063 /* Motorola */ +#define USB_VENDOR_CCYU 0x1065 /* CCYU Technology */ +#define USB_VENDOR_CURITEL 0x106c /* Curitel Communications Inc */ +#define USB_VENDOR_SILABS2 0x10a6 /* SILABS2 */ +#define USB_VENDOR_USI 0x10ab /* USI */ +#define USB_VENDOR_PLX 0x10b5 /* PLX */ +#define USB_VENDOR_ASANTE 0x10bd /* Asante */ +#define USB_VENDOR_SILABS 0x10c4 /* Silicon Labs */ +#define USB_VENDOR_ANALOG 0x1110 /* Analog Devices */ +#define USB_VENDOR_TENX 0x1130 /* Ten X Technology, Inc. */ +#define USB_VENDOR_ISSC 0x1131 /* Integrated System Solution Corp. */ +#define USB_VENDOR_JRC 0x1145 /* Japan Radio Company */ +#define USB_VENDOR_SPHAIRON 0x114b /* Sphairon Access Systems GmbH */ +#define USB_VENDOR_DELORME 0x1163 /* DeLorme */ +#define USB_VENDOR_SERVERWORKS 0x1166 /* ServerWorks */ +#define USB_VENDOR_ACERCM 0x1189 /* Acer Communications & Multimedia */ +#define USB_VENDOR_SIERRA 0x1199 /* Sierra Wireless */ +#define USB_VENDOR_TOPFIELD 0x11db /* Topfield Co., Ltd */ +#define USB_VENDOR_SIEMENS3 0x11f5 /* Siemens */ +#define USB_VENDOR_PROLIFIC2 0x11f6 /* Prolific */ +#define USB_VENDOR_ALCATEL 0x11f7 /* Alcatel */ +#define USB_VENDOR_UNKNOWN3 0x1233 /* Unknown vendor */ +#define USB_VENDOR_TSUNAMI 0x1241 /* Tsunami */ +#define USB_VENDOR_PHEENET 0x124a /* Pheenet */ +#define USB_VENDOR_TARGUS 0x1267 /* Targus */ +#define USB_VENDOR_TWINMOS 0x126f /* TwinMOS */ +#define USB_VENDOR_TENDA 0x1286 /* Tenda */ +#define USB_VENDOR_CREATIVE2 0x1292 /* Creative Labs */ +#define USB_VENDOR_BELKIN2 0x1293 /* Belkin Components */ +#define USB_VENDOR_CYBERTAN 0x129b /* CyberTAN Technology */ +#define USB_VENDOR_HUAWEI 0x12d1 /* Huawei Technologies */ +#define USB_VENDOR_ARANEUS 0x12d8 /* Araneus Information Systems */ +#define USB_VENDOR_TAPWAVE 0x12ef /* Tapwave */ +#define USB_VENDOR_AINCOMM 0x12fd /* Aincomm */ +#define USB_VENDOR_MOBILITY 0x1342 /* Mobility */ +#define USB_VENDOR_DICKSMITH 0x1371 /* Dick Smith Electronics */ +#define USB_VENDOR_NETGEAR3 0x1385 /* Netgear */ +#define USB_VENDOR_BALTECH 0x13ad /* Baltech */ +#define USB_VENDOR_CISCOLINKSYS 0x13b1 /* Cisco-Linksys */ +#define USB_VENDOR_SHARK 0x13d2 /* Shark */ +#define USB_VENDOR_NOVATEL 0x1410 /* Novatel Wireless */ +#define USB_VENDOR_MERLIN 0x1416 /* Merlin */ +#define USB_VENDOR_WISTRONNEWEB 0x1435 /* Wistron NeWeb */ +#define USB_VENDOR_RADIOSHACK 0x1453 /* Radio Shack */ +#define USB_VENDOR_HUAWEI3COM 0x1472 /* Huawei-3Com */ +#define USB_VENDOR_SILICOM 0x1485 /* Silicom */ +#define USB_VENDOR_RALINK 0x148f /* Ralink Technology */ +#define USB_VENDOR_IMAGINATION 0x149a /* Imagination Technologies */ +#define USB_VENDOR_CONCEPTRONIC2 0x14b2 /* Conceptronic */ +#define USB_VENDOR_PLANEX3 0x14ea /* Planex Communications */ +#define USB_VENDOR_SILICONPORTALS 0x1527 /* Silicon Portals */ +#define USB_VENDOR_UBIQUAM 0x1529 /* UBIQUAM Co., Ltd. */ +#define USB_VENDOR_UBLOX 0x1546 /* U-blox */ +#define USB_VENDOR_PNY 0x154b /* PNY */ +#define USB_VENDOR_OQO 0x1557 /* OQO */ +#define USB_VENDOR_UMEDIA 0x157e /* U-MEDIA Communications */ +#define USB_VENDOR_FIBERLINE 0x1582 /* Fiberline */ +#define USB_VENDOR_SPARKLAN 0x15a9 /* SparkLAN */ +#define USB_VENDOR_SOHOWARE 0x15e8 /* SOHOware */ +#define USB_VENDOR_UMAX 0x1606 /* UMAX Data Systems */ +#define USB_VENDOR_INSIDEOUT 0x1608 /* Inside Out Networks */ +#define USB_VENDOR_GOODWAY 0x1631 /* Good Way Technology */ +#define USB_VENDOR_ENTREGA 0x1645 /* Entrega */ +#define USB_VENDOR_ACTIONTEC 0x1668 /* Actiontec Electronics */ +#define USB_VENDOR_ATHEROS 0x168c /* Atheros Communications */ +#define USB_VENDOR_GIGASET 0x1690 /* Gigaset */ +#define USB_VENDOR_GLOBALSUN 0x16ab /* Global Sun Technology */ +#define USB_VENDOR_ANYDATA 0x16d5 /* AnyDATA Corporation */ +#define USB_VENDOR_JABLOTRON 0x16d6 /* Jablotron */ +#define USB_VENDOR_CMOTECH 0x16d8 /* CMOTECH Co., Ltd. */ +#define USB_VENDOR_AXESSTEL 0x1726 /* Axesstel Co., Ltd. */ +#define USB_VENDOR_LINKSYS4 0x1737 /* Linksys */ +#define USB_VENDOR_SENAO 0x1740 /* Senao */ +#define USB_VENDOR_METAGEEK 0x1781 /* MetaGeek */ +#define USB_VENDOR_AMIT 0x18c5 /* AMIT */ +#define USB_VENDOR_QCOM 0x18e8 /* Qcom */ +#define USB_VENDOR_LINKSYS3 0x1915 /* Linksys */ +#define USB_VENDOR_QUALCOMMINC 0x19d2 /* Qualcomm, Incorporated */ +#define USB_VENDOR_DLINK 0x2001 /* D-Link */ +#define USB_VENDOR_PLANEX2 0x2019 /* Planex Communications */ +#define USB_VENDOR_ERICSSON 0x2282 /* Ericsson */ +#define USB_VENDOR_MOTOROLA2 0x22b8 /* Motorola */ +#define USB_VENDOR_TRIPPLITE 0x2478 /* Tripp-Lite */ +#define USB_VENDOR_HIROSE 0x2631 /* Hirose Electric */ +#define USB_VENDOR_NHJ 0x2770 /* NHJ */ +#define USB_VENDOR_PLANEX 0x2c02 /* Planex Communications */ +#define USB_VENDOR_VIDZMEDIA 0x3275 /* VidzMedia Pte Ltd */ +#define USB_VENDOR_AEI 0x3334 /* AEI */ +#define USB_VENDOR_HANK 0x3353 /* Hank Connection */ +#define USB_VENDOR_PQI 0x3538 /* PQI */ +#define USB_VENDOR_DAISY 0x3579 /* Daisy Technology */ +#define USB_VENDOR_NI 0x3923 /* National Instruments */ +#define USB_VENDOR_MICRONET 0x3980 /* Micronet Communications */ +#define USB_VENDOR_IODATA2 0x40bb /* I-O Data */ +#define USB_VENDOR_IRIVER 0x4102 /* iRiver */ +#define USB_VENDOR_DELL 0x413c /* Dell */ +#define USB_VENDOR_WCH 0x4348 /* QinHeng Electronics */ +#define USB_VENDOR_ACEECA 0x4766 /* Aceeca */ +#define USB_VENDOR_AVERATEC 0x50c2 /* Averatec */ +#define USB_VENDOR_SWEEX 0x5173 /* Sweex */ +#define USB_VENDOR_ONSPEC2 0x55aa /* OnSpec Electronic Inc. */ +#define USB_VENDOR_ZINWELL 0x5a57 /* Zinwell */ +#define USB_VENDOR_SITECOM 0x6189 /* Sitecom */ +#define USB_VENDOR_ARKMICRO 0x6547 /* Arkmicro Technologies Inc. */ +#define USB_VENDOR_3COM2 0x6891 /* 3Com */ +#define USB_VENDOR_INTEL 0x8086 /* Intel */ +#define USB_VENDOR_SITECOM2 0x9016 /* Sitecom */ +#define USB_VENDOR_MOSCHIP 0x9710 /* MosChip Semiconductor */ +#define USB_VENDOR_3COM3 0xa727 /* 3Com */ +#define USB_VENDOR_HP2 0xf003 /* Hewlett Packard */ +#define USB_VENDOR_USRP 0xfffe /* GNU Radio USRP */ + +/* + * List of known products. Grouped by vendor. + */ + +/* 3Com products */ +#define USB_PRODUCT_3COM_HOMECONN 0x009d /* HomeConnect Camera */ +#define USB_PRODUCT_3COM_3CREB96 0x00a0 /* Bluetooth USB Adapter */ +#define USB_PRODUCT_3COM_3C19250 0x03e8 /* 3C19250 Ethernet Adapter */ +#define USB_PRODUCT_3COM_3CRSHEW696 0x0a01 /* 3CRSHEW696 Wireless Adapter */ +#define USB_PRODUCT_3COM_3C460 0x11f8 /* HomeConnect 3C460 */ +#define USB_PRODUCT_3COM_USR56K 0x3021 /* U.S.Robotics 56000 Voice FaxModem Pro */ +#define USB_PRODUCT_3COM_3C460B 0x4601 /* HomeConnect 3C460B */ +#define USB_PRODUCT_3COM2_3CRUSB10075 0xa727 /* 3CRUSB10075 */ +#define USB_PRODUCT_3COM3_AR5523_1 0x6893 /* AR5523 */ +#define USB_PRODUCT_3COM3_AR5523_2 0x6895 /* AR5523 */ +#define USB_PRODUCT_3COM3_AR5523_3 0x6897 /* AR5523 */ + +#define USB_PRODUCT_3COMUSR_OFFICECONN 0x0082 /* 3Com OfficeConnect Analog Modem */ +#define USB_PRODUCT_3COMUSR_USRISDN 0x008f /* 3Com U.S. Robotics Pro ISDN TA */ +#define USB_PRODUCT_3COMUSR_HOMECONN 0x009d /* 3Com HomeConnect Camera */ +#define USB_PRODUCT_3COMUSR_USR56K 0x3021 /* U.S. Robotics 56000 Voice FaxModem Pro */ + +/* AboCom products */ +#define USB_PRODUCT_ABOCOM_XX1 0x110c /* XX1 */ +#define USB_PRODUCT_ABOCOM_XX2 0x200c /* XX2 */ +#define USB_PRODUCT_ABOCOM_URE450 0x4000 /* URE450 Ethernet Adapter */ +#define USB_PRODUCT_ABOCOM_UFE1000 0x4002 /* UFE1000 Fast Ethernet Adapter */ +#define USB_PRODUCT_ABOCOM_DSB650TX_PNA 0x4003 /* 1/10/100 Ethernet Adapter */ +#define USB_PRODUCT_ABOCOM_XX4 0x4004 /* XX4 */ +#define USB_PRODUCT_ABOCOM_XX5 0x4007 /* XX5 */ +#define USB_PRODUCT_ABOCOM_XX6 0x400b /* XX6 */ +#define USB_PRODUCT_ABOCOM_XX7 0x400c /* XX7 */ +#define USB_PRODUCT_ABOCOM_RTL8151 0x401a /* RTL8151 */ +#define USB_PRODUCT_ABOCOM_XX8 0x4102 /* XX8 */ +#define USB_PRODUCT_ABOCOM_XX9 0x4104 /* XX9 */ +#define USB_PRODUCT_ABOCOM_UF200 0x420a /* UF200 Ethernet */ +#define USB_PRODUCT_ABOCOM_WL54 0x6001 /* WL54 */ +#define USB_PRODUCT_ABOCOM_XX10 0xabc1 /* XX10 */ +#define USB_PRODUCT_ABOCOM_BWU613 0xb000 /* BWU613 */ +#define USB_PRODUCT_ABOCOM_HWU54DM 0xb21b /* HWU54DM */ +#define USB_PRODUCT_ABOCOM_RT2573_2 0xb21c /* RT2573 */ +#define USB_PRODUCT_ABOCOM_RT2573_3 0xb21d /* RT2573 */ +#define USB_PRODUCT_ABOCOM_RT2573_4 0xb21e /* RT2573 */ +#define USB_PRODUCT_ABOCOM_WUG2700 0xb21f /* WUG2700 */ + +/* Accton products */ +#define USB_PRODUCT_ACCTON_USB320_EC 0x1046 /* USB320-EC Ethernet Adapter */ +#define USB_PRODUCT_ACCTON_2664W 0x3501 /* 2664W */ +#define USB_PRODUCT_ACCTON_111 0x3503 /* T-Sinus 111 Wireless Adapter */ +#define USB_PRODUCT_ACCTON_SMCWUSBG 0x4505 /* SMCWUSB-G */ +#define USB_PRODUCT_ACCTON_PRISM_GT 0x4521 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_ACCTON_SS1001 0x5046 /* SpeedStream Ethernet Adapter */ +#define USB_PRODUCT_ACCTON_ZD1211B 0xe501 /* ZD1211B */ + +/* Aceeca products */ +#define USB_PRODUCT_ACEECA_MEZ1000 0x0001 /* MEZ1000 RDA */ + +/* Acer Communications & Multimedia (oemd by Surecom) */ +#define USB_PRODUCT_ACERCM_EP1427X2 0x0893 /* EP-1427X-2 Ethernet Adapter */ + +/* Acer Labs products */ +#define USB_PRODUCT_ACERLABS_M5632 0x5632 /* USB 2.0 Data Link */ + +/* Acer Peripherals, Inc. products */ +#define USB_PRODUCT_ACERP_ACERSCAN_C310U 0x12a6 /* Acerscan C310U */ +#define USB_PRODUCT_ACERP_ACERSCAN_320U 0x2022 /* Acerscan 320U */ +#define USB_PRODUCT_ACERP_ACERSCAN_640U 0x2040 /* Acerscan 640U */ +#define USB_PRODUCT_ACERP_ACERSCAN_620U 0x2060 /* Acerscan 620U */ +#define USB_PRODUCT_ACERP_ACERSCAN_4300U 0x20b0 /* Benq 3300U/4300U */ +#define USB_PRODUCT_ACERP_ACERSCAN_640BT 0x20be /* Acerscan 640BT */ +#define USB_PRODUCT_ACERP_ACERSCAN_1240U 0x20c0 /* Acerscan 1240U */ +#define USB_PRODUCT_ACERP_ATAPI 0x6003 /* ATA/ATAPI Adapter */ +#define USB_PRODUCT_ACERP_AWL300 0x9000 /* AWL300 Wireless Adapter */ +#define USB_PRODUCT_ACERP_AWL400 0x9001 /* AWL400 Wireless Adapter */ + +/* Acer Warp products */ +#define USB_PRODUCT_ACERW_WARPLINK 0x0204 /* Warplink */ + +/* Actiontec, Inc. products */ +#define USB_PRODUCT_ACTIONTEC_PRISM_25 0x0408 /* Prism2.5 Wireless Adapter */ +#define USB_PRODUCT_ACTIONTEC_PRISM_25A 0x0421 /* Prism2.5 Wireless Adapter A */ +#define USB_PRODUCT_ACTIONTEC_FREELAN 0x6106 /* ROPEX FreeLan 802.11b */ +#define USB_PRODUCT_ACTIONTEC_UAT1 0x7605 /* UAT1 Wireless Ethernet Adapter */ + +/* ACTiSYS products */ +#define USB_PRODUCT_ACTISYS_IR2000U 0x0011 /* ACT-IR2000U FIR */ + +/* ActiveWire, Inc. products */ +#define USB_PRODUCT_ACTIVEWIRE_IOBOARD 0x0100 /* I/O Board */ +#define USB_PRODUCT_ACTIVEWIRE_IOBOARD_FW1 0x0101 /* I/O Board, rev. 1 firmware */ + +/* Adaptec products */ +#define USB_PRODUCT_ADAPTEC_AWN8020 0x0020 /* AWN-8020 WLAN */ + +/* Addtron products */ +#define USB_PRODUCT_ADDTRON_AWU120 0xff31 /* AWU-120 */ + +/* ADMtek products */ +#define USB_PRODUCT_ADMTEK_PEGASUSII_4 0x07c2 /* AN986A Ethernet */ +#define USB_PRODUCT_ADMTEK_PEGASUS 0x0986 /* AN986 Ethernet */ +#define USB_PRODUCT_ADMTEK_PEGASUSII 0x8511 /* AN8511 Ethernet */ +#define USB_PRODUCT_ADMTEK_PEGASUSII_2 0x8513 /* AN8513 Ethernet */ +#define USB_PRODUCT_ADMTEK_PEGASUSII_3 0x8515 /* AN8515 Ethernet */ + +/* ADDON products */ +/* PNY OEMs these */ +#define USB_PRODUCT_ADDON_ATTACHE 0x1300 /* USB 2.0 Flash Drive */ +#define USB_PRODUCT_ADDON_ATTACHE 0x1300 /* USB 2.0 Flash Drive */ +#define USB_PRODUCT_ADDON_A256MB 0x1400 /* Attache 256MB USB 2.0 Flash Drive */ +#define USB_PRODUCT_ADDON_DISKPRO512 0x1420 /* USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) */ + +/* Addonics products */ +#define USB_PRODUCT_ADDONICS2_CABLE_205 0xa001 /* Cable 205 */ + +/* ADS products */ +#define USB_PRODUCT_ADS_UBS10BT 0x0008 /* UBS-10BT Ethernet */ +#define USB_PRODUCT_ADS_UBS10BTX 0x0009 /* UBS-10BT Ethernet */ + +/* AEI products */ +#define USB_PRODUCT_AEI_FASTETHERNET 0x1701 /* Fast Ethernet */ + +/* Agate Technologies products */ +#define USB_PRODUCT_AGATE_QDRIVE 0x0378 /* Q-Drive */ + +/* AGFA products */ +#define USB_PRODUCT_AGFA_SNAPSCAN1212U 0x0001 /* SnapScan 1212U */ +#define USB_PRODUCT_AGFA_SNAPSCAN1236U 0x0002 /* SnapScan 1236U */ +#define USB_PRODUCT_AGFA_SNAPSCANTOUCH 0x0100 /* SnapScan Touch */ +#define USB_PRODUCT_AGFA_SNAPSCAN1212U2 0x2061 /* SnapScan 1212U */ +#define USB_PRODUCT_AGFA_SNAPSCANE40 0x208d /* SnapScan e40 */ +#define USB_PRODUCT_AGFA_SNAPSCANE50 0x208f /* SnapScan e50 */ +#define USB_PRODUCT_AGFA_SNAPSCANE20 0x2091 /* SnapScan e20 */ +#define USB_PRODUCT_AGFA_SNAPSCANE25 0x2095 /* SnapScan e25 */ +#define USB_PRODUCT_AGFA_SNAPSCANE26 0x2097 /* SnapScan e26 */ +#define USB_PRODUCT_AGFA_SNAPSCANE52 0x20fd /* SnapScan e52 */ + +/* Ain Communication Technology products */ +#define USB_PRODUCT_AINCOMM_AWU2000B 0x1001 /* AWU2000B Wireless Adapter */ + +/* AIPTEK products */ +#define USB_PRODUCT_AIPTEK_POCKETCAM3M 0x2011 /* PocketCAM 3Mega */ +#define USB_PRODUCT_SUNPLUS_PENCAM_MEGA_1_3 0x504a /* PenCam Mega 1.3 */ + +/* AirPrime products */ +#define USB_PRODUCT_AIRPRIME_PC5220 0x0112 /* CDMA Wireless PC Card */ + +/* AKS products */ +#define USB_PRODUCT_AKS_USBHASP 0x0001 /* USB-HASP 0.06 */ + +/* Alcor Micro, Inc. products */ +#define USB_PRODUCT_ALCOR2_KBD_HUB 0x2802 /* Kbd Hub */ + +#define USB_PRODUCT_ALCOR_MA_KBD_HUB 0x9213 /* MacAlly Kbd Hub */ +#define USB_PRODUCT_ALCOR_AU9814 0x9215 /* AU9814 Hub */ +#define USB_PRODUCT_ALCOR_UMCR_9361 0x9361 /* USB Multimedia Card Reader */ +#define USB_PRODUCT_ALCOR_SM_KBD 0x9410 /* MicroConnectors/StrongMan Keyboard */ +#define USB_PRODUCT_ALCOR_NEC_KBD_HUB 0x9472 /* NEC Kbd Hub */ + +/* Altec Lansing products */ +#define USB_PRODUCT_ALTEC_ADA70 0x0070 /* ADA70 Speakers */ +#define USB_PRODUCT_ALTEC_ASC495 0xff05 /* ASC495 Speakers */ + +/* Allied Telesyn International products */ +#define USB_PRODUCT_ALLIEDTELESYN_ATUSB100 0xb100 /* AT-USB100 */ + +/* American Power Conversion products */ +#define USB_PRODUCT_APC_UPS 0x0002 /* Uninterruptible Power Supply */ + +/* Ambit Microsystems products */ +#define USB_PRODUCT_AMBIT_WLAN 0x0302 /* WLAN */ +#define USB_PRODUCT_AMBIT_NTL_250 0x6098 /* NTL 250 cable modem */ + +/* AMIT products */ +#define USB_PRODUCT_AMIT_CGWLUSB2GO 0x0002 /* CG-WLUSB2GO */ + +/* Anchor products */ +#define USB_PRODUCT_ANCHOR_EZUSB 0x2131 /* EZUSB */ +#define USB_PRODUCT_ANCHOR_EZLINK 0x2720 /* EZLINK */ + +/* AnyData products */ +#define USB_PRODUCT_ANYDATA_ADU_E100X 0x6501 /* CDMA 2000 1xRTT/EV-DO USB Modem */ +#define USB_PRODUCT_ANYDATA_ADU_500A 0x6502 /* CDMA 2000 EV-DO USB Modem */ + +/* AOX, Inc. products */ +#define USB_PRODUCT_AOX_USB101 0x0008 /* Ethernet */ + +/* American Power Conversion products */ +#define USB_PRODUCT_APC_UPS 0x0002 /* Uninterruptible Power Supply */ + +/* Apple Computer products */ +#define USB_PRODUCT_APPLE_EXT_KBD 0x020c /* Apple Extended USB Keyboard */ +#define USB_PRODUCT_APPLE_OPTMOUSE 0x0302 /* Optical mouse */ +#define USB_PRODUCT_APPLE_MIGHTYMOUSE 0x0304 /* Mighty Mouse */ +#define USB_PRODUCT_APPLE_EXT_KBD_HUB 0x1003 /* Hub in Apple Extended USB Keyboard */ +#define USB_PRODUCT_APPLE_SPEAKERS 0x1101 /* Speakers */ +#define USB_PRODUCT_APPLE_IPOD 0x1201 /* iPod */ +#define USB_PRODUCT_APPLE_IPOD2G 0x1202 /* iPod 2G */ +#define USB_PRODUCT_APPLE_IPOD3G 0x1203 /* iPod 3G */ +#define USB_PRODUCT_APPLE_IPOD_04 0x1204 /* iPod '04' */ +#define USB_PRODUCT_APPLE_IPODMINI 0x1205 /* iPod Mini */ +#define USB_PRODUCT_APPLE_IPOD_06 0x1206 /* iPod '06' */ +#define USB_PRODUCT_APPLE_IPOD_07 0x1207 /* iPod '07' */ +#define USB_PRODUCT_APPLE_IPOD_08 0x1208 /* iPod '08' */ +#define USB_PRODUCT_APPLE_IPODVIDEO 0x1209 /* iPod Video */ +#define USB_PRODUCT_APPLE_IPODNANO 0x120a /* iPod Nano */ +#define USB_PRODUCT_APPLE_IPHONE 0x1290 /* iPhone */ +#define USB_PRODUCT_APPLE_IPHONE_3G 0x1292 /* iPhone 3G */ +#define USB_PRODUCT_APPLE_ETHERNET 0x1402 /* Ethernet A1277 */ + +/* Arkmicro Technologies */ +#define USB_PRODUCT_ARKMICRO_ARK3116 0x0232 /* ARK3116 Serial */ + +/* Asahi Optical products */ +#define USB_PRODUCT_ASAHIOPTICAL_OPTIO230 0x0004 /* Digital camera */ +#define USB_PRODUCT_ASAHIOPTICAL_OPTIO330 0x0006 /* Digital camera */ + +/* Asante products */ +#define USB_PRODUCT_ASANTE_EA 0x1427 /* Ethernet */ + +/* ASIX Electronics products */ +#define USB_PRODUCT_ASIX_AX88172 0x1720 /* 10/100 Ethernet */ +#define USB_PRODUCT_ASIX_AX88178 0x1780 /* AX88178 */ +#define USB_PRODUCT_ASIX_AX88772 0x7720 /* AX88772 */ + +/* ASUS products */ +#define USB_PRODUCT_ASUS_WL167G 0x1707 /* WL-167g Wireless Adapter */ +#define USB_PRODUCT_ASUS_WL159G 0x170c /* WL-159g */ +#define USB_PRODUCT_ASUS_A9T_WIFI 0x171b /* A9T wireless */ +#define USB_PRODUCT_ASUS_RT2573_1 0x1723 /* RT2573 */ +#define USB_PRODUCT_ASUS_RT2573_2 0x1724 /* RT2573 */ +#define USB_PRODUCT_ASUS_LCM 0x1726 /* LCM display */ +#define USB_PRODUCT_ASUS_P535 0x420f /* ASUS P535 PDA */ + +/* ATen products */ +#define USB_PRODUCT_ATEN_UC1284 0x2001 /* Parallel printer */ +#define USB_PRODUCT_ATEN_UC10T 0x2002 /* 10Mbps Ethernet */ +#define USB_PRODUCT_ATEN_UC110T 0x2007 /* UC-110T Ethernet */ +#define USB_PRODUCT_ATEN_UC232A 0x2008 /* Serial */ +#define USB_PRODUCT_ATEN_UC210T 0x2009 /* UC-210T Ethernet */ +#define USB_PRODUCT_ATEN_DSB650C 0x4000 /* DSB-650C */ + +/* Atheros Communications products */ +#define USB_PRODUCT_ATHEROS_AR5523 0x0001 /* AR5523 */ +#define USB_PRODUCT_ATHEROS_AR5523_NF 0x0002 /* AR5523 (no firmware) */ +#define USB_PRODUCT_ATHEROS2_AR5523_1 0x0001 /* AR5523 */ +#define USB_PRODUCT_ATHEROS2_AR5523_1_NF 0x0002 /* AR5523 (no firmware) */ +#define USB_PRODUCT_ATHEROS2_AR5523_2 0x0003 /* AR5523 */ +#define USB_PRODUCT_ATHEROS2_AR5523_2_NF 0x0004 /* AR5523 (no firmware) */ +#define USB_PRODUCT_ATHEROS2_AR5523_3 0x0005 /* AR5523 */ +#define USB_PRODUCT_ATHEROS2_AR5523_3_NF 0x0006 /* AR5523 (no firmware) */ + +/* Atmel Comp. products */ +#define USB_PRODUCT_ATMEL_UHB124 0x3301 /* UHB124 hub */ +#define USB_PRODUCT_ATMEL_DWL120 0x7603 /* DWL-120 Wireless Adapter */ +#define USB_PRODUCT_ATMEL_BW002 0x7605 /* BW002 Wireless Adapter */ +#define USB_PRODUCT_ATMEL_WL1130USB 0x7613 /* WL-1130 USB */ +#define USB_PRODUCT_ATMEL_AT76C505A 0x7614 /* AT76c505a Wireless Adapter */ + +/* Avision products */ +#define USB_PRODUCT_AVISION_1200U 0x0268 /* 1200U scanner */ + +/* Axesstel products */ +#define USB_PRODUCT_AXESSTEL_DATAMODEM 0x1000 /* Data Modem */ + +/* Baltech products */ +#define USB_PRODUCT_BALTECH_CARDREADER 0x9999 /* Card reader */ + +/* B&B Electronics products */ +#define USB_PRODUCT_BBELECTRONICS_USOTL4 0xAC01 /* RS-422/485 */ + +/* Belkin products */ +/*product BELKIN F5U111 0x???? F5U111 Ethernet*/ +#define USB_PRODUCT_BELKIN_F5D6050 0x0050 /* F5D6050 802.11b Wireless Adapter */ +#define USB_PRODUCT_BELKIN_FBT001V 0x0081 /* FBT001v2 Bluetooth */ +#define USB_PRODUCT_BELKIN_FBT003V 0x0084 /* FBT003v2 Bluetooth */ +#define USB_PRODUCT_BELKIN_F5U103 0x0103 /* F5U103 Serial */ +#define USB_PRODUCT_BELKIN_F5U109 0x0109 /* F5U109 Serial */ +#define USB_PRODUCT_BELKIN_USB2SCSI 0x0115 /* USB to SCSI */ +#define USB_PRODUCT_BELKIN_USB2LAN 0x0121 /* USB to LAN */ +#define USB_PRODUCT_BELKIN_F5U208 0x0208 /* F5U208 VideoBus II */ +#define USB_PRODUCT_BELKIN_F5U237 0x0237 /* F5U237 USB 2.0 7-Port Hub */ +#define USB_PRODUCT_BELKIN_F5U257 0x0257 /* F5U257 Serial */ +#define USB_PRODUCT_BELKIN_F5U409 0x0409 /* F5U409 Serial */ +#define USB_PRODUCT_BELKIN_F6C550AVR 0x0551 /* F6C550-AVR UPS */ +#define USB_PRODUCT_BELKIN_F5U120 0x1203 /* F5U120-PC Hub */ +#define USB_PRODUCT_BELKIN_ZD1211B 0x4050 /* ZD1211B */ +#define USB_PRODUCT_BELKIN_F5D5055 0x5055 /* F5D5055 */ +#define USB_PRODUCT_BELKIN_F5D7050 0x7050 /* F5D7050 Wireless Adapter */ +#define USB_PRODUCT_BELKIN_F5D7051 0x7051 /* F5D7051 54g USB Network Adapter */ +#define USB_PRODUCT_BELKIN_F5D7050A 0x705a /* F5D7050A Wireless Adapter */ +#define USB_PRODUCT_BELKIN_F5D7050_V4000 0x705c /* F5D7050 v4000 Wireless Adapter */ +#define USB_PRODUCT_BELKIN_F5D9050V3 0x905b /* F5D9050 ver 3 Wireless Adapter */ +#define USB_PRODUCT_BELKIN2_F5U002 0x0002 /* F5U002 Parallel printer */ + +/* Billionton products */ +#define USB_PRODUCT_BILLIONTON_USB100 0x0986 /* USB100N 10/100 FastEthernet */ +#define USB_PRODUCT_BILLIONTON_USBLP100 0x0987 /* USB100LP */ +#define USB_PRODUCT_BILLIONTON_USBEL100 0x0988 /* USB100EL */ +#define USB_PRODUCT_BILLIONTON_USBE100 0x8511 /* USBE100 */ +#define USB_PRODUCT_BILLIONTON_USB2AR 0x90ff /* USB2AR Ethernet */ + +/* Broadcom products */ +#define USB_PRODUCT_BROADCOM_BCM2033 0x2033 /* BCM2033 Bluetooth USB dongle */ + +/* Brother Industries products */ +#define USB_PRODUCT_BROTHER_HL1050 0x0002 /* HL-1050 laser printer */ + +/* Behavior Technology Computer products */ +#define USB_PRODUCT_BTC_BTC7932 0x6782 /* Keyboard with mouse port */ + +/* Canon, Inc. products */ +#define USB_PRODUCT_CANON_N656U 0x2206 /* CanoScan N656U */ +#define USB_PRODUCT_CANON_N1220U 0x2207 /* CanoScan N1220U */ +#define USB_PRODUCT_CANON_D660U 0x2208 /* CanoScan D660U */ +#define USB_PRODUCT_CANON_N676U 0x220d /* CanoScan N676U */ +#define USB_PRODUCT_CANON_N1240U 0x220e /* CanoScan N1240U */ +#define USB_PRODUCT_CANON_LIDE25 0x2220 /* CanoScan LIDE 25 */ +#define USB_PRODUCT_CANON_S10 0x3041 /* PowerShot S10 */ +#define USB_PRODUCT_CANON_S100 0x3045 /* PowerShot S100 */ +#define USB_PRODUCT_CANON_S200 0x3065 /* PowerShot S200 */ +#define USB_PRODUCT_CANON_REBELXT 0x30ef /* Digital Rebel XT */ + +/* CATC products */ +#define USB_PRODUCT_CATC_NETMATE 0x000a /* Netmate Ethernet */ +#define USB_PRODUCT_CATC_NETMATE2 0x000c /* Netmate2 Ethernet */ +#define USB_PRODUCT_CATC_CHIEF 0x000d /* USB Chief Bus & Protocol Analyzer */ +#define USB_PRODUCT_CATC_ANDROMEDA 0x1237 /* Andromeda hub */ + +/* CASIO products */ +#define USB_PRODUCT_CASIO_QV_DIGICAM 0x1001 /* QV DigiCam */ +#define USB_PRODUCT_CASIO_EXS880 0x1105 /* Exilim EX-S880 */ +#define USB_PRODUCT_CASIO_BE300 0x2002 /* BE-300 PDA */ +#define USB_PRODUCT_CASIO_NAMELAND 0x4001 /* CASIO Nameland EZ-USB */ + +/* CCYU products */ +#define USB_PRODUCT_CCYU_ED1064 0x2136 /* EasyDisk ED1064 */ + +/* Century products */ +#define USB_PRODUCT_CENTURY_EX35QUAT 0x011e /* Century USB Disk Enclosure */ + +/* Cherry products */ +#define USB_PRODUCT_CHERRY_MY3000KBD 0x0001 /* My3000 keyboard */ +#define USB_PRODUCT_CHERRY_MY3000HUB 0x0003 /* My3000 hub */ +#define USB_PRODUCT_CHERRY_CYBOARD 0x0004 /* CyBoard Keyboard */ + +/* Chic Technology products */ +#define USB_PRODUCT_CHIC_MOUSE1 0x0001 /* mouse */ +#define USB_PRODUCT_CHIC_CYPRESS 0x0003 /* Cypress USB Mouse */ + +/* Chicony products */ +#define USB_PRODUCT_CHICONY_KB8933 0x0001 /* KB-8933 keyboard */ +#define USB_PRODUCT_MICRODIA_TWINKLECAM 0x600d /* TwinkleCam USB camera */ + +/* CH Products */ +#define USB_PRODUCT_CHPRODUCTS_PROTHROTTLE 0x00f1 /* Pro Throttle */ +#define USB_PRODUCT_CHPRODUCTS_PROPEDALS 0x00f2 /* Pro Pedals */ +#define USB_PRODUCT_CHPRODUCTS_FIGHTERSTICK 0x00f3 /* Fighterstick */ +#define USB_PRODUCT_CHPRODUCTS_FLIGHTYOKE 0x00ff /* Flight Sim Yoke */ + +/* Cisco-Linksys products */ +#define USB_PRODUCT_CISCOLINKSYS_WUSB54G 0x000d /* WUSB54G Wireless Adapter */ +#define USB_PRODUCT_CISCOLINKSYS_WUSB54GP 0x0011 /* WUSB54GP Wireless Adapter */ +#define USB_PRODUCT_CISCOLINKSYS_USB200MV2 0x0018 /* USB200M v2 */ +#define USB_PRODUCT_CISCOLINKSYS_HU200TS 0x001a /* HU200TS Wireless Adapter */ +#define USB_PRODUCT_CISCOLINKSYS_WUSB54GC 0x0020 /* WUSB54GC */ +#define USB_PRODUCT_CISCOLINKSYS_WUSB54GR 0x0023 /* WUSB54GR */ +#define USB_PRODUCT_CISCOLINKSYS_WUSBF54G 0x0024 /* WUSBF54G */ + +/* CMOTECH products */ +#define USB_PRODUCT_CMOTECH_CNU510 0x5141 /* CMOTECH CDMA Technologies USB modem */ +#define USB_PRODUCT_CMOTECH_CNU550 0x5543 /* CDMA 2000 1xRTT/1xEVDO USB modem */ +#define USB_PRODUCT_CMOTECH_CDMA_MODEM1 0x6280 /* CMOTECH CDMA Technologies USB modem */ + +/* Compaq products */ +#define USB_PRODUCT_COMPAQ_IPAQPOCKETPC 0x0003 /* iPAQ PocketPC */ +#define USB_PRODUCT_COMPAQ_PJB100 0x504a /* Personal Jukebox PJB100 */ +#define USB_PRODUCT_COMPAQ_IPAQLINUX 0x505a /* iPAQ Linux */ + +/* Composite Corp products looks the same as "TANGTOP" */ +#define USB_PRODUCT_COMPOSITE_USBPS2 0x0001 /* USB to PS2 Adaptor */ + +/* Conceptronic products */ +#define USB_PRODUCT_CONCEPTRONIC_PRISM_GT 0x3762 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_CONCEPTRONIC_C11U 0x7100 /* C11U */ +#define USB_PRODUCT_CONCEPTRONIC_WL210 0x7110 /* WL-210 */ +#define USB_PRODUCT_CONCEPTRONIC_AR5523_1 0x7801 /* AR5523 */ +#define USB_PRODUCT_CONCEPTRONIC_AR5523_1_NF 0x7802 /* AR5523 (no firmware) */ +#define USB_PRODUCT_CONCEPTRONIC_AR5523_2 0x7811 /* AR5523 */ +#define USB_PRODUCT_CONCEPTRONIC_AR5523_2_NF 0x7812 /* AR5523 (no firmware) */ +#define USB_PRODUCT_CONCEPTRONIC2_C54RU 0x3c02 /* C54RU WLAN */ +#define USB_PRODUCT_CONCEPTRONIC2_C54RU2 0x3c22 /* C54RU */ + +/* Connectix products */ +#define USB_PRODUCT_CONNECTIX_QUICKCAM 0x0001 /* QuickCam */ + +/* Corega products */ +#define USB_PRODUCT_COREGA_ETHER_USB_T 0x0001 /* Ether USB-T */ +#define USB_PRODUCT_COREGA_FETHER_USB_TX 0x0004 /* FEther USB-TX */ +#define USB_PRODUCT_COREGA_WLAN_USB_USB_11 0x000c /* WirelessLAN USB-11 */ +#define USB_PRODUCT_COREGA_FETHER_USB_TXS 0x000d /* FEther USB-TXS */ +#define USB_PRODUCT_COREGA_WLANUSB 0x0012 /* Wireless LAN Stick-11 */ +#define USB_PRODUCT_COREGA_FETHER_USB2_TX 0x0017 /* FEther USB2-TX */ +#define USB_PRODUCT_COREGA_WLUSB_11_KEY 0x001a /* ULUSB-11 Key */ +#define USB_PRODUCT_COREGA_CGWLUSB2GL 0x002d /* CG-WLUSB2GL */ +#define USB_PRODUCT_COREGA_CGWLUSB2GPX 0x002e /* CG-WLUSB2GPX */ +#define USB_PRODUCT_COREGA_WLUSB_11_STICK 0x7613 /* WLAN USB Stick 11 */ +#define USB_PRODUCT_COREGA_FETHER_USB_TXC 0x9601 /* FEther USB-TXC */ + +/* Creative products */ +#define USB_PRODUCT_CREATIVE_NOMAD_II 0x1002 /* Nomad II MP3 player */ +#define USB_PRODUCT_CREATIVE_NOMAD_IIMG 0x4004 /* Nomad II MG */ +#define USB_PRODUCT_CREATIVE_NOMAD 0x4106 /* Nomad */ +#define USB_PRODUCT_CREATIVE2_VOIP_BLASTER 0x0258 /* Voip Blaster */ +#define USB_PRODUCT_CREATIVE3_OPTICAL_MOUSE 0x0001 /* Notebook Optical Mouse */ + +/* Cambridge Silicon Radio Ltd. products */ +#define USB_PRODUCT_CSR_BT_DONGLE 0x0001 /* Bluetooth USB dongle */ +#define USB_PRODUCT_CSR_CSRDFU 0xffff /* USB Bluetooth Device in DFU State */ + +/* CTX products */ +#define USB_PRODUCT_CTX_EX1300 0x9999 /* Ex1300 hub */ + +/* Curitel products */ +#define USB_PRODUCT_CURITEL_HX550C 0x1101 /* CDMA 2000 1xRTT USB modem (HX-550C) */ +#define USB_PRODUCT_CURITEL_HX57XB 0x2101 /* CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) */ +#define USB_PRODUCT_CURITEL_PC5740 0x3701 /* Broadband Wireless modem */ + +/* CyberPower products */ +#define USB_PRODUCT_CYBERPOWER_1500CAVRLCD 0x0501 /* 1500CAVRLCD */ + +/* CyberTAN Technology products */ +#define USB_PRODUCT_CYBERTAN_TG54USB 0x1666 /* TG54USB */ + +/* Cypress Semiconductor products */ +#define USB_PRODUCT_CYPRESS_MOUSE 0x0001 /* mouse */ +#define USB_PRODUCT_CYPRESS_THERMO 0x0002 /* thermometer */ +#define USB_PRODUCT_CYPRESS_WISPY1A 0x0bad /* MetaGeek Wi-Spy */ +#define USB_PRODUCT_CYPRESS_KBDHUB 0x0101 /* Keyboard/Hub */ +#define USB_PRODUCT_CYPRESS_FMRADIO 0x1002 /* FM Radio */ +#define USB_PRODUCT_CYPRESS_USBRS232 0x5500 /* USB-RS232 Interface */ +#define USB_PRODUCT_CYPRESS_SLIM_HUB 0x6560 /* Slim Hub */ + +/* Daisy Technology products */ +#define USB_PRODUCT_DAISY_DMC 0x6901 /* USB MultiMedia Reader */ + +/* Dallas Semiconductor products */ +#define USB_PRODUCT_DALLAS_J6502 0x4201 /* J-6502 speakers */ + +/* Dell products */ +#define USB_PRODUCT_DELL_PORT 0x0058 /* Port Replicator */ +#define USB_PRODUCT_DELL_AIO926 0x5115 /* Photo AIO Printer 926 */ +#define USB_PRODUCT_DELL_BC02 0x8000 /* BC02 Bluetooth USB Adapter */ +#define USB_PRODUCT_DELL_PRISM_GT_1 0x8102 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_DELL_TM350 0x8103 /* TrueMobile 350 Bluetooth USB Adapter */ +#define USB_PRODUCT_DELL_PRISM_GT_2 0x8104 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_DELL_U740 0x8135 /* Dell U740 CDMA */ + +/* Delorme Paublishing products */ +#define USB_PRODUCT_DELORME_EARTHMATE 0x0100 /* Earthmate GPS */ + +/* Desknote products */ +#define USB_PRODUCT_DESKNOTE_UCR_61S2B 0x0c55 /* UCR-61S2B */ + +/* Diamond products */ +#define USB_PRODUCT_DIAMOND_RIO500USB 0x0001 /* Rio 500 USB */ + +/* Dick Smith Electronics (really C-Net) products */ +#define USB_PRODUCT_DICKSMITH_RT2573 0x9022 /* RT2573 */ +#define USB_PRODUCT_DICKSMITH_CWD854F 0x9032 /* C-Net CWD-854 rev F */ + +/* Digi International products */ +#define USB_PRODUCT_DIGI_ACCELEPORT2 0x0002 /* AccelePort USB 2 */ +#define USB_PRODUCT_DIGI_ACCELEPORT4 0x0004 /* AccelePort USB 4 */ +#define USB_PRODUCT_DIGI_ACCELEPORT8 0x0008 /* AccelePort USB 8 */ + +/* D-Link products */ +/*product DLINK DSBS25 0x0100 DSB-S25 serial*/ +#define USB_PRODUCT_DLINK_DUBE100 0x1a00 /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX4 0x200c /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DWL120E 0x3200 /* DWL-120 rev E */ +#define USB_PRODUCT_DLINK_DWL122 0x3700 /* DWL-122 */ +#define USB_PRODUCT_DLINK_DWLG120 0x3701 /* DWL-G120 */ +#define USB_PRODUCT_DLINK_DWL120F 0x3702 /* DWL-120 rev F */ +#define USB_PRODUCT_DLINK_DWLAG132 0x3a00 /* DWL-AG132 */ +#define USB_PRODUCT_DLINK_DWLAG132_NF 0x3a01 /* DWL-AG132 (no firmware) */ +#define USB_PRODUCT_DLINK_DWLG132 0x3a02 /* DWL-G132 */ +#define USB_PRODUCT_DLINK_DWLG132_NF 0x3a03 /* DWL-G132 (no firmware) */ +#define USB_PRODUCT_DLINK_DWLAG122 0x3a04 /* DWL-AG122 */ +#define USB_PRODUCT_DLINK_DWLAG122_NF 0x3a05 /* DWL-AG122 (no firmware) */ +#define USB_PRODUCT_DLINK_DWLG122 0x3c00 /* DWL-G122 b1 Wireless Adapter */ +#define USB_PRODUCT_DLINK_DUBE100B1 0x3c05 /* DUB-E100 rev B1 */ +#define USB_PRODUCT_DLINK_DSB650C 0x4000 /* 10Mbps Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX1 0x4001 /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX 0x4002 /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX_PNA 0x4003 /* 1/10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX3 0x400b /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650TX2 0x4102 /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK_DSB650 0xabc1 /* 10/100 Ethernet */ +#define USB_PRODUCT_DLINK2_DWLG122C1 0x3c03 /* DWL-G122 c1 */ +#define USB_PRODUCT_DLINK2_WUA1340 0x3c04 /* WUA-1340 */ +#define USB_PRODUCT_DLINK2_DWA111 0x3c06 /* DWA-111 */ +#define USB_PRODUCT_DLINK2_DWA110 0x3c07 /* DWA-110 */ + +/* DMI products */ +#define USB_PRODUCT_DMI_CFSM_RW 0xa109 /* CF/SM Reader/Writer */ + +/* DrayTek products */ +#define USB_PRODUCT_DRAYTEK_VIGOR550 0x0550 /* Vigor550 */ + +/* Dynastream Innovations */ +#define USB_PRODUCT_DYNASTREAM_ANTDEVBOARD 0x1003 /* ANT dev board */ + +/* EIZO products */ +#define USB_PRODUCT_EIZO_HUB 0x0000 /* hub */ +#define USB_PRODUCT_EIZO_MONITOR 0x0001 /* monitor */ + +/* ELCON Systemtechnik products */ +#define USB_PRODUCT_ELCON_PLAN 0x0002 /* Goldpfeil P-LAN */ + +/* Elecom products */ +#define USB_PRODUCT_ELECOM_MOUSE29UO 0x0002 /* mouse 29UO */ +#define USB_PRODUCT_ELECOM_LDUSBTX0 0x200c /* LD-USB/TX */ +#define USB_PRODUCT_ELECOM_LDUSBTX1 0x4002 /* LD-USB/TX */ +#define USB_PRODUCT_ELECOM_LDUSBLTX 0x4005 /* LD-USBL/TX */ +#define USB_PRODUCT_ELECOM_LDUSBTX2 0x400b /* LD-USB/TX */ +#define USB_PRODUCT_ELECOM_LDUSB20 0x4010 /* LD-USB20 */ +#define USB_PRODUCT_ELECOM_UCSGT 0x5003 /* UC-SGT */ +#define USB_PRODUCT_ELECOM_UCSGT0 0x5004 /* UC-SGT */ +#define USB_PRODUCT_ELECOM_LDUSBTX3 0xabc1 /* LD-USB/TX */ + +/* Elsa products */ +#define USB_PRODUCT_ELSA_MODEM1 0x2265 /* ELSA Modem Board */ +#define USB_PRODUCT_ELSA_USB2ETHERNET 0x3000 /* Microlink USB2Ethernet */ + +/* EMS products */ +#define USB_PRODUCT_EMS_DUAL_SHOOTER 0x0003 /* PSX gun controller converter */ + +/* Entrega products */ +#define USB_PRODUCT_ENTREGA_1S 0x0001 /* 1S serial */ +#define USB_PRODUCT_ENTREGA_2S 0x0002 /* 2S serial */ +#define USB_PRODUCT_ENTREGA_1S25 0x0003 /* 1S25 serial */ +#define USB_PRODUCT_ENTREGA_4S 0x0004 /* 4S serial */ +#define USB_PRODUCT_ENTREGA_E45 0x0005 /* E45 Ethernet */ +#define USB_PRODUCT_ENTREGA_CENTRONICS 0x0006 /* Parallel Port */ +#define USB_PRODUCT_ENTREGA_XX1 0x0008 /* Ethernet */ +#define USB_PRODUCT_ENTREGA_1S9 0x0093 /* 1S9 serial */ +#define USB_PRODUCT_ENTREGA_EZUSB 0x8000 /* EZ-USB */ +/*product ENTREGA SERIAL 0x8001 DB25 Serial*/ +#define USB_PRODUCT_ENTREGA_2U4S 0x8004 /* 2U4S serial/usb hub */ +#define USB_PRODUCT_ENTREGA_XX2 0x8005 /* Ethernet */ +/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ + +/* Epson products */ +#define USB_PRODUCT_EPSON_PRINTER1 0x0001 /* USB Printer */ +#define USB_PRODUCT_EPSON_PRINTER2 0x0002 /* ISD USB Smart Cable for Mac */ +#define USB_PRODUCT_EPSON_PRINTER3 0x0003 /* ISD USB Smart Cable */ +#define USB_PRODUCT_EPSON_PRINTER5 0x0005 /* USB Printer */ +#define USB_PRODUCT_EPSON_636 0x0101 /* Perfection 636U / 636Photo scanner */ +#define USB_PRODUCT_EPSON_610 0x0103 /* Perfection 610 scanner */ +#define USB_PRODUCT_EPSON_1200 0x0104 /* Perfection 1200U / 1200Photo scanner */ +#define USB_PRODUCT_EPSON_1600 0x0107 /* Expression 1600 scanner */ +#define USB_PRODUCT_EPSON_1640 0x010a /* Perfection 1640SU scanner */ +#define USB_PRODUCT_EPSON_1240 0x010b /* Perfection 1240U / 1240Photo scanner */ +#define USB_PRODUCT_EPSON_640U 0x010c /* Perfection 640U scanner */ +#define USB_PRODUCT_EPSON_1250 0x010f /* Perfection 1250U / 1250Photo scanner */ +#define USB_PRODUCT_EPSON_1650 0x0110 /* Perfection 1650 scanner */ +#define USB_PRODUCT_EPSON_GT9700F 0x0112 /* GT-9700F scanner */ +#define USB_PRODUCT_EPSON_GT9300UF 0x011b /* GT-9300UF scanner */ +#define USB_PRODUCT_EPSON_3200 0x011c /* Perfection 3200 scanner */ +#define USB_PRODUCT_EPSON_1260 0x011d /* Perfection 1260 scanner */ +#define USB_PRODUCT_EPSON_1660 0x011e /* Perfection 1660 scanner */ +#define USB_PRODUCT_EPSON_1670 0x011f /* Perfection 1670 scanner */ +#define USB_PRODUCT_EPSON_1270 0x0120 /* Perfection 1270 scanner */ +#define USB_PRODUCT_EPSON_2480 0x0121 /* Perfection 2480 scanner */ +#define USB_PRODUCT_EPSON_3590 0x0122 /* Perfection 3590 scanner */ +#define USB_PRODUCT_EPSON_4990 0x012a /* Perfection 4990 Photo scanner */ +#define USB_PRODUCT_EPSON_STYLUS_875DC 0x0601 /* Stylus Photo 875DC Card Reader */ +#define USB_PRODUCT_EPSON_STYLUS_895 0x0602 /* Stylus Photo 895 Card Reader */ +#define USB_PRODUCT_EPSON_CX5400 0x0808 /* CX5400 scanner */ +#define USB_PRODUCT_EPSON_3500 0x080e /* CX-3500/3600/3650 MFP */ +#define USB_PRODUCT_EPSON_RX425 0x080f /* Stylus Photo RX425 scanner */ +#define USB_PRODUCT_EPSON_4800 0x0819 /* CX4800 MP scanner */ +#define USB_PRODUCT_EPSON_4200 0x0820 /* CX4200 MP scanner */ +#define USB_PRODUCT_EPSON_5000 0x082b /* DX-50x0 MFP scanner */ +#define USB_PRODUCT_EPSON_6000 0x082e /* DX-60x0 MFP scanner */ +#define USB_PRODUCT_EPSON_DX7400 0x0838 /* DX7400/CX7300 scanner */ +#define USB_PRODUCT_EPSON_DX8400 0x0839 /* DX8400 scanner */ + +/* e-TEK Labs products */ +#define USB_PRODUCT_ETEK_1COM 0x8007 /* Serial */ + +/* Extended Systems products */ +#define USB_PRODUCT_EXTENDED_XTNDACCESS 0x0100 /* XTNDAccess IrDA */ + +/* FEIYA products */ +#define USB_PRODUCT_FEIYA_5IN1 0x1132 /* 5-in-1 Card Reader */ + +/* Fiberline */ +#define USB_PRODUCT_FIBERLINE_WL430U 0x6003 /* WL-430U */ + +/* Fossil, Inc products */ +#define USB_PRODUCT_FOSSIL_WRISTPDA 0x0002 /* Wrist PDA */ + +/* Freecom products */ +#define USB_PRODUCT_FREECOM_DVD 0xfc01 /* DVD drive */ + +/* Fujitsu Siemens Computers products */ +#define USB_PRODUCT_FSC_E5400 0x1009 /* PrismGT USB 2.0 WLAN */ + +/* Future Technology Devices products */ +#define USB_PRODUCT_FTDI_SERIAL_8U100AX 0x8372 /* 8U100AX Serial */ +#define USB_PRODUCT_FTDI_SERIAL_8U232AM 0x6001 /* 8U232AM Serial */ +#define USB_PRODUCT_FTDI_SERIAL_2232C 0x6010 /* FT2232C Dual port Serial */ +/* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ +#define USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M 0xcc48 /* OpenPort 1.3 Mitsubishi */ +#define USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S 0xcc49 /* OpenPort 1.3 Subaru */ +#define USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U 0xcc4a /* OpenPort 1.3 Universal */ +#define USB_PRODUCT_FTDI_EISCOU 0xe888 /* Expert ISDN Control USB */ +#define USB_PRODUCT_FTDI_UOPTBR 0xe889 /* USB-RS232 OptoBridge */ +#define USB_PRODUCT_FTDI_EMCU2D 0xe88a /* Expert mouseCLOCK USB II */ +#define USB_PRODUCT_FTDI_PCMSFU 0xe88b /* Precision Clock MSF USB */ +#define USB_PRODUCT_FTDI_EMCU2H 0xe88c /* Expert mouseCLOCK USB II HBG */ +#define USB_PRODUCT_FTDI_USBSERIAL 0xfa00 /* Matrix Orbital USB Serial */ +#define USB_PRODUCT_FTDI_MX2_3 0xfa01 /* Matrix Orbital MX2 or MX3 */ +#define USB_PRODUCT_FTDI_MX4_5 0xfa02 /* Matrix Orbital MX4 or MX5 */ +#define USB_PRODUCT_FTDI_LK202 0xfa03 /* Matrix Orbital VK/LK202 Family */ +#define USB_PRODUCT_FTDI_LK204 0xfa04 /* Matrix Orbital VK/LK204 Family */ +#define USB_PRODUCT_FTDI_CFA_632 0xfc08 /* Crystalfontz CFA-632 USB LCD */ +#define USB_PRODUCT_FTDI_CFA_634 0xfc09 /* Crystalfontz CFA-634 USB LCD */ +#define USB_PRODUCT_FTDI_CFA_633 0xfc0b /* Crystalfontz CFA-633 USB LCD */ +#define USB_PRODUCT_FTDI_CFA_631 0xfc0c /* Crystalfontz CFA-631 USB LCD */ +#define USB_PRODUCT_FTDI_CFA_635 0xfc0d /* Crystalfontz CFA-635 USB LCD */ +#define USB_PRODUCT_FTDI_SEMC_DSS20 0xfc82 /* SEMC DSS-20 SyncStation */ + +/* Fuji photo products */ +#define USB_PRODUCT_FUJIPHOTO_MASS0100 0x0100 /* Mass Storage */ + +/* Fujitsu protducts */ +#define USB_PRODUCT_FUJITSU_AH_F401U 0x105b /* AH-F401U Air H device */ + +/* Garmin products */ +#define USB_PRODUCT_GARMIN_IQUE_3600 0x0004 /* iQue 3600 */ + +/* General Instruments (Motorola) products */ +#define USB_PRODUCT_GENERALINSTMNTS_SB5100 0x5100 /* SURFboard SB5100 Cable modem */ + +/* Genesys Logic products */ +#define USB_PRODUCT_GENESYS_GL620USB 0x0501 /* GL620USB Host-Host interface */ +#define USB_PRODUCT_GENESYS_GL650 0x0604 /* GL650 Hub */ +#define USB_PRODUCT_GENESYS_GL641USB 0x0700 /* GL641USB CompactFlash Card Reader */ +#define USB_PRODUCT_GENESYS_GL641USB2IDE_2 0x0701 /* GL641USB USB-IDE Bridge No 2 */ +#define USB_PRODUCT_GENESYS_GL641USB2IDE 0x0702 /* GL641USB USB-IDE Bridge */ +#define USB_PRODUCT_GENESYS_GL641USB_2 0x0760 /* GL641USB 6-in-1 Card Reader */ + +/* GIGABYTE products */ +#define USB_PRODUCT_GIGABYTE_GN54G 0x8001 /* GN-54G */ +#define USB_PRODUCT_GIGABYTE_GNBR402W 0x8002 /* GN-BR402W */ +#define USB_PRODUCT_GIGABYTE_GNWLBM101 0x8003 /* GN-WLBM101 */ +#define USB_PRODUCT_GIGABYTE_GNWBKG 0x8007 /* GN-WBKG */ +#define USB_PRODUCT_GIGABYTE_GNWB01GS 0x8008 /* GN-WB01GS */ +#define USB_PRODUCT_GIGABYTE_GNWI05GS 0x800a /* GN-WI05GS */ + +/* Gigaset products */ +#define USB_PRODUCT_GIGASET_WLAN 0x0701 /* WLAN */ +#define USB_PRODUCT_GIGASET_SMCWUSBTG 0x0710 /* SMCWUSBT-G */ +#define USB_PRODUCT_GIGASET_SMCWUSBTG_NF 0x0711 /* SMCWUSBT-G (no firmware) */ +#define USB_PRODUCT_GIGASET_AR5523 0x0712 /* AR5523 */ +#define USB_PRODUCT_GIGASET_AR5523_NF 0x0713 /* AR5523 (no firmware) */ +#define USB_PRODUCT_GIGASET_RT2573 0x0722 /* RT2573 */ + +/* Global Sun Technology product */ +#define USB_PRODUCT_GLOBALSUN_AR5523_1 0x7801 /* AR5523 */ +#define USB_PRODUCT_GLOBALSUN_AR5523_1_NF 0x7802 /* AR5523 (no firmware) */ +#define USB_PRODUCT_GLOBALSUN_AR5523_2 0x7811 /* AR5523 */ +#define USB_PRODUCT_GLOBALSUN_AR5523_2_NF 0x7812 /* AR5523 (no firmware) */ + +/* Globespan products */ +#define USB_PRODUCT_GLOBESPAN_PRISM_GT_1 0x2000 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_GLOBESPAN_PRISM_GT_2 0x2002 /* PrismGT USB 2.0 WLAN */ + +/* G.Mate, Inc products */ +#define USB_PRODUCT_GMATE_YP3X00 0x1001 /* YP3X00 PDA */ + +/* GoHubs products */ +#define USB_PRODUCT_GOHUBS_GOCOM232 0x1001 /* GoCOM232 Serial */ + +/* Good Way Technology products */ +#define USB_PRODUCT_GOODWAY_GWUSB2E 0x6200 /* GWUSB2E */ +#define USB_PRODUCT_GOODWAY_RT2573 0xc019 /* RT2573 */ + +/* Gravis products */ +#define USB_PRODUCT_GRAVIS_GAMEPADPRO 0x4001 /* GamePad Pro */ + +/* GREENHOUSE products */ +#define USB_PRODUCT_GREENHOUSE_KANA21 0x0001 /* CF-writer with MP3 */ + +/* Griffin Technology */ +#define USB_PRODUCT_GRIFFIN_IMATE 0x0405 /* iMate, ADB Adapter */ + +/* Guillemot Corporation */ +#define USB_PRODUCT_GUILLEMOT_DALEADER 0xa300 /* DA Leader */ +#define USB_PRODUCT_GUILLEMOT_HWGUSB254 0xe000 /* HWGUSB2-54 WLAN */ +#define USB_PRODUCT_GUILLEMOT_HWGUSB254LB 0xe010 /* HWGUSB2-54-LB */ +#define USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP 0xe020 /* HWGUSB2-54V2-AP */ + +/* Hagiwara products */ +#define USB_PRODUCT_HAGIWARA_FGSM 0x0002 /* FlashGate SmartMedia Card Reader */ +#define USB_PRODUCT_HAGIWARA_FGCF 0x0003 /* FlashGate CompactFlash Card Reader */ +#define USB_PRODUCT_HAGIWARA_FG 0x0005 /* FlashGate */ + +/* HAL Corporation products */ +#define USB_PRODUCT_HAL_IMR001 0x0011 /* Crossam2+USB IR commander */ + +/* Handspring, Inc. */ +#define USB_PRODUCT_HANDSPRING_VISOR 0x0100 /* Handspring Visor */ +#define USB_PRODUCT_HANDSPRING_TREO 0x0200 /* Handspring Treo */ +#define USB_PRODUCT_HANDSPRING_TREO600 0x0300 /* Handspring Treo 600 */ + +/* Hauppauge Computer Works */ +#define USB_PRODUCT_HAUPPAUGE_WINTV_USB_FM 0x4d12 /* WinTV USB FM */ + +/* Hawking Technologies products */ +#define USB_PRODUCT_HAWKING_UF100 0x400c /* 10/100 USB Ethernet */ + +/* Hitachi, Ltd. products */ +#define USB_PRODUCT_HITACHI_DVDCAM_DZ_MV100A 0x0004 /* DVD-CAM DZ-MV100A Camcorder */ +#define USB_PRODUCT_HITACHI_DVDCAM_USB 0x001e /* DVDCAM USB HS Interface */ + +/* HP products */ +#define USB_PRODUCT_HP_895C 0x0004 /* DeskJet 895C */ +#define USB_PRODUCT_HP_4100C 0x0101 /* Scanjet 4100C */ +#define USB_PRODUCT_HP_S20 0x0102 /* Photosmart S20 */ +#define USB_PRODUCT_HP_880C 0x0104 /* DeskJet 880C */ +#define USB_PRODUCT_HP_4200C 0x0105 /* ScanJet 4200C */ +#define USB_PRODUCT_HP_CDWRITERPLUS 0x0107 /* CD-Writer Plus */ +#define USB_PRODUCT_HP_KBDHUB 0x010c /* Multimedia Keyboard Hub */ +#define USB_PRODUCT_HP_G55XI 0x0111 /* OfficeJet G55xi */ +#define USB_PRODUCT_HP_HN210W 0x011c /* HN210W 802.11b WLAN */ +#define USB_PRODUCT_HP_49GPLUS 0x0121 /* 49g+ graphing calculator */ +#define USB_PRODUCT_HP_6200C 0x0201 /* ScanJet 6200C */ +#define USB_PRODUCT_HP_S20b 0x0202 /* PhotoSmart S20 */ +#define USB_PRODUCT_HP_815C 0x0204 /* DeskJet 815C */ +#define USB_PRODUCT_HP_3300C 0x0205 /* ScanJet 3300C */ +#define USB_PRODUCT_HP_CDW8200 0x0207 /* CD-Writer Plus 8200e */ +#define USB_PRODUCT_HP_MMKEYB 0x020c /* Multimedia keyboard */ +#define USB_PRODUCT_HP_1220C 0x0212 /* DeskJet 1220C */ +#define USB_PRODUCT_HP_810C 0x0304 /* DeskJet 810C/812C */ +#define USB_PRODUCT_HP_4300C 0x0305 /* Scanjet 4300C */ +#define USB_PRODUCT_HP_CDW4E 0x0307 /* CD-Writer+ CD-4e */ +#define USB_PRODUCT_HP_G85XI 0x0311 /* OfficeJet G85xi */ +#define USB_PRODUCT_HP_1200 0x0317 /* LaserJet 1200 */ +#define USB_PRODUCT_HP_5200C 0x0401 /* Scanjet 5200C */ +#define USB_PRODUCT_HP_830C 0x0404 /* DeskJet 830C */ +#define USB_PRODUCT_HP_3400CSE 0x0405 /* ScanJet 3400cse */ +#define USB_PRODUCT_HP_6300C 0x0601 /* Scanjet 6300C */ +#define USB_PRODUCT_HP_840C 0x0604 /* DeskJet 840c */ +#define USB_PRODUCT_HP_2200C 0x0605 /* ScanJet 2200C */ +#define USB_PRODUCT_HP_5300C 0x0701 /* Scanjet 5300C */ +#define USB_PRODUCT_HP_4400C 0x0705 /* Scanjet 4400C */ +#define USB_PRODUCT_HP_82x0C 0x0b01 /* Scanjet 82x0C */ +#define USB_PRODUCT_HP_2300D 0x0b17 /* Laserjet 2300d */ +#define USB_PRODUCT_HP_970CSE 0x1004 /* Deskjet 970Cse */ +#define USB_PRODUCT_HP_5400C 0x1005 /* Scanjet 5400C */ +#define USB_PRODUCT_HP_2215 0x1016 /* iPAQ 22xx/Jornada 548 */ +#define USB_PRODUCT_HP_568J 0x1116 /* Jornada 568 */ +#define USB_PRODUCT_HP_930C 0x1204 /* DeskJet 930c */ +#define USB_PRODUCT_HP_P2000U 0x1801 /* Inkjet P-2000U */ +#define USB_PRODUCT_HP_640C 0x2004 /* DeskJet 640c */ +#define USB_PRODUCT_HP_4670V 0x3005 /* ScanJet 4670v */ +#define USB_PRODUCT_HP_P1100 0x3102 /* Photosmart P1100 */ +#define USB_PRODUCT_HP_HN210E 0x811c /* Ethernet HN210E */ +#define USB_PRODUCT_HP2_C500 0x6002 /* PhotoSmart C500 */ + +/* HTC products */ +#define USB_PRODUCT_HTC_WINMOBILE 0x00ce /* HTC USB Sync */ +#define USB_PRODUCT_HTC_PPC6700MODEM 0x00cf /* PPC6700 Modem */ +#define USB_PRODUCT_HTC_SMARTPHONE 0x0a51 /* SmartPhone USB Sync */ + +/* HUAWEI products */ +#define USB_PRODUCT_HUAWEI_MOBILE 0x1001 /* Huawei Mobile */ +#define USB_PRODUCT_HUAWEI_E270 0x1003 /* Huawei HSPA modem */ + +/* HUAWEI 3com products */ +#define USB_PRODUCT_HUAWEI3COM_WUB320G 0x0009 /* Aolynk WUB320g */ + +/* IBM Corporation */ +#define USB_PRODUCT_IBM_USBCDROMDRIVE 0x4427 /* USB CD-ROM Drive */ + +/* Imagination Technologies products */ +#define USB_PRODUCT_IMAGINATION_DBX1 0x2107 /* DBX1 DSP core */ + +/* Inside Out Networks products */ +#define USB_PRODUCT_INSIDEOUT_EDGEPORT4 0x0001 /* EdgePort/4 serial ports */ + +/* In-System products */ +#define USB_PRODUCT_INSYSTEM_F5U002 0x0002 /* Parallel printer */ +#define USB_PRODUCT_INSYSTEM_ATAPI 0x0031 /* ATAPI Adapter */ +#define USB_PRODUCT_INSYSTEM_ISD110 0x0200 /* IDE Adapter ISD110 */ +#define USB_PRODUCT_INSYSTEM_ISD105 0x0202 /* IDE Adapter ISD105 */ +#define USB_PRODUCT_INSYSTEM_USBCABLE 0x081a /* USB cable */ +#define USB_PRODUCT_INSYSTEM_STORAGE_V2 0x5701 /* USB Storage Adapter V2 */ + +/* Intel products */ +#define USB_PRODUCT_INTEL_EASYPC_CAMERA 0x0110 /* Easy PC Camera */ +#define USB_PRODUCT_INTEL_TESTBOARD 0x9890 /* 82930 test board */ + +/* Intersil products */ +#define USB_PRODUCT_INTERSIL_PRISM_GT 0x1000 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_INTERSIL_PRISM_2X 0x3642 /* Prism2.x or Atmel WLAN */ + +/* Interpid Control Systems products */ +#define USB_PRODUCT_INTREPIDCS_VALUECAN 0x0601 /* ValueCAN CAN bus interface */ +#define USB_PRODUCT_INTREPIDCS_NEOVI 0x0701 /* NeoVI Blue vehicle bus interface */ + +/* I/O DATA products */ +#define USB_PRODUCT_IODATA_IU_CD2 0x0204 /* DVD Multi-plus unit iU-CD2 */ +#define USB_PRODUCT_IODATA_DVR_UEH8 0x0206 /* DVD Multi-plus unit DVR-UEH8 */ +#define USB_PRODUCT_IODATA_USBSSMRW 0x0314 /* USB-SSMRW SD-card */ +#define USB_PRODUCT_IODATA_USBSDRW 0x031e /* USB-SDRW SD-card */ +#define USB_PRODUCT_IODATA_USBETT 0x0901 /* USB ETT */ +#define USB_PRODUCT_IODATA_USBETTX 0x0904 /* USB ETTX */ +#define USB_PRODUCT_IODATA_USBETTXS 0x0913 /* USB ETTX */ +#define USB_PRODUCT_IODATA_USBWNB11A 0x0919 /* USB WN-B11 */ +#define USB_PRODUCT_IODATA_USBWNB11 0x0922 /* USB Airport WN-B11 */ +#define USB_PRODUCT_IODATA_ETGUS2 0x0930 /* ETG-US2 */ +#define USB_PRODUCT_IODATA_USBRSAQ 0x0a03 /* Serial USB-RSAQ1 */ +#define USB_PRODUCT_IODATA2_USB2SC 0x0a09 /* USB2.0-SCSI Bridge USB2-SC */ + +/* Iomega products */ +#define USB_PRODUCT_IOMEGA_ZIP100 0x0001 /* Zip 100 */ +#define USB_PRODUCT_IOMEGA_ZIP250 0x0030 /* Zip 250 */ + +/* Ituner networks products */ +#define USB_PRODUCT_ITUNERNET_USBLCD2X20 0x0002 /* USB-LCD 2x20 */ + +/* Jablotron products */ +#define USB_PRODUCT_JABLOTRON_PC60B 0x0001 /* PC-60B */ + +/* Jaton products */ +#define USB_PRODUCT_JATON_EDA 0x5704 /* Ethernet */ + +/* JVC products */ +#define USB_PRODUCT_JVC_GR_DX95 0x000a /* GR-DX95 */ +#define USB_PRODUCT_JVC_MP_PRX1 0x3008 /* MP-PRX1 Ethernet */ + +/* JRC products */ +#define USB_PRODUCT_JRC_AH_J3001V_J3002V 0x0001 /* AirH PHONE AH-J3001V/J3002V */ + +/* Kawatsu products */ +#define USB_PRODUCT_KAWATSU_MH4000P 0x0003 /* MiniHub 4000P */ + +/* Keisokugiken Corp. products */ +#define USB_PRODUCT_KEISOKUGIKEN_USBDAQ 0x0068 /* HKS-0200 USBDAQ */ + +/* Kensington products */ +#define USB_PRODUCT_KENSINGTON_ORBIT 0x1003 /* Orbit USB/PS2 trackball */ +#define USB_PRODUCT_KENSINGTON_TURBOBALL 0x1005 /* TurboBall */ + +/* Keyspan products */ +#define USB_PRODUCT_KEYSPAN_USA28_NF 0x0101 /* USA-28 serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA28X_NF 0x0102 /* USA-28X serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19_NF 0x0103 /* USA-19 serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA18_NF 0x0104 /* USA-18 serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA18X_NF 0x0105 /* USA-18X serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19W_NF 0x0106 /* USA-19W serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19 0x0107 /* USA-19 serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA19W 0x0108 /* USA-19W serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA49W_NF 0x0109 /* USA-49W serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA49W 0x010a /* USA-49W serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA19QI_NF 0x010b /* USA-19QI serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19QI 0x010c /* USA-19QI serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA19Q_NF 0x010d /* USA-19Q serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19Q 0x010e /* USA-19Q serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA28 0x010f /* USA-28 serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA28XXB 0x0110 /* USA-28X/XB serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA18 0x0111 /* USA-18 serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA18X 0x0112 /* USA-18X serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA28XB_NF 0x0113 /* USA-28XB serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA28XA_NF 0x0114 /* USA-28XB serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA28XA 0x0115 /* USA-28XA serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA18XA_NF 0x0116 /* USA-18XA serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA18XA 0x0117 /* USA-18XA serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA19QW_NF 0x0118 /* USA-19WQ serial Adapter (no firmware) */ +#define USB_PRODUCT_KEYSPAN_USA19QW 0x0119 /* USA-19WQ serial Adapter */ +#define USB_PRODUCT_KEYSPAN_USA19HA 0x0121 /* USA-19HS serial Adapter */ +#define USB_PRODUCT_KEYSPAN_UIA10 0x0201 /* UIA-10 remote control */ +#define USB_PRODUCT_KEYSPAN_UIA11 0x0202 /* UIA-11 remote control */ + +/* Kingston products */ +#define USB_PRODUCT_KINGSTON_XX1 0x0008 /* Ethernet */ +#define USB_PRODUCT_KINGSTON_KNU101TX 0x000a /* KNU101TX USB Ethernet */ + +/* Kawasaki products */ +#define USB_PRODUCT_KLSI_DUH3E10BT 0x0008 /* USB Ethernet */ +#define USB_PRODUCT_KLSI_DUH3E10BTN 0x0009 /* USB Ethernet */ + +/* Kodak products */ +#define USB_PRODUCT_KODAK_DC220 0x0100 /* Digital Science DC220 */ +#define USB_PRODUCT_KODAK_DC260 0x0110 /* Digital Science DC260 */ +#define USB_PRODUCT_KODAK_DC265 0x0111 /* Digital Science DC265 */ +#define USB_PRODUCT_KODAK_DC290 0x0112 /* Digital Science DC290 */ +#define USB_PRODUCT_KODAK_DC240 0x0120 /* Digital Science DC240 */ +#define USB_PRODUCT_KODAK_DC280 0x0130 /* Digital Science DC280 */ + +/* Konica Corp. Products */ +#define USB_PRODUCT_KONICA_CAMERA 0x0720 /* Digital Color Camera */ + +/* KYE products */ +#define USB_PRODUCT_KYE_NICHE 0x0001 /* Niche mouse */ +#define USB_PRODUCT_KYE_NETSCROLL 0x0003 /* Genius NetScroll mouse */ +#define USB_PRODUCT_KYE_FLIGHT2000 0x1004 /* Flight 2000 joystick */ +#define USB_PRODUCT_KYE_VIVIDPRO 0x2001 /* ColorPage Vivid-Pro scanner */ + +/* Kyocera products */ +#define USB_PRODUCT_KYOCERA_FINECAM_S3X 0x0100 /* Finecam S3x */ +#define USB_PRODUCT_KYOCERA_FINECAM_S4 0x0101 /* Finecam S4 */ +#define USB_PRODUCT_KYOCERA_FINECAM_S5 0x0103 /* Finecam S5 */ +#define USB_PRODUCT_KYOCERA_FINECAM_L3 0x0105 /* Finecam L3 */ +#define USB_PRODUCT_KYOCERA_AHK3001V 0x0203 /* AH-K3001V */ +#define USB_PRODUCT_KYOCERA2_CDMA_MSM_K 0x17da /* Qualcomm Kyocera CDMA Technologies MSM */ + +/* LaCie products */ +#define USB_PRODUCT_LACIE_HD 0xa601 /* Hard Disk */ +#define USB_PRODUCT_LACIE_CDRW 0xa602 /* CD R/W */ + +/* Lexar products */ +#define USB_PRODUCT_LEXAR_JUMPSHOT 0x0001 /* jumpSHOT CompactFlash Reader */ +#define USB_PRODUCT_LEXAR_CF_READER 0xb002 /* USB CF Reader */ + +/* Lexmark products */ +#define USB_PRODUCT_LEXMARK_S2450 0x0009 /* Optra S 2450 */ + +/* Linksys products */ +#define USB_PRODUCT_LINKSYS_MAUSB2 0x0105 /* Camedia MAUSB-2 */ +#define USB_PRODUCT_LINKSYS_USB10TX1 0x200c /* USB10TX */ +#define USB_PRODUCT_LINKSYS_USB10T 0x2202 /* USB10T Ethernet */ +#define USB_PRODUCT_LINKSYS_USB100TX 0x2203 /* USB100TX Ethernet */ +#define USB_PRODUCT_LINKSYS_USB100H1 0x2204 /* USB100H1 Ethernet/HPNA */ +#define USB_PRODUCT_LINKSYS_USB10TA 0x2206 /* USB10TA Ethernet */ +#define USB_PRODUCT_LINKSYS_USB10TX2 0x400b /* USB10TX */ +#define USB_PRODUCT_LINKSYS2_WUSB11 0x2219 /* WUSB11 Wireless Adapter */ +#define USB_PRODUCT_LINKSYS2_USB200M 0x2226 /* USB 2.0 10/100 Ethernet */ +#define USB_PRODUCT_LINKSYS3_WUSB11v28 0x2233 /* WUSB11 v2.8 Wireless Adapter */ +#define USB_PRODUCT_LINKSYS4_USB1000 0x0039 /* USB1000 */ + +/* Logitech products */ +#define USB_PRODUCT_LOGITECH_M2452 0x0203 /* M2452 keyboard */ +#define USB_PRODUCT_LOGITECH_M4848 0x0301 /* M4848 mouse */ +#define USB_PRODUCT_LOGITECH_PAGESCAN 0x040f /* PageScan */ +#define USB_PRODUCT_LOGITECH_QUICKCAMWEB 0x0801 /* QuickCam Web */ +#define USB_PRODUCT_LOGITECH_QUICKCAMPRO 0x0810 /* QuickCam Pro */ +#define USB_PRODUCT_LOGITECH_QUICKCAMEXP 0x0840 /* QuickCam Express */ +#define USB_PRODUCT_LOGITECH_QUICKCAM 0x0850 /* QuickCam */ +#define USB_PRODUCT_LOGITECH_N43 0xc000 /* N43 */ +#define USB_PRODUCT_LOGITECH_N48 0xc001 /* N48 mouse */ +#define USB_PRODUCT_LOGITECH_MBA47 0xc002 /* M-BA47 mouse */ +#define USB_PRODUCT_LOGITECH_WMMOUSE 0xc004 /* WingMan Gaming Mouse */ +#define USB_PRODUCT_LOGITECH_BD58 0xc00c /* BD58 mouse */ +#define USB_PRODUCT_LOGITECH_UN58A 0xc030 /* iFeel Mouse */ +#define USB_PRODUCT_LOGITECH_UN53B 0xc032 /* iFeel MouseMan */ +#define USB_PRODUCT_LOGITECH_WMPAD 0xc208 /* WingMan GamePad Extreme */ +#define USB_PRODUCT_LOGITECH_WMRPAD 0xc20a /* WingMan RumblePad */ +#define USB_PRODUCT_LOGITECH_WMJOY 0xc281 /* WingMan Force joystick */ +#define USB_PRODUCT_LOGITECH_BB13 0xc401 /* USB-PS/2 Trackball */ +#define USB_PRODUCT_LOGITECH_RK53 0xc501 /* Cordless mouse */ +#define USB_PRODUCT_LOGITECH_RB6 0xc503 /* Cordless keyboard */ +#define USB_PRODUCT_LOGITECH_MX700 0xc506 /* Cordless optical mouse */ +#define USB_PRODUCT_LOGITECH_QUICKCAMPRO2 0xd001 /* QuickCam Pro */ + +/* Logitec Corp. products */ +#define USB_PRODUCT_LOGITEC_LDR_H443SU2 0x0033 /* DVD Multi-plus unit LDR-H443SU2 */ +#define USB_PRODUCT_LOGITEC_LDR_H443U2 0x00b3 /* DVD Multi-plus unit LDR-H443U2 */ + +/* Lucent products */ +#define USB_PRODUCT_LUCENT_EVALKIT 0x1001 /* USS-720 evaluation kit */ + +/* Luwen products */ +#define USB_PRODUCT_LUWEN_EASYDISK 0x0005 /* EasyDisc */ + +/* Macally products */ +#define USB_PRODUCT_MACALLY_MOUSE1 0x0101 /* mouse */ + +/* MCT Corp. */ +#define USB_PRODUCT_MCT_HUB0100 0x0100 /* Hub */ +#define USB_PRODUCT_MCT_DU_H3SP_USB232 0x0200 /* D-Link DU-H3SP USB BAY Hub */ +#define USB_PRODUCT_MCT_USB232 0x0210 /* USB-232 Interface */ +#define USB_PRODUCT_MCT_SITECOM_USB232 0x0230 /* Sitecom USB-232 Products */ + +/* Melco, Inc products */ +#define USB_PRODUCT_MELCO_LUATX1 0x0001 /* LUA-TX Ethernet */ +#define USB_PRODUCT_MELCO_LUATX5 0x0005 /* LUA-TX Ethernet */ +#define USB_PRODUCT_MELCO_LUA2TX5 0x0009 /* LUA2-TX Ethernet */ +#define USB_PRODUCT_MELCO_LUAKTX 0x0012 /* LUA-KTX Ethernet */ +#define USB_PRODUCT_MELCO_DUBPXXG 0x001c /* USB-IDE Bridge: DUB-PxxG */ +#define USB_PRODUCT_MELCO_LUAU2KTX 0x003d /* LUA-U2-KTX Ethernet */ +#define USB_PRODUCT_MELCO_KG54YB 0x005e /* WLI-U2-KG54-YB WLAN */ +#define USB_PRODUCT_MELCO_KG54 0x0066 /* WLI-U2-KG54 WLAN */ +#define USB_PRODUCT_MELCO_KG54AI 0x0067 /* WLI-U2-KG54-AI WLAN */ +#define USB_PRODUCT_MELCO_NINWIFI 0x008b /* Nintendo Wi-Fi */ +#define USB_PRODUCT_MELCO_PCOPRS1 0x00b3 /* PC-OP-RS1 RemoteStation */ +#define USB_PRODUCT_MELCO_SG54HP 0x00d8 /* WLI-U2-SG54HP */ +#define USB_PRODUCT_MELCO_G54HP 0x00d9 /* WLI-U2-G54HP */ +#define USB_PRODUCT_MELCO_KG54L 0x00da /* WLI-U2-KG54L */ + +/* Merlin products */ +#define USB_PRODUCT_MERLIN_V620 0x1110 /* Merlin V620 */ + +/* MetaGeek products */ +#define USB_PRODUCT_METAGEEK_WISPY1B 0x083e /* MetaGeek Wi-Spy */ +#define USB_PRODUCT_METAGEEK_WISPY24X 0x083f /* MetaGeek Wi-Spy 2.4x */ + +/* Metricom products */ +#define USB_PRODUCT_METRICOM_RICOCHET_GS 0x0001 /* Ricochet GS */ + +/* MGE UPS Systems */ +#define USB_PRODUCT_MGE_UPS1 0x0001 /* MGE UPS SYSTEMS PROTECTIONCENTER 1 */ +#define USB_PRODUCT_MGE_UPS2 0xffff /* MGE UPS SYSTEMS PROTECTIONCENTER 2 */ + +/* Micro Star International products */ +#define USB_PRODUCT_MSI_BT_DONGLE 0x1967 /* Bluetooth USB dongle */ +#define USB_PRODUCT_MSI_UB11B 0x6823 /* UB11B */ +#define USB_PRODUCT_MSI_RT2570 0x6861 /* RT2570 */ +#define USB_PRODUCT_MSI_RT2570_2 0x6865 /* RT2570 */ +#define USB_PRODUCT_MSI_RT2570_3 0x6869 /* RT2570 */ +#define USB_PRODUCT_MSI_RT2573_1 0x6874 /* RT2573 */ +#define USB_PRODUCT_MSI_RT2573_2 0x6877 /* RT2573 */ +#define USB_PRODUCT_MSI_RT2573_3 0xa861 /* RT2573 */ +#define USB_PRODUCT_MSI_RT2573_4 0xa874 /* RT2573 */ + +/* Microdia products */ +#define USB_PRODUCT_MICRODIA_TWINKLECAM 0x600d /* TwinkleCam USB camera */ + +/* Microsoft products */ +#define USB_PRODUCT_MICROSOFT_SIDEPREC 0x0008 /* SideWinder Precision Pro */ +#define USB_PRODUCT_MICROSOFT_INTELLIMOUSE 0x0009 /* IntelliMouse */ +#define USB_PRODUCT_MICROSOFT_NATURALKBD 0x000b /* Natural Keyboard Elite */ +#define USB_PRODUCT_MICROSOFT_DDS80 0x0014 /* Digital Sound System 80 */ +#define USB_PRODUCT_MICROSOFT_SIDEWINDER 0x001a /* Sidewinder Precision Racing Wheel */ +#define USB_PRODUCT_MICROSOFT_INETPRO 0x001c /* Internet Keyboard Pro */ +#define USB_PRODUCT_MICROSOFT_TBEXPLORER 0x0024 /* Trackball Explorer */ +#define USB_PRODUCT_MICROSOFT_INTELLIEYE 0x0025 /* IntelliEye mouse */ +#define USB_PRODUCT_MICROSOFT_INETPRO2 0x002b /* Internet Keyboard Pro */ +#define USB_PRODUCT_MICROSOFT_MN510 0x006e /* MN510 Wireless */ +#define USB_PRODUCT_MICROSOFT_MN110 0x007a /* 10/100 USB NIC */ +#define USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE 0x008c /* Wireless Optical IntelliMouse */ +#define USB_PRODUCT_MICROSOFT_WLNOTEBOOK 0x00b9 /* Wireless Optical Mouse (Model 1023) */ +#define USB_PRODUCT_MICROSOFT_WLNOTEBOOK2 0x00e1 /* Wireless Optical Mouse 3000 (Model 1056) */ +#define USB_PRODUCT_MICROSOFT_WLNOTEBOOK3 0x00d2 /* Wireless Optical Mouse 3000 (Model 1049) */ +#define USB_PRODUCT_MICROSOFT_WLUSBMOUSE 0x00b9 /* Wireless USB Mouse */ +#define USB_PRODUCT_MICROSOFT_XBOX360 0x0292 /* XBOX 360 WLAN */ + +/* Microtech products */ +#define USB_PRODUCT_MICROTECH_SCSIDB25 0x0004 /* USB-SCSI-DB25 */ +#define USB_PRODUCT_MICROTECH_SCSIHD50 0x0005 /* USB-SCSI-HD50 */ +#define USB_PRODUCT_MICROTECH_DPCM 0x0006 /* USB CameraMate */ +#define USB_PRODUCT_MICROTECH_FREECOM 0xfc01 /* Freecom USB-IDE */ + +/* Microtek products */ +#define USB_PRODUCT_MICROTEK_336CX 0x0094 /* Phantom 336CX - C3 scanner */ +#define USB_PRODUCT_MICROTEK_X6U 0x0099 /* ScanMaker X6 - X6U */ +#define USB_PRODUCT_MICROTEK_C6 0x009a /* Phantom C6 scanner */ +#define USB_PRODUCT_MICROTEK_336CX2 0x00a0 /* Phantom 336CX - C3 scanner */ +#define USB_PRODUCT_MICROTEK_V6USL 0x00a3 /* ScanMaker V6USL */ +#define USB_PRODUCT_MICROTEK_V6USL2 0x80a3 /* ScanMaker V6USL */ +#define USB_PRODUCT_MICROTEK_V6UL 0x80ac /* ScanMaker V6UL */ + +/* Microtune, Inc. products */ +#define USB_PRODUCT_MICROTUNE_BT_DONGLE 0x1000 /* Bluetooth USB dongle */ + +/* Midiman products */ +#define USB_PRODUCT_MIDIMAN_MIDISPORT2X2 0x1001 /* Midisport 2x2 */ + +/* MindsAtWork products */ +#define USB_PRODUCT_MINDSATWORK_WALLET 0x0001 /* Digital Wallet */ + +/* Minolta Co., Ltd. */ +#define USB_PRODUCT_MINOLTA_2300 0x4001 /* Dimage 2300 */ +#define USB_PRODUCT_MINOLTA_S304 0x4007 /* Dimage S304 */ +#define USB_PRODUCT_MINOLTA_X 0x4009 /* Dimage X */ +#define USB_PRODUCT_MINOLTA_5400 0x400e /* Dimage 5400 */ +#define USB_PRODUCT_MINOLTA_F300 0x4011 /* Dimage F300 */ +#define USB_PRODUCT_MINOLTA_E223 0x4017 /* Dimage E223 */ + +/* Mitsumi products */ +#define USB_PRODUCT_MITSUMI_CDRRW 0x0000 /* CD-R/RW Drive */ +#define USB_PRODUCT_MITSUMI_BT_DONGLE 0x641f /* Bluetooth USB dongle */ +#define USB_PRODUCT_MITSUMI_FDD 0x6901 /* USB FDD */ + +/* Mobility products */ +#define USB_PRODUCT_MOBILITY_EA 0x0204 /* Ethernet */ +#define USB_PRODUCT_MOBILITY_EASIDOCK 0x0304 /* EasiDock Ethernet */ + +/* MosChip products */ +#define USB_PRODUCT_MOSCHIP_MCS7703 0x7703 /* MCS7703 Serial Port Adapter */ +#define USB_PRODUCT_MOSCHIP_MCS7830 0x7830 /* MCS7830 Ethernet */ + +/* Motorola products */ +#define USB_PRODUCT_MOTOROLA_MC141555 0x1555 /* MC141555 hub controller */ +#define USB_PRODUCT_MOTOROLA_SB4100 0x4100 /* SB4100 USB Cable Modem */ +#define USB_PRODUCT_MOTOROLA2_A41XV32X 0x2a22 /* A41x/V32x Mobile Phones */ +#define USB_PRODUCT_MOTOROLA2_E398 0x4810 /* E398 Mobile Phone */ +#define USB_PRODUCT_MOTOROLA2_USBLAN 0x600c /* USBLAN */ +#define USB_PRODUCT_MOTOROLA2_USBLAN2 0x6027 /* USBLAN */ + +/* MultiTech products */ +#define USB_PRODUCT_MULTITECH_ATLAS 0xf101 /* MT5634ZBA-USB modem */ + +/* Mustek products */ +#define USB_PRODUCT_MUSTEK_1200CU 0x0001 /* 1200 CU scanner */ +#define USB_PRODUCT_MUSTEK_600CU 0x0002 /* 600 CU scanner */ +#define USB_PRODUCT_MUSTEK_1200USB 0x0003 /* 1200 USB scanner */ +#define USB_PRODUCT_MUSTEK_1200UB 0x0006 /* 1200 UB scanner */ +#define USB_PRODUCT_MUSTEK_1200USBPLUS 0x0007 /* 1200 USB Plus scanner */ +#define USB_PRODUCT_MUSTEK_1200CUPLUS 0x0008 /* 1200 CU Plus scanner */ +#define USB_PRODUCT_MUSTEK_BEARPAW1200F 0x0010 /* BearPaw 1200F scanner */ +#define USB_PRODUCT_MUSTEK_BEARPAW1200TA 0x021e /* BearPaw 1200TA scanner */ +#define USB_PRODUCT_MUSTEK_600USB 0x0873 /* 600 USB scanner */ +#define USB_PRODUCT_MUSTEK_MDC800 0xa800 /* MDC-800 digital camera */ + +/* M-Systems products */ +#define USB_PRODUCT_MSYSTEMS_DISKONKEY 0x0010 /* DiskOnKey */ +#define USB_PRODUCT_MSYSTEMS_DISKONKEY2 0x0011 /* DiskOnKey */ + +/* Myson products */ +#define USB_PRODUCT_MYSON_HEDEN 0x8818 /* USB-IDE */ + +/* National Semiconductor */ +#define USB_PRODUCT_NATIONAL_BEARPAW1200 0x1000 /* BearPaw 1200 */ +#define USB_PRODUCT_NATIONAL_BEARPAW2400 0x1001 /* BearPaw 2400 */ + +/* NEC products */ +#define USB_PRODUCT_NEC_HUB 0x55aa /* hub */ +#define USB_PRODUCT_NEC_HUB_B 0x55ab /* hub */ + +/* NEODIO products */ +#define USB_PRODUCT_NEODIO_ND3260 0x3260 /* 8-in-1 Multi-format Flash Controller */ +#define USB_PRODUCT_NEODIO_ND5010 0x5010 /* Multi-format Flash Controller */ + +/* Netac products */ +#define USB_PRODUCT_NETAC_CF_CARD 0x1060 /* USB-CF-Card */ +#define USB_PRODUCT_NETAC_ONLYDISK 0x0003 /* OnlyDisk */ + +/* NetChip Technology Products */ +#define USB_PRODUCT_NETCHIP_TURBOCONNECT 0x1080 /* Turbo-Connect */ +#define USB_PRODUCT_NETCHIP_CLIK_40 0xa140 /* USB Clik! 40 */ +#define USB_PRODUCT_NETCHIP_ETHERNETGADGET 0xa4a2 /* Linux Ethernet/RNDIS gadget on pxa210/25x/26x */ + +/* Netgear products */ +#define USB_PRODUCT_NETGEAR_EA101 0x1001 /* Ethernet */ +#define USB_PRODUCT_NETGEAR_EA101X 0x1002 /* Ethernet */ +#define USB_PRODUCT_NETGEAR_FA101 0x1020 /* Ethernet 10/100, USB1.1 */ +#define USB_PRODUCT_NETGEAR_FA120 0x1040 /* USB 2.0 Ethernet */ +#define USB_PRODUCT_NETGEAR_WG111V2_2 0x4240 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_NETGEAR_WG111U 0x4300 /* WG111U */ +#define USB_PRODUCT_NETGEAR_WG111U_NF 0x4301 /* WG111U (no firmware) */ +#define USB_PRODUCT_NETGEAR2_MA101 0x4100 /* MA101 */ +#define USB_PRODUCT_NETGEAR2_MA101B 0x4102 /* MA101 Rev B */ +#define USB_PRODUCT_NETGEAR3_WG111T 0x4250 /* WG111T */ +#define USB_PRODUCT_NETGEAR3_WG111T_NF 0x4251 /* WG111T (no firmware) */ +#define USB_PRODUCT_NETGEAR3_WPN111 0x5f00 /* WPN111 */ +#define USB_PRODUCT_NETGEAR3_WPN111_NF 0x5f01 /* WPN111 (no firmware) */ + +/* Nikon products */ +#define USB_PRODUCT_NIKON_E990 0x0102 /* Digital Camera E990 */ +#define USB_PRODUCT_NIKON_LS40 0x4000 /* CoolScan LS40 ED */ +#define USB_PRODUCT_NIKON_D300 0x041a /* Digital Camera D300 */ + +/* NovaTech Products */ +#define USB_PRODUCT_NOVATECH_NV902 0x9020 /* NovaTech NV-902W */ +#define USB_PRODUCT_NOVATECH_RT2573 0x9021 /* RT2573 */ + +/* Novatel Wireless products */ +#define USB_PRODUCT_NOVATEL_V640 0x1100 /* Merlin V620 */ +#define USB_PRODUCT_NOVATEL_CDMA_MODEM 0x1110 /* Novatel Wireless Merlin CDMA */ +#define USB_PRODUCT_NOVATEL_V620 0x1110 /* Merlin V620 */ +#define USB_PRODUCT_NOVATEL_V740 0x1120 /* Merlin V740 */ +#define USB_PRODUCT_NOVATEL_V720 0x1130 /* Merlin V720 */ +#define USB_PRODUCT_NOVATEL_U740 0x1400 /* Merlin U740 */ +#define USB_PRODUCT_NOVATEL_U740_2 0x1410 /* Merlin U740 */ +#define USB_PRODUCT_NOVATEL_U870 0x1420 /* Merlin U870 */ +#define USB_PRODUCT_NOVATEL_XU870 0x1430 /* Merlin XU870 */ +#define USB_PRODUCT_NOVATEL_X950D 0x1450 /* Merlin X950D */ +#define USB_PRODUCT_NOVATEL_ES620 0x2100 /* ES620 CDMA */ +#define USB_PRODUCT_NOVATEL_U720 0x2110 /* Merlin U720 */ +#define USB_PRODUCT_NOVATEL_U727 0x4100 /* Merlin U727 CDMA */ +#define USB_PRODUCT_NOVATEL_U950D 0x4400 /* Novatel MC950D HSUPA */ +#define USB_PRODUCT_NOVATEL_ZEROCD 0x5010 /* Novatel ZeroCD */ +#define USB_PRODUCT_NOVATEL2_FLEXPACKGPS 0x0100 /* NovAtel FlexPack GPS receiver */ + +/* Merlin products */ +#define USB_PRODUCT_MERLIN_V620 0x1110 /* Merlin V620 */ + +/* Olympus products */ +#define USB_PRODUCT_OLYMPUS_C1 0x0102 /* C-1 Digital Camera */ +#define USB_PRODUCT_OLYMPUS_C700 0x0105 /* C-700 Ultra Zoom */ + +/* OmniVision Technologies, Inc. products */ +#define USB_PRODUCT_OMNIVISION_OV511 0x0511 /* OV511 Camera */ +#define USB_PRODUCT_OMNIVISION_OV511PLUS 0xa511 /* OV511+ Camera */ + +/* OnSpec Electronic, Inc. */ +#define USB_PRODUCT_ONSPEC_MDCFE_B_CF_READER 0xa000 /* MDCFE-B USB CF Reader */ +#define USB_PRODUCT_ONSPEC_CFMS_RW 0xa001 /* SIIG/Datafab Memory Stick+CF Reader/Writer */ +#define USB_PRODUCT_ONSPEC_READER 0xa003 /* Datafab-based Reader */ +#define USB_PRODUCT_ONSPEC_CFSM_READER 0xa005 /* PNY/Datafab CF+SM Reader */ +#define USB_PRODUCT_ONSPEC_CFSM_READER2 0xa006 /* Simple Tech/Datafab CF+SM Reader */ +#define USB_PRODUCT_ONSPEC_MDSM_B_READER 0xa103 /* MDSM-B reader */ +#define USB_PRODUCT_ONSPEC_CFSM_COMBO 0xa109 /* USB to CF + SM Combo (LC1) */ +#define USB_PRODUCT_ONSPEC_UCF100 0xa400 /* FlashLink UCF-100 CompactFlash Reader */ +#define USB_PRODUCT_ONSPEC2_IMAGEMATE_SDDR55 0xa103 /* ImageMate SDDR55 */ + +/* Option products */ +#define USB_PRODUCT_OPTION_VODAFONEMC3G 0x5000 /* Vodafone Mobile Connect 3G datacard */ +#define USB_PRODUCT_OPTION_GT3G 0x6000 /* GlobeTrotter 3G datacard */ +#define USB_PRODUCT_OPTION_GT3GQUAD 0x6300 /* GlobeTrotter 3G QUAD datacard */ +#define USB_PRODUCT_OPTION_GT3GPLUS 0x6600 /* GlobeTrotter 3G+ datacard */ +#define USB_PRODUCT_OPTION_GTMAX36 0x6701 /* GlobeTrotter Max 3.6 Modem */ + +/* OQO */ +#define USB_PRODUCT_OQO_WIFI01 0x0002 /* model 01 WiFi interface */ +#define USB_PRODUCT_OQO_BT01 0x0003 /* model 01 Bluetooth interface */ +#define USB_PRODUCT_OQO_ETHER01PLUS 0x7720 /* model 01+ Ethernet */ +#define USB_PRODUCT_OQO_ETHER01 0x8150 /* model 01 Ethernet interface */ + +/* Palm Computing, Inc. product */ +#define USB_PRODUCT_PALM_SERIAL 0x0080 /* USB Serial */ +#define USB_PRODUCT_PALM_M500 0x0001 /* Palm m500 */ +#define USB_PRODUCT_PALM_M505 0x0002 /* Palm m505 */ +#define USB_PRODUCT_PALM_M515 0x0003 /* Palm m515 */ +#define USB_PRODUCT_PALM_I705 0x0020 /* Palm i705 */ +#define USB_PRODUCT_PALM_TUNGSTEN_Z 0x0031 /* Palm Tungsten Z */ +#define USB_PRODUCT_PALM_M125 0x0040 /* Palm m125 */ +#define USB_PRODUCT_PALM_M130 0x0050 /* Palm m130 */ +#define USB_PRODUCT_PALM_TUNGSTEN_T 0x0060 /* Palm Tungsten T */ +#define USB_PRODUCT_PALM_ZIRE31 0x0061 /* Palm Zire 31 */ +#define USB_PRODUCT_PALM_ZIRE 0x0070 /* Palm Zire */ + +/* Panasonic products */ +#define USB_PRODUCT_PANASONIC_LS120CAM 0x0901 /* LS-120 Camera */ +#define USB_PRODUCT_PANASONIC_KXL840AN 0x0d01 /* CD-R Drive KXL-840AN */ +#define USB_PRODUCT_PANASONIC_KXLRW32AN 0x0d09 /* CD-R Drive KXL-RW32AN */ +#define USB_PRODUCT_PANASONIC_KXLCB20AN 0x0d0a /* CD-R Drive KXL-CB20AN */ +#define USB_PRODUCT_PANASONIC_KXLCB35AN 0x0d0e /* DVD-ROM & CD-R/RW */ +#define USB_PRODUCT_PANASONIC_SDCAAE 0x1b00 /* MultiMediaCard */ + +/* Peracom products */ +#define USB_PRODUCT_PERACOM_SERIAL1 0x0001 /* Serial */ +#define USB_PRODUCT_PERACOM_ENET 0x0002 /* Ethernet */ +#define USB_PRODUCT_PERACOM_ENET3 0x0003 /* At Home Ethernet */ +#define USB_PRODUCT_PERACOM_ENET2 0x0005 /* Ethernet */ + +/* Philips products */ +#define USB_PRODUCT_PHILIPS_DSS350 0x0101 /* DSS 350 Digital Speaker System */ +#define USB_PRODUCT_PHILIPS_DSS 0x0104 /* DSS XXX Digital Speaker System */ +#define USB_PRODUCT_PHILIPS_HUB 0x0201 /* hub */ +#define USB_PRODUCT_PHILIPS_PCA646VC 0x0303 /* PCA646VC PC Camera */ +#define USB_PRODUCT_PHILIPS_PCVC680K 0x0308 /* PCVC680K Vesta Pro PC Camera */ +#define USB_PRODUCT_PHILIPS_DSS150 0x0471 /* DSS 150 Digital Speaker System */ +#define USB_PRODUCT_PHILIPS_SNU5600 0x1236 /* SNU5600 */ +#define USB_PRODUCT_PHILIPS_UM10016 0x1552 /* ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit */ +#define USB_PRODUCT_PHILIPS_DIVAUSB 0x1801 /* DIVA USB mp3 player */ + +/* Philips Semiconductor products */ +#define USB_PRODUCT_PHILIPSSEMI_HUB1122 0x1122 /* hub */ + +/* P.I. Engineering products */ +#define USB_PRODUCT_PIENGINEERING_PS2USB 0x020b /* PS2 to Mac USB Adapter */ + +/* Planex Communications products */ +#define USB_PRODUCT_PLANEX_GW_US11H 0x14ea /* GW-US11H WLAN */ +#define USB_PRODUCT_PLANEX2_GW_US11S 0x3220 /* GW-US11S WLAN */ +#define USB_PRODUCT_PLANEX2_GW_US54GXS 0x5303 /* GW-US54GXS WLAN */ +#define USB_PRODUCT_PLANEX2_GWUS54HP 0xab01 /* GW-US54HP */ +#define USB_PRODUCT_PLANEX2_GWUS54MINI2 0xab50 /* GW-US54Mini2 */ +#define USB_PRODUCT_PLANEX2_GWUS54SG 0xc002 /* GW-US54SG */ +#define USB_PRODUCT_PLANEX2_GWUS54GZL 0xc007 /* GW-US54GZL */ +#define USB_PRODUCT_PLANEX2_GWUS54GD 0xed01 /* GW-US54GD */ +#define USB_PRODUCT_PLANEX2_GWUSMM 0xed02 /* GW-USMM */ +#define USB_PRODUCT_PLANEX3_GWUS54GZ 0xab10 /* GW-US54GZ */ +#define USB_PRODUCT_PLANEX3_GU1000T 0xab11 /* GU-1000T */ +#define USB_PRODUCT_PLANEX3_GWUS54MINI 0xab13 /* GW-US54Mini */ + +/* Plextor Corp. */ +#define USB_PRODUCT_PLEXTOR_40_12_40U 0x0011 /* PlexWriter 40/12/40U */ + +/* PLX products */ +#define USB_PRODUCT_PLX_TESTBOARD 0x9060 /* test board */ + +/* PNY products */ +#define USB_PRODUCT_PNY_ATTACHE2 0x0010 /* USB 2.0 Flash Drive */ + +/* PortGear products */ +#define USB_PRODUCT_PORTGEAR_EA8 0x0008 /* Ethernet */ +#define USB_PRODUCT_PORTGEAR_EA9 0x0009 /* Ethernet */ + +/* Portsmith products */ +#define USB_PRODUCT_PORTSMITH_EEA 0x3003 /* Express Ethernet */ + +/* Primax products */ +#define USB_PRODUCT_PRIMAX_G2X300 0x0300 /* G2-200 scanner */ +#define USB_PRODUCT_PRIMAX_G2E300 0x0301 /* G2E-300 scanner */ +#define USB_PRODUCT_PRIMAX_G2300 0x0302 /* G2-300 scanner */ +#define USB_PRODUCT_PRIMAX_G2E3002 0x0303 /* G2E-300 scanner */ +#define USB_PRODUCT_PRIMAX_9600 0x0340 /* Colorado USB 9600 scanner */ +#define USB_PRODUCT_PRIMAX_600U 0x0341 /* Colorado 600u scanner */ +#define USB_PRODUCT_PRIMAX_6200 0x0345 /* Visioneer 6200 scanner */ +#define USB_PRODUCT_PRIMAX_19200 0x0360 /* Colorado USB 19200 scanner */ +#define USB_PRODUCT_PRIMAX_1200U 0x0361 /* Colorado 1200u scanner */ +#define USB_PRODUCT_PRIMAX_G600 0x0380 /* G2-600 scanner */ +#define USB_PRODUCT_PRIMAX_636I 0x0381 /* ReadyScan 636i */ +#define USB_PRODUCT_PRIMAX_G2600 0x0382 /* G2-600 scanner */ +#define USB_PRODUCT_PRIMAX_G2E600 0x0383 /* G2E-600 scanner */ +#define USB_PRODUCT_PRIMAX_COMFORT 0x4d01 /* Comfort */ +#define USB_PRODUCT_PRIMAX_MOUSEINABOX 0x4d02 /* Mouse-in-a-Box */ +#define USB_PRODUCT_PRIMAX_PCGAUMS1 0x4d04 /* Sony PCGA-UMS1 */ + +/* Prolific products */ +#define USB_PRODUCT_PROLIFIC_PL2301 0x0000 /* PL2301 Host-Host interface */ +#define USB_PRODUCT_PROLIFIC_PL2302 0x0001 /* PL2302 Host-Host interface */ +#define USB_PRODUCT_PROLIFIC_RSAQ2 0x04bb /* PL2303 Serial (IODATA USB-RSAQ2) */ +#define USB_PRODUCT_PROLIFIC_PL2303 0x2303 /* PL2303 Serial (ATEN/IOGEAR UC232A) */ +#define USB_PRODUCT_PROLIFIC_PL2305 0x2305 /* Parallel printer */ +#define USB_PRODUCT_PROLIFIC_ATAPI4 0x2307 /* ATAPI-4 Controller */ +#define USB_PRODUCT_PROLIFIC_PL2501 0x2501 /* PL2501 Host-Host interface */ +#define USB_PRODUCT_PROLIFIC_PHAROS 0xaaa0 /* Prolific Pharos */ +#define USB_PRODUCT_PROLIFIC_RSAQ3 0xaaa2 /* PL2303 Serial Adapter (IODATA USB-RSAQ3) */ +#define USB_PRODUCT_PROLIFIC2_WSIM 0x2001 /* Willcom WSIM */ + +/* Putercom products */ +#define USB_PRODUCT_PUTERCOM_UPA100 0x047e /* USB-1284 BRIDGE */ + +/* Qcom products */ +#define USB_PRODUCT_QCOM_RT2573 0x6196 /* RT2573 */ +#define USB_PRODUCT_QCOM_RT2573_2 0x6229 /* RT2573 */ + +/* Qualcomm products */ +#define USB_PRODUCT_QUALCOMM_CDMA_MSM 0x6000 /* CDMA Technologies MSM phone */ +#define USB_PRODUCT_QUALCOMM2_RWT_FCT 0x3100 /* RWT FCT-CDMA 2000 1xRTT modem */ +#define USB_PRODUCT_QUALCOMM2_CDMA_MSM 0x3196 /* CDMA Technologies MSM modem */ +#define USB_PRODUCT_QUALCOMMINC_CDMA_MSM 0x0001 /* CDMA Technologies MSM modem */ + +/* Qtronix products */ +#define USB_PRODUCT_QTRONIX_980N 0x2011 /* Scorpion-980N keyboard */ + +/* Quickshot products */ +#define USB_PRODUCT_QUICKSHOT_STRIKEPAD 0x6238 /* USB StrikePad */ + +/* Radio Shack */ +#define USB_PRODUCT_RADIOSHACK_USBCABLE 0x4026 /* USB to Serial Cable */ + +/* Rainbow Technologies products */ +#define USB_PRODUCT_RAINBOW_IKEY2000 0x1200 /* i-Key 2000 */ + +/* Ralink Technology products */ +#define USB_PRODUCT_RALINK_RT2570 0x1706 /* RT2500USB Wireless Adapter */ +#define USB_PRODUCT_RALINK_RT2570_2 0x2570 /* RT2500USB Wireless Adapter */ +#define USB_PRODUCT_RALINK_RT2573 0x2573 /* RT2501USB Wireless Adapter */ +#define USB_PRODUCT_RALINK_RT2671 0x2671 /* RT2601USB Wireless Adapter */ +#define USB_PRODUCT_RALINK_RT2570_3 0x9020 /* RT2500USB Wireless Adapter */ +#define USB_PRODUCT_RALINK_RT2573_2 0x9021 /* RT2501USB Wireless Adapter */ + +/* ReakTek products */ +/* Green House and CompUSA OEM this part */ +#define USB_PRODUCT_REALTEK_USBKR100 0x8150 /* USBKR100 USB Ethernet */ + +/* Ricoh products */ +#define USB_PRODUCT_RICOH_VGPVCC2 0x1830 /* VGP-VCC2 Camera */ +#define USB_PRODUCT_RICOH_VGPVCC3 0x1832 /* VGP-VCC3 Camera */ +#define USB_PRODUCT_RICOH_VGPVCC2_2 0x1833 /* VGP-VCC2 Camera */ +#define USB_PRODUCT_RICOH_VGPVCC2_3 0x1834 /* VGP-VCC2 Camera */ +#define USB_PRODUCT_RICOH_VGPVCC7 0x183a /* VGP-VCC7 Camera */ +#define USB_PRODUCT_RICOH_VGPVCC8 0x183b /* VGP-VCC8 Camera */ + +/* Roland products */ +#define USB_PRODUCT_ROLAND_UM1 0x0009 /* UM-1 MIDI I/F */ +#define USB_PRODUCT_ROLAND_UM880N 0x0014 /* EDIROL UM-880 MIDI I/F (native) */ +#define USB_PRODUCT_ROLAND_UM880G 0x0015 /* EDIROL UM-880 MIDI I/F (generic) */ + +/* Rockfire products */ +#define USB_PRODUCT_ROCKFIRE_GAMEPAD 0x2033 /* gamepad 203USB */ + +/* RATOC Systems products */ +#define USB_PRODUCT_RATOC_REXUSB60 0xb000 /* REX-USB60 */ + +/* Sagem products */ +#define USB_PRODUCT_SAGEM_USBSERIAL 0x0027 /* USB-Serial Controller */ +#define USB_PRODUCT_SAGEM_XG760A 0x004a /* XG-760A */ +#define USB_PRODUCT_SAGEM_XG76NA 0x0062 /* XG-76NA */ + +/* Samsung products */ +#define USB_PRODUCT_SAMSUNG_ML6060 0x3008 /* ML-6060 laser printer */ +#define USB_PRODUCT_SAMSUNG_YP_U2 0x5050 /* YP-U2 MP3 Player */ +#define USB_PRODUCT_SAMSUNG_I500 0x6601 /* I500 Palm USB Phone */ + +/* Samsung Techwin products */ +#define USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410 0x000a /* Digimax 410 */ + +/* SanDisk products */ +#define USB_PRODUCT_SANDISK_SDDR05A 0x0001 /* ImageMate SDDR-05a */ +#define USB_PRODUCT_SANDISK_SDDR31 0x0002 /* ImageMate SDDR-31 */ +#define USB_PRODUCT_SANDISK_SDDR05 0x0005 /* ImageMate SDDR-05 */ +#define USB_PRODUCT_SANDISK_SDDR12 0x0100 /* ImageMate SDDR-12 */ +#define USB_PRODUCT_SANDISK_SDDR09 0x0200 /* ImageMate SDDR-09 */ +#define USB_PRODUCT_SANDISK_SDDR75 0x0810 /* ImageMate SDDR-75 */ +#define USB_PRODUCT_SANDISK_SDCZ2_256 0x7104 /* Cruzer Mini 256MB */ +#define USB_PRODUCT_SANDISK_SDCZ4_128 0x7112 /* Cruzer Micro 128MB */ +#define USB_PRODUCT_SANDISK_SDCZ4_256 0x7113 /* Cruzer Micro 256MB */ + +/* Sanyo Electric products */ +#define USB_PRODUCT_SANYO_SCP4900 0x0701 /* Sanyo SCP-4900 USB Phone */ + +/* ScanLogic products */ +#define USB_PRODUCT_SCANLOGIC_SL11R 0x0002 /* SL11R IDE Adapter */ +#define USB_PRODUCT_SCANLOGIC_336CX 0x0300 /* Phantom 336CX - C3 scanner */ + +/* Senao products */ +#define USB_PRODUCT_SENAO_NUB8301 0x2000 /* NUB-8301 */ + +/* ShanTou products */ +#define USB_PRODUCT_SHANTOU_ST268 0x0268 /* ST268 */ +#define USB_PRODUCT_SHANTOU_DM9601 0x9601 /* DM 9601 */ + +/* Shark products */ +#define USB_PRODUCT_SHARK_PA 0x0400 /* Pocket Adapter */ + +/* Sharp products */ +#define USB_PRODUCT_SHARP_SL5500 0x8004 /* Zaurus SL-5500 PDA */ +#define USB_PRODUCT_SHARP_SLA300 0x8005 /* Zaurus SL-A300 PDA */ +#define USB_PRODUCT_SHARP_SL5600 0x8006 /* Zaurus SL-5600 PDA */ +#define USB_PRODUCT_SHARP_SLC700 0x8007 /* Zaurus SL-C700 PDA */ +#define USB_PRODUCT_SHARP_SLC750 0x9031 /* Zaurus SL-C750 PDA */ +#define USB_PRODUCT_SHARP_WZERO3ES 0x9123 /* W-ZERO3 ES Smartphone */ + +/* Shuttle Technology products */ +#define USB_PRODUCT_SHUTTLE_EUSB 0x0001 /* E-USB Bridge */ +#define USB_PRODUCT_SHUTTLE_EUSCSI 0x0002 /* eUSCSI Bridge */ +#define USB_PRODUCT_SHUTTLE_SDDR09 0x0003 /* ImageMate SDDR09 */ +#define USB_PRODUCT_SHUTTLE_EUSBCFSM 0x0005 /* eUSB SmartMedia / CompactFlash Adapter */ +#define USB_PRODUCT_SHUTTLE_ZIOMMC 0x0006 /* eUSB MultiMediaCard Adapter */ +#define USB_PRODUCT_SHUTTLE_HIFD 0x0007 /* Sony Hifd */ +#define USB_PRODUCT_SHUTTLE_EUSBATAPI 0x0009 /* eUSB ATA/ATAPI Adapter */ +#define USB_PRODUCT_SHUTTLE_CF 0x000a /* eUSB CompactFlash Adapter */ +#define USB_PRODUCT_SHUTTLE_EUSCSI_B 0x000b /* eUSCSI Bridge */ +#define USB_PRODUCT_SHUTTLE_EUSCSI_C 0x000c /* eUSCSI Bridge */ +#define USB_PRODUCT_SHUTTLE_CDRW 0x0101 /* CD-RW Device */ +#define USB_PRODUCT_SHUTTLE_EUSBORCA 0x0325 /* eUSB ORCA Quad Reader */ + +/* Siemens products */ +#define USB_PRODUCT_SIEMENS_SPEEDSTREAM 0x1001 /* SpeedStream */ +#define USB_PRODUCT_SIEMENS_SPEEDSTREAM22 0x1022 /* SpeedStream 1022 */ +#define USB_PRODUCT_SIEMENS2_WLL013 0x001b /* WLL013 */ +#define USB_PRODUCT_SIEMENS2_ES75 0x0034 /* GSM module MC35 */ +#define USB_PRODUCT_SIEMENS2_WL54G 0x3c06 /* 54g USB Network Adapter */ +#define USB_PRODUCT_SIEMENS3_SX1 0x0001 /* SX1 */ +#define USB_PRODUCT_SIEMENS3_X65 0x0003 /* X65 */ +#define USB_PRODUCT_SIEMENS3_X75 0x0004 /* X75 */ + +/* Sierra Wireless products */ +#define USB_PRODUCT_SIERRA_AIRCARD580 0x0112 /* Sierra Wireless AirCard 580 */ +#define USB_PRODUCT_SIERRA_AIRCARD595 0x0019 /* Sierra Wireless AirCard 595 */ +#define USB_PRODUCT_SIERRA_AC595U 0x0120 /* Sierra Wireless AirCard 595U */ +#define USB_PRODUCT_SIERRA_AC597E 0x0021 /* Sierra Wireless AirCard 597E */ +#define USB_PRODUCT_SIERRA_C597 0x0023 /* Sierra Wireless Compass 597 */ +#define USB_PRODUCT_SIERRA_AC880 0x6850 /* Sierra Wireless AirCard 880 */ +#define USB_PRODUCT_SIERRA_AC881 0x6851 /* Sierra Wireless AirCard 881 */ +#define USB_PRODUCT_SIERRA_AC880E 0x6852 /* Sierra Wireless AirCard 880E */ +#define USB_PRODUCT_SIERRA_AC881E 0x6853 /* Sierra Wireless AirCard 881E */ +#define USB_PRODUCT_SIERRA_AC880U 0x6855 /* Sierra Wireless AirCard 880U */ +#define USB_PRODUCT_SIERRA_AC881U 0x6856 /* Sierra Wireless AirCard 881U */ +#define USB_PRODUCT_SIERRA_EM5625 0x0017 /* EM5625 */ +#define USB_PRODUCT_SIERRA_MC5720 0x0218 /* MC5720 Wireless Modem */ +#define USB_PRODUCT_SIERRA_MC5720_2 0x0018 /* MC5720 */ +#define USB_PRODUCT_SIERRA_MC5725 0x0020 /* MC5725 */ +#define USB_PRODUCT_SIERRA_MINI5725 0x0220 /* Sierra Wireless miniPCI 5275 */ +#define USB_PRODUCT_SIERRA_MC8755_2 0x6802 /* MC8755 */ +#define USB_PRODUCT_SIERRA_MC8765 0x6803 /* MC8765 */ +#define USB_PRODUCT_SIERRA_MC8755 0x6804 /* MC8755 */ +#define USB_PRODUCT_SIERRA_AC875U 0x6812 /* AC875U HSDPA USB Modem */ +#define USB_PRODUCT_SIERRA_MC8755_3 0x6813 /* MC8755 HSDPA */ +#define USB_PRODUCT_SIERRA_MC8775_2 0x6815 /* MC8775 */ +#define USB_PRODUCT_SIERRA_AIRCARD875 0x6820 /* Aircard 875 HSDPA */ +#define USB_PRODUCT_SIERRA_MC8780 0x6832 /* MC8780 */ +#define USB_PRODUCT_SIERRA_MC8781 0x6833 /* MC8781 */ +#define USB_PRODUCT_SIERRA_TRUINSTALL 0x0fff /* Aircard Tru Installer */ + +/* Sigmatel products */ +#define USB_PRODUCT_SIGMATEL_I_BEAD100 0x8008 /* i-Bead 100 MP3 Player */ + +/* SIIG products */ +/* Also: Omnidirectional Control Technology products */ +#define USB_PRODUCT_SIIG_DIGIFILMREADER 0x0004 /* DigiFilm-Combo Reader */ +#define USB_PRODUCT_SIIG_WINTERREADER 0x0330 /* WINTERREADER Reader */ +#define USB_PRODUCT_SIIG2_USBTOETHER 0x0109 /* USB TO Ethernet */ +#define USB_PRODUCT_SIIG2_US2308 0x0421 /* Serial */ + +/* Silicom products */ +#define USB_PRODUCT_SILICOM_U2E 0x0001 /* U2E */ +#define USB_PRODUCT_SILICOM_GPE 0x0002 /* Psion Gold Port Ethernet */ + +/* SI Labs */ +#define USB_PRODUCT_SILABS_POLOLU 0x803b /* Pololu Serial */ +#define USB_PRODUCT_SILABS_ARGUSISP 0x8066 /* Argussoft ISP */ +#define USB_PRODUCT_SILABS_CRUMB128 0x807a /* Crumb128 board */ +#define USB_PRODUCT_SILABS_DEGREE 0x80ca /* Degree Controls Inc */ +#define USB_PRODUCT_SILABS_TRAQMATE 0x80ed /* Track Systems Traqmate */ +#define USB_PRODUCT_SILABS_SUUNTO 0x80f6 /* Suunto Sports Instrument */ +#define USB_PRODUCT_SILABS_BURNSIDE 0x813d /* Burnside Telecon Deskmobile */ +#define USB_PRODUCT_SILABS_HELICOM 0x815e /* Helicomm IP-Link 1220-DVM */ +#define USB_PRODUCT_SILABS_CP2102 0xea60 /* SILABS USB UART */ +#define USB_PRODUCT_SILABS_LIPOWSKY_JTAG 0x81c8 /* Lipowsky Baby-JTAG */ +#define USB_PRODUCT_SILABS_LIPOWSKY_LIN 0x81e2 /* Lipowsky Baby-LIN */ +#define USB_PRODUCT_SILABS_LIPOWSKY_HARP 0x8218 /* Lipowsky HARP-1 */ +#define USB_PRODUCT_SILABS_CP2102 0xea60 /* SILABS USB UARTa */ +#define USB_PRODUCT_SILABS_CP210X_2 0xea61 /* CP210x Serial */ +#define USB_PRODUCT_SILABS2_DCU11CLONE 0xaa26 /* DCU-11 clone */ + +/* Silicon Portals Inc. */ +#define USB_PRODUCT_SILICONPORTALS_YAPPH_NF 0x0200 /* YAP Phone (no firmware) */ +#define USB_PRODUCT_SILICONPORTALS_YAPPHONE 0x0201 /* YAP Phone */ + +/* Sirius Technologies products */ +#define USB_PRODUCT_SIRIUS_ROADSTER 0x0001 /* NetComm Roadster II 56 USB */ + +/* Sitecom products */ +#define USB_PRODUCT_SITECOM_LN029 0x182d /* USB 2.0 Ethernet */ +#define USB_PRODUCT_SITECOM_SERIAL 0x2068 /* USB to serial cable (v2) */ +#define USB_PRODUCT_SITECOM2_WL022 0x182d /* WL-022 */ + +/* Sitecom Europe products */ +#define USB_PRODUCT_SITECOMEU_LN028 0x061c /* LN-028 */ +#define USB_PRODUCT_SITECOMEU_WL113 0x9071 /* WL-113 */ +#define USB_PRODUCT_SITECOMEU_ZD1211B 0x9075 /* ZD1211B */ +#define USB_PRODUCT_SITECOMEU_WL172 0x90ac /* WL-172 */ +#define USB_PRODUCT_SITECOMEU_WL113R2 0x9712 /* WL-113 rev 2 */ + +/* Skanhex Technology products */ +#define USB_PRODUCT_SKANHEX_MD_7425 0x410a /* MD 7425 Camera */ +#define USB_PRODUCT_SKANHEX_SX_520Z 0x5200 /* SX 520z Camera */ + +/* SmartBridges products */ +#define USB_PRODUCT_SMARTBRIDGES_SMARTLINK 0x0001 /* SmartLink USB Ethernet */ +#define USB_PRODUCT_SMARTBRIDGES_SMARTNIC 0x0003 /* smartNIC 2 PnP Ethernet */ + +/* SMC products */ +#define USB_PRODUCT_SMC_2102USB 0x0100 /* 10Mbps Ethernet */ +#define USB_PRODUCT_SMC_2202USB 0x0200 /* 10/100 Ethernet */ +#define USB_PRODUCT_SMC_2206USB 0x0201 /* EZ Connect USB Ethernet */ +#define USB_PRODUCT_SMC_2862WG 0xee13 /* EZ Connect Wireless Adapter */ +#define USB_PRODUCT_SMC2_2020HUB 0x2020 /* USB Hub */ +#define USB_PRODUCT_SMC3_2662WUSB 0xa002 /* 2662W-AR Wireless */ + +/* SOHOware products */ +#define USB_PRODUCT_SOHOWARE_NUB100 0x9100 /* 10/100 USB Ethernet */ +#define USB_PRODUCT_SOHOWARE_NUB110 0x9110 /* 10/100 USB Ethernet */ + +/* SOLID YEAR products */ +#define USB_PRODUCT_SOLIDYEAR_KEYBOARD 0x2101 /* Solid Year USB keyboard */ + +/* SONY products */ +#define USB_PRODUCT_SONY_DSC 0x0010 /* DSC cameras */ +#define USB_PRODUCT_SONY_MS_NW_MS7 0x0025 /* Memorystick NW-MS7 */ +#define USB_PRODUCT_SONY_PORTABLE_HDD_V2 0x002b /* Portable USB Harddrive V2 */ +#define USB_PRODUCT_SONY_MSACUS1 0x002d /* Memorystick MSAC-US1 */ +#define USB_PRODUCT_SONY_HANDYCAM 0x002e /* Handycam */ +#define USB_PRODUCT_SONY_MSC 0x0032 /* MSC memory stick slot */ +#define USB_PRODUCT_SONY_CLIE_35 0x0038 /* Sony Clie v3.5 */ +#define USB_PRODUCT_SONY_MS_PEG_N760C 0x0058 /* PEG N760c Memorystick */ +#define USB_PRODUCT_SONY_CLIE_40 0x0066 /* Sony Clie v4.0 */ +#define USB_PRODUCT_SONY_MS_MSC_U03 0x0069 /* Memorystick MSC-U03 */ +#define USB_PRODUCT_SONY_CLIE_40_MS 0x006d /* Sony Clie v4.0 Memory Stick slot */ +#define USB_PRODUCT_SONY_CLIE_S360 0x0095 /* Sony Clie s360 */ +#define USB_PRODUCT_SONY_CLIE_41_MS 0x0099 /* Sony Clie v4.1 Memory Stick slot */ +#define USB_PRODUCT_SONY_CLIE_41 0x009a /* Sony Clie v4.1 */ +#define USB_PRODUCT_SONY_CLIE_NX60 0x00da /* Sony Clie nx60 */ +#define USB_PRODUCT_SONY_CLIE_TH55 0x0144 /* Sony Clie th55 */ +#define USB_PRODUCT_SONY_CLIE_TJ37 0x0169 /* Sony Clie tj37 */ + +/* Sony Ericsson products */ +#define USB_PRODUCT_SONYERICSSON_DCU10 0x0528 /* USB Cable */ + +/* SOURCENEXT products */ +#define USB_PRODUCT_SOURCENEXT_KEIKAI8 0x039f /* KeikaiDenwa 8 */ +#define USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG 0x012e /* KeikaiDenwa 8 with charger */ + +/* SparkLAN products */ +#define USB_PRODUCT_SPARKLAN_RT2573 0x0004 /* RT2573 */ + +/* Sphairon Access Systems GmbH products */ +#define USB_PRODUCT_SPHAIRON_UB801R 0x0110 /* UB801R */ + +/* STMicroelectronics products */ +#define USB_PRODUCT_STMICRO_BIOCPU 0x2016 /* Biometric Coprocessor */ +#define USB_PRODUCT_STMICRO_COMMUNICATOR 0x7554 /* USB Communicator */ + +/* STSN products */ +#define USB_PRODUCT_STSN_STSN0001 0x0001 /* Internet Access Device */ + +/* SUN Corporation products */ +#define USB_PRODUCT_SUNTAC_DS96L 0x0003 /* SUNTAC U-Cable type D2 */ +#define USB_PRODUCT_SUNTAC_PS64P1 0x0005 /* SUNTAC U-Cable type P1 */ +#define USB_PRODUCT_SUNTAC_VS10U 0x0009 /* SUNTAC Slipper U */ +#define USB_PRODUCT_SUNTAC_IS96U 0x000a /* SUNTAC Ir-Trinity */ +#define USB_PRODUCT_SUNTAC_AS64LX 0x000b /* SUNTAC U-Cable type A3 */ +#define USB_PRODUCT_SUNTAC_AS144L4 0x0011 /* SUNTAC U-Cable type A4 */ + +/* Sun Microsystems products */ +#define USB_PRODUCT_SUN_KEYBOARD 0x0005 /* Type 6 USB keyboard */ +/* XXX The above is a North American PC style keyboard possibly */ +#define USB_PRODUCT_SUN_MOUSE 0x0100 /* Type 6 USB mouse */ + +/* Supra products */ +#define USB_PRODUCT_DIAMOND2_SUPRAEXPRESS56K 0x07da /* Supra Express 56K modem */ +#define USB_PRODUCT_DIAMOND2_SUPRA2890 0x0b4a /* SupraMax 2890 56K Modem */ +#define USB_PRODUCT_DIAMOND2_RIO600USB 0x5001 /* Rio 600 USB */ +#define USB_PRODUCT_DIAMOND2_RIO800USB 0x5002 /* Rio 800 USB */ + +/* Surecom Technology products */ +#define USB_PRODUCT_SURECOM_RT2570 0x11f3 /* RT2570 */ +#define USB_PRODUCT_SURECOM_RT2573 0x31f3 /* RT2573 */ + +/* Sweex products */ +#define USB_PRODUCT_SWEEX_ZD1211 0x1809 /* ZD1211 */ + +/* System TALKS, Inc. */ +#define USB_PRODUCT_SYSTEMTALKS_SGCX2UL 0x1920 /* SGC-X2UL */ + +/* Tapwave products */ +#define USB_PRODUCT_TAPWAVE_ZODIAC 0x0100 /* Zodiac */ + +/* Taugagreining products */ +#define USB_PRODUCT_TAUGA_CAMERAMATE 0x0005 /* CameraMate (DPCM_USB) */ + +/* TDK products */ +#define USB_PRODUCT_TDK_UPA9664 0x0115 /* USB-PDC Adapter UPA9664 */ +#define USB_PRODUCT_TDK_UCA1464 0x0116 /* USB-cdmaOne Adapter UCA1464 */ +#define USB_PRODUCT_TDK_UHA6400 0x0117 /* USB-PHS Adapter UHA6400 */ +#define USB_PRODUCT_TDK_UPA6400 0x0118 /* USB-PHS Adapter UPA6400 */ +#define USB_PRODUCT_TDK_BT_DONGLE 0x0309 /* Bluetooth USB dongle */ + +/* TEAC products */ +#define USB_PRODUCT_TEAC_FD05PUB 0x0000 /* FD-05PUB floppy */ + +/* Tekram Technology products */ +#define USB_PRODUCT_TEKRAM_QUICKWLAN 0x1630 /* QuickWLAN */ +#define USB_PRODUCT_TEKRAM_ZD1211_1 0x5630 /* ZD1211 */ +#define USB_PRODUCT_TEKRAM_ZD1211_2 0x6630 /* ZD1211 */ + +/* Telex Communications products */ +#define USB_PRODUCT_TELEX_MIC1 0x0001 /* Enhanced USB Microphone */ + +/* Ten X Technology, Inc. */ +#define USB_PRODUCT_TENX_UAUDIO0 0xf211 /* USB audio headset */ + +/* Texas Intel products */ +#define USB_PRODUCT_TI_UTUSB41 0x1446 /* UT-USB41 hub */ +#define USB_PRODUCT_TI_TUSB2046 0x2046 /* TUSB2046 hub */ + +/* Thrustmaster products */ +#define USB_PRODUCT_THRUST_FUSION_PAD 0xa0a3 /* Fusion Digital Gamepad */ + +/* Topre Corporation products */ +#define USB_PRODUCT_TOPRE_HHKB 0x0100 /* HHKB Professional */ + +/* Toshiba Corporation products */ +#define USB_PRODUCT_TOSHIBA_POCKETPC_E740 0x0706 /* PocketPC e740 */ + +/* Trek Technology products */ +#define USB_PRODUCT_TREK_THUMBDRIVE 0x1111 /* ThumbDrive */ +#define USB_PRODUCT_TREK_MEMKEY 0x8888 /* IBM USB Memory Key */ +#define USB_PRODUCT_TREK_THUMBDRIVE_8MB 0x9988 /* ThumbDrive_8MB */ + +/* Tripp-Lite products */ +#define USB_PRODUCT_TRIPPLITE_U209 0x2008 /* Serial */ + +/* Trumpion products */ +#define USB_PRODUCT_TRUMPION_T33520 0x1001 /* T33520 USB Flash Card Controller */ +#define USB_PRODUCT_TRUMPION_C3310 0x1100 /* Comotron C3310 MP3 player */ +#define USB_PRODUCT_TRUMPION_MP3 0x1200 /* MP3 player */ + +/* TwinMOS */ +#define USB_PRODUCT_TWINMOS_G240 0xa006 /* G240 */ +#define USB_PRODUCT_TWINMOS_MDIV 0x1325 /* Memory Disk IV */ + +/* Ubiquam products */ +#define USB_PRODUCT_UBIQUAM_UALL 0x3100 /* CDMA 1xRTT USB Modem (U-100/105/200/300/520) */ + +/* Ultima products */ +#define USB_PRODUCT_ULTIMA_1200UBPLUS 0x4002 /* 1200 UB Plus scanner */ + +/* UMAX products */ +#define USB_PRODUCT_UMAX_ASTRA1236U 0x0002 /* Astra 1236U Scanner */ +#define USB_PRODUCT_UMAX_ASTRA1220U 0x0010 /* Astra 1220U Scanner */ +#define USB_PRODUCT_UMAX_ASTRA2000U 0x0030 /* Astra 2000U Scanner */ +#define USB_PRODUCT_UMAX_ASTRA2100U 0x0130 /* Astra 2100U Scanner */ +#define USB_PRODUCT_UMAX_ASTRA2200U 0x0230 /* Astra 2200U Scanner */ +#define USB_PRODUCT_UMAX_ASTRA3400 0x0060 /* Astra 3400 Scanner */ + +/* U-MEDIA Communications products */ +#define USB_PRODUCT_UMEDIA_TEW444UBEU 0x3006 /* TEW-444UB EU */ +#define USB_PRODUCT_UMEDIA_TEW444UBEU_NF 0x3007 /* TEW-444UB EU (no firmware) */ +#define USB_PRODUCT_UMEDIA_TEW429UB_A 0x300a /* TEW-429UB_A */ +#define USB_PRODUCT_UMEDIA_TEW429UB 0x300b /* TEW-429UB */ +#define USB_PRODUCT_UMEDIA_TEW429UBC1 0x300d /* TEW-429UB C1 */ +#define USB_PRODUCT_UMEDIA_ALL0298V2 0x3204 /* ALL0298 v2 */ +#define USB_PRODUCT_UMEDIA_AR5523_2 0x3205 /* AR5523 */ +#define USB_PRODUCT_UMEDIA_AR5523_2_NF 0x3206 /* AR5523 (no firmware) */ + +/* Universal Access products */ +#define USB_PRODUCT_UNIACCESS_PANACHE 0x0101 /* Panache Surf USB ISDN Adapter */ + +/* U.S. Robotics products */ +#define USB_PRODUCT_USR_USR5423 0x0121 /* USR5423 WLAN */ + +/* VIA Technologies products */ +#define USB_PRODUCT_VIA_USB2IDEBRIDGE 0x6204 /* USB 2.0 IDE Bridge */ + +/* USI products */ +#define USB_PRODUCT_USI_MC60 0x10c5 /* MC60 Serial */ + +/* VidzMedia products */ +#define USB_PRODUCT_VIDZMEDIA_MONSTERTV 0x4fb1 /* MonsterTV P2H */ + +/* Vision products */ +#define USB_PRODUCT_VISION_VC6452V002 0x0002 /* CPiA Camera */ + +/* Visioneer products */ +#define USB_PRODUCT_VISIONEER_7600 0x0211 /* OneTouch 7600 */ +#define USB_PRODUCT_VISIONEER_5300 0x0221 /* OneTouch 5300 */ +#define USB_PRODUCT_VISIONEER_3000 0x0224 /* Scanport 3000 */ +#define USB_PRODUCT_VISIONEER_6100 0x0231 /* OneTouch 6100 */ +#define USB_PRODUCT_VISIONEER_6200 0x0311 /* OneTouch 6200 */ +#define USB_PRODUCT_VISIONEER_8100 0x0321 /* OneTouch 8100 */ +#define USB_PRODUCT_VISIONEER_8600 0x0331 /* OneTouch 8600 */ + +/* Vivitar products */ +#define USB_PRODUCT_VIVITAR_35XX 0x0003 /* Vivicam 35Xx */ + +/* VTech products */ +#define USB_PRODUCT_VTECH_RT2570 0x3012 /* RT2570 */ +#define USB_PRODUCT_VTECH_ZD1211B 0x3014 /* ZD1211B */ + +/* Wacom products */ +#define USB_PRODUCT_WACOM_CT0405U 0x0000 /* CT-0405-U Tablet */ +#define USB_PRODUCT_WACOM_GRAPHIRE 0x0010 /* Graphire */ +#define USB_PRODUCT_WACOM_GRAPHIRE3_4X5 0x0013 /* Graphire 3 4x5 */ +#define USB_PRODUCT_WACOM_INTUOSA5 0x0021 /* Intuos A5 */ +#define USB_PRODUCT_WACOM_GD0912U 0x0022 /* Intuos 9x12 Graphics Tablet */ +/* WCH products*/ +#define USB_PRODUCT_WCH_CH341SER 0x5523 /* CH341/CH340 USB-Serial Bridge */ +/* Western Digital products */ +#define USB_PRODUCT_WESTERN_COMBO 0x0200 /* Firewire USB Combo */ +#define USB_PRODUCT_WESTERN_EXTHDD 0x0400 /* External HDD */ +#define USB_PRODUCT_WESTERN_HUB 0x0500 /* USB HUB */ +#define USB_PRODUCT_WESTERN_MYBOOK 0x0901 /* MyBook External HDD */ + +/* Windbond Electronics */ +#define USB_PRODUCT_WINBOND_UH104 0x5518 /* 4-port USB Hub */ + +/* WinMaxGroup products */ +#define USB_PRODUCT_WINMAXGROUP_FLASH64MC 0x6660 /* USB Flash Disk 64M-C */ + +/* Wistron NeWeb products */ +#define USB_PRODUCT_WISTRONNEWEB_UR045G 0x0427 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_WISTRONNEWEB_UR055G 0x0711 /* UR055G */ +#define USB_PRODUCT_WISTRONNEWEB_AR5523_1 0x0826 /* AR5523 */ +#define USB_PRODUCT_WISTRONNEWEB_AR5523_1_NF 0x0827 /* AR5523 (no firmware) */ +#define USB_PRODUCT_WISTRONNEWEB_AR5523_2 0x082a /* AR5523 */ +#define USB_PRODUCT_WISTRONNEWEB_AR5523_2_NF 0x0829 /* AR5523 (no firmware) */ + +/* Xerox products */ +#define USB_PRODUCT_XEROX_WCM15 0xffef /* WorkCenter M15 */ + +/* Xirlink products */ +#define USB_PRODUCT_XIRLINK_PCCAM 0x8080 /* IBM PC Camera */ + +/* Xyratex products */ +#define USB_PRODUCT_XYRATEX_PRISM_GT_1 0x2000 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_XYRATEX_PRISM_GT_2 0x2002 /* PrismGT USB 2.0 WLAN */ + +/* Y-E Data products */ +#define USB_PRODUCT_YEDATA_FLASHBUSTERU 0x0000 /* Flashbuster-U */ + +/* Yamaha products */ +#define USB_PRODUCT_YAMAHA_UX256 0x1000 /* UX256 MIDI I/F */ +#define USB_PRODUCT_YAMAHA_UX96 0x1008 /* UX96 MIDI I/F */ +#define USB_PRODUCT_YAMAHA_RTA54I 0x4000 /* NetVolante RTA54i Broadband&ISDN Router */ +#define USB_PRODUCT_YAMAHA_RTA55I 0x4004 /* NetVolante RTA55i Broadband VoIP Router */ +#define USB_PRODUCT_YAMAHA_RTW65B 0x4001 /* NetVolante RTW65b Broadband Wireless Router */ +#define USB_PRODUCT_YAMAHA_RTW65I 0x4002 /* NetVolante RTW65i Broadband&ISDN Wireless Router */ + +/* Yano products */ +#define USB_PRODUCT_YANO_U640MO 0x0101 /* U640MO-03 */ +#define USB_PRODUCT_YANO_FW800HD 0x05fc /* METALWEAR-HDD */ + +/* Z-Com products */ +#define USB_PRODUCT_ZCOM_M4Y750 0x0001 /* M4Y-750 */ +#define USB_PRODUCT_ZCOM_XI725 0x0002 /* XI-725/726 */ +#define USB_PRODUCT_ZCOM_XI735 0x0005 /* XI-735 */ +#define USB_PRODUCT_ZCOM_XG703A 0x0008 /* PrismGT USB 2.0 WLAN */ +#define USB_PRODUCT_ZCOM_ZD1211 0x0011 /* ZD1211 */ +#define USB_PRODUCT_ZCOM_AR5523 0x0012 /* AR5523 */ +#define USB_PRODUCT_ZCOM_AR5523_NF 0x0013 /* AR5523 driver (no firmware) */ +#define USB_PRODUCT_ZCOM_ZD1211B 0x001a /* ZD1211B */ + +/* Zinwell products */ +#define USB_PRODUCT_ZINWELL_RT2570 0x0260 /* RT2570 */ + +/* Zoom Telephonics, Inc. products */ +#define USB_PRODUCT_ZOOM_2986L 0x9700 /* 2986L Fax modem */ + +/* Zoran Microelectronics products */ +#define USB_PRODUCT_ZORAN_EX20DSC 0x4343 /* Digital Camera EX-20 DSC */ + +/* Zydas Technology Corporation products */ +#define USB_PRODUCT_ZYDAS_ZD1211 0x1211 /* ZD1211 WLAN abg */ +#define USB_PRODUCT_ZYDAS_ZD1211B 0x1215 /* ZD1211B */ + +/* ZyXEL Communication Co. products */ +#define USB_PRODUCT_ZYXEL_OMNI56K 0x1500 /* Omni 56K Plus */ +#define USB_PRODUCT_ZYXEL_980N 0x2011 /* Scorpion-980N keyboard */ +#define USB_PRODUCT_ZYXEL_ZYAIRG220 0x3401 /* ZyAIR G-220 */ +#define USB_PRODUCT_ZYXEL_G200V2 0x3407 /* G-200 v2 */ +#define USB_PRODUCT_ZYXEL_AG225H 0x3409 /* AG-225H */ +#define USB_PRODUCT_ZYXEL_M202 0x340a /* M-202 */ +#define USB_PRODUCT_ZYXEL_G220V2 0x340f /* G-220 v2 */ +#define USB_PRODUCT_ZYXEL_G202 0x3410 /* G-202 */ diff --git a/sys/dev/usb2/include/usb2_devtable.h b/sys/dev/usb2/include/usb2_devtable.h new file mode 100644 index 000000000000..168457261ae9 --- /dev/null +++ b/sys/dev/usb2/include/usb2_devtable.h @@ -0,0 +1,10748 @@ +/* $FreeBSD$ */ + +/* + * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * FreeBSD: src/sys/dev/usb/usbdevs,v 1.372 2008/09/19 09:04:06 kevlo Exp + */ +/* $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. + */ + +const struct usb_knowndev usb_knowndevs[] = { + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_HOMECONN, + 0, + "3Com", + "HomeConnect Camera", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_3CREB96, + 0, + "3Com", + "Bluetooth USB Adapter", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250, + 0, + "3Com", + "3C19250 Ethernet Adapter", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_3CRSHEW696, + 0, + "3Com", + "3CRSHEW696 Wireless Adapter", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460, + 0, + "3Com", + "HomeConnect 3C460", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_USR56K, + 0, + "3Com", + "U.S.Robotics 56000 Voice FaxModem Pro", + }, + { + USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, + 0, + "3Com", + "HomeConnect 3C460B", + }, + { + USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075, + 0, + "3Com", + "3CRUSB10075", + }, + { + USB_VENDOR_3COM3, USB_PRODUCT_3COM3_AR5523_1, + 0, + "3Com", + "AR5523", + }, + { + USB_VENDOR_3COM3, USB_PRODUCT_3COM3_AR5523_2, + 0, + "3Com", + "AR5523", + }, + { + USB_VENDOR_3COM3, USB_PRODUCT_3COM3_AR5523_3, + 0, + "3Com", + "AR5523", + }, + { + USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_OFFICECONN, + 0, + "U.S. Robotics", + "3Com OfficeConnect Analog Modem", + }, + { + USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_USRISDN, + 0, + "U.S. Robotics", + "3Com U.S. Robotics Pro ISDN TA", + }, + { + USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_HOMECONN, + 0, + "U.S. Robotics", + "3Com HomeConnect Camera", + }, + { + USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_USR56K, + 0, + "U.S. Robotics", + "U.S. Robotics 56000 Voice FaxModem Pro", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, + 0, + "AboCom Systems", + "XX1", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, + 0, + "AboCom Systems", + "XX2", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450, + 0, + "AboCom Systems", + "URE450 Ethernet Adapter", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, + 0, + "AboCom Systems", + "UFE1000 Fast Ethernet Adapter", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, + 0, + "AboCom Systems", + "1/10/100 Ethernet Adapter", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, + 0, + "AboCom Systems", + "XX4", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, + 0, + "AboCom Systems", + "XX5", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, + 0, + "AboCom Systems", + "XX6", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, + 0, + "AboCom Systems", + "XX7", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RTL8151, + 0, + "AboCom Systems", + "RTL8151", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, + 0, + "AboCom Systems", + "XX8", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, + 0, + "AboCom Systems", + "XX9", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, + 0, + "AboCom Systems", + "UF200 Ethernet", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54, + 0, + "AboCom Systems", + "WL54", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, + 0, + "AboCom Systems", + "XX10", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_BWU613, + 0, + "AboCom Systems", + "BWU613", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM, + 0, + "AboCom Systems", + "HWU54DM", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2, + 0, + "AboCom Systems", + "RT2573", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3, + 0, + "AboCom Systems", + "RT2573", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4, + 0, + "AboCom Systems", + "RT2573", + }, + { + USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700, + 0, + "AboCom Systems", + "WUG2700", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, + 0, + "Accton Technology", + "USB320-EC Ethernet Adapter", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_2664W, + 0, + "Accton Technology", + "2664W", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_111, + 0, + "Accton Technology", + "T-Sinus 111 Wireless Adapter", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SMCWUSBG, + 0, + "Accton Technology", + "SMCWUSB-G", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_PRISM_GT, + 0, + "Accton Technology", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, + 0, + "Accton Technology", + "SpeedStream Ethernet Adapter", + }, + { + USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_ZD1211B, + 0, + "Accton Technology", + "ZD1211B", + }, + { + USB_VENDOR_ACEECA, USB_PRODUCT_ACEECA_MEZ1000, + 0, + "Aceeca", + "MEZ1000 RDA", + }, + { + USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, + 0, + "Acer Communications & Multimedia", + "EP-1427X-2 Ethernet Adapter", + }, + { + USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, + 0, + "Acer Labs", + "USB 2.0 Data Link", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U, + 0, + "Acer Peripherals", + "Acerscan C310U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U, + 0, + "Acer Peripherals", + "Acerscan 320U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U, + 0, + "Acer Peripherals", + "Acerscan 640U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U, + 0, + "Acer Peripherals", + "Acerscan 620U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U, + 0, + "Acer Peripherals", + "Benq 3300U/4300U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT, + 0, + "Acer Peripherals", + "Acerscan 640BT", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U, + 0, + "Acer Peripherals", + "Acerscan 1240U", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ATAPI, + 0, + "Acer Peripherals", + "ATA/ATAPI Adapter", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL300, + 0, + "Acer Peripherals", + "AWL300 Wireless Adapter", + }, + { + USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL400, + 0, + "Acer Peripherals", + "AWL400 Wireless Adapter", + }, + { + USB_VENDOR_ACERW, USB_PRODUCT_ACERW_WARPLINK, + 0, + "Acer", + "Warplink", + }, + { + USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_PRISM_25, + 0, + "Actiontec Electronics", + "Prism2.5 Wireless Adapter", + }, + { + USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_PRISM_25A, + 0, + "Actiontec Electronics", + "Prism2.5 Wireless Adapter A", + }, + { + USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_FREELAN, + 0, + "Actiontec Electronics", + "ROPEX FreeLan 802.11b", + }, + { + USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_UAT1, + 0, + "Actiontec Electronics", + "UAT1 Wireless Ethernet Adapter", + }, + { + USB_VENDOR_ACTISYS, USB_PRODUCT_ACTISYS_IR2000U, + 0, + "ACTiSYS", + "ACT-IR2000U FIR", + }, + { + USB_VENDOR_ACTIVEWIRE, USB_PRODUCT_ACTIVEWIRE_IOBOARD, + 0, + "ActiveWire", + "I/O Board", + }, + { + USB_VENDOR_ACTIVEWIRE, USB_PRODUCT_ACTIVEWIRE_IOBOARD_FW1, + 0, + "ActiveWire", + "I/O Board, rev. 1 firmware", + }, + { + USB_VENDOR_ADAPTEC, USB_PRODUCT_ADAPTEC_AWN8020, + 0, + "Adaptec", + "AWN-8020 WLAN", + }, + { + USB_VENDOR_ADDTRON, USB_PRODUCT_ADDTRON_AWU120, + 0, + "Addtron", + "AWU-120", + }, + { + USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, + 0, + "ADMtek", + "AN986A Ethernet", + }, + { + USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, + 0, + "ADMtek", + "AN986 Ethernet", + }, + { + USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, + 0, + "ADMtek", + "AN8511 Ethernet", + }, + { + USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, + 0, + "ADMtek", + "AN8513 Ethernet", + }, + { + USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, + 0, + "ADMtek", + "AN8515 Ethernet", + }, + { + USB_VENDOR_ADDON, USB_PRODUCT_ADDON_ATTACHE, + 0, + "Add-on Technology", + "USB 2.0 Flash Drive", + }, + { + USB_VENDOR_ADDON, USB_PRODUCT_ADDON_ATTACHE, + 0, + "Add-on Technology", + "USB 2.0 Flash Drive", + }, + { + USB_VENDOR_ADDON, USB_PRODUCT_ADDON_A256MB, + 0, + "Add-on Technology", + "Attache 256MB USB 2.0 Flash Drive", + }, + { + USB_VENDOR_ADDON, USB_PRODUCT_ADDON_DISKPRO512, + 0, + "Add-on Technology", + "USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive)", + }, + { + USB_VENDOR_ADDONICS2, USB_PRODUCT_ADDONICS2_CABLE_205, + 0, + "Addonics Technology", + "Cable 205", + }, + { + USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, + 0, + "ADS Technologies", + "UBS-10BT Ethernet", + }, + { + USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX, + 0, + "ADS Technologies", + "UBS-10BT Ethernet", + }, + { + USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, + 0, + "AEI", + "Fast Ethernet", + }, + { + USB_VENDOR_AGATE, USB_PRODUCT_AGATE_QDRIVE, + 0, + "Agate Technologies", + "Q-Drive", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U, + 0, + "AGFA-Gevaert", + "SnapScan 1212U", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U, + 0, + "AGFA-Gevaert", + "SnapScan 1236U", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH, + 0, + "AGFA-Gevaert", + "SnapScan Touch", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2, + 0, + "AGFA-Gevaert", + "SnapScan 1212U", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40, + 0, + "AGFA-Gevaert", + "SnapScan e40", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50, + 0, + "AGFA-Gevaert", + "SnapScan e50", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20, + 0, + "AGFA-Gevaert", + "SnapScan e20", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25, + 0, + "AGFA-Gevaert", + "SnapScan e25", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26, + 0, + "AGFA-Gevaert", + "SnapScan e26", + }, + { + USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52, + 0, + "AGFA-Gevaert", + "SnapScan e52", + }, + { + USB_VENDOR_AINCOMM, USB_PRODUCT_AINCOMM_AWU2000B, + 0, + "Aincomm", + "AWU2000B Wireless Adapter", + }, + { + USB_VENDOR_AIPTEK, USB_PRODUCT_AIPTEK_POCKETCAM3M, + 0, + "AIPTEK International", + "PocketCAM 3Mega", + }, + { + USB_VENDOR_SUNPLUS, USB_PRODUCT_SUNPLUS_PENCAM_MEGA_1_3, + 0, + "Sunplus", + "PenCam Mega 1.3", + }, + { + USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, + 0, + "AirPrime, Inc.", + "CDMA Wireless PC Card", + }, + { + USB_VENDOR_AKS, USB_PRODUCT_AKS_USBHASP, + 0, + "Aladdin Knowledge Systems", + "USB-HASP 0.06", + }, + { + USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, + 0, + "Alcor Micro", + "Kbd Hub", + }, + { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_MA_KBD_HUB, + 0, + "Alcor Micro", + "MacAlly Kbd Hub", + }, + { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_AU9814, + 0, + "Alcor Micro", + "AU9814 Hub", + }, + { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_UMCR_9361, + 0, + "Alcor Micro", + "USB Multimedia Card Reader", + }, + { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_SM_KBD, + 0, + "Alcor Micro", + "MicroConnectors/StrongMan Keyboard", + }, + { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_NEC_KBD_HUB, + 0, + "Alcor Micro", + "NEC Kbd Hub", + }, + { + USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, + 0, + "Altec Lansing", + "ADA70 Speakers", + }, + { + USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, + 0, + "Altec Lansing", + "ASC495 Speakers", + }, + { + USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, + 0, + "Allied Telesyn International", + "AT-USB100", + }, + { + USB_VENDOR_APC, USB_PRODUCT_APC_UPS, + 0, + "American Power Conversion", + "Uninterruptible Power Supply", + }, + { + USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_WLAN, + 0, + "Ambit Microsystems", + "WLAN", + }, + { + USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, + 0, + "Ambit Microsystems", + "NTL 250 cable modem", + }, + { + USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO, + 0, + "AMIT", + "CG-WLUSB2GO", + }, + { + USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZUSB, + 0, + "Anchor Chips", + "EZUSB", + }, + { + USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK, + 0, + "Anchor Chips", + "EZLINK", + }, + { + USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X, + 0, + "AnyDATA Corporation", + "CDMA 2000 1xRTT/EV-DO USB Modem", + }, + { + USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A, + 0, + "AnyDATA Corporation", + "CDMA 2000 EV-DO USB Modem", + }, + { + USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101, + 0, + "AOX", + "Ethernet", + }, + { + USB_VENDOR_APC, USB_PRODUCT_APC_UPS, + 0, + "American Power Conversion", + "Uninterruptible Power Supply", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_EXT_KBD, + 0, + "Apple Computer", + "Apple Extended USB Keyboard", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_OPTMOUSE, + 0, + "Apple Computer", + "Optical mouse", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_MIGHTYMOUSE, + 0, + "Apple Computer", + "Mighty Mouse", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_EXT_KBD_HUB, + 0, + "Apple Computer", + "Hub in Apple Extended USB Keyboard", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_SPEAKERS, + 0, + "Apple Computer", + "Speakers", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD, + 0, + "Apple Computer", + "iPod", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD2G, + 0, + "Apple Computer", + "iPod 2G", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD3G, + 0, + "Apple Computer", + "iPod 3G", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_04, + 0, + "Apple Computer", + "iPod '04'", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPODMINI, + 0, + "Apple Computer", + "iPod Mini", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_06, + 0, + "Apple Computer", + "iPod '06'", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_07, + 0, + "Apple Computer", + "iPod '07'", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_08, + 0, + "Apple Computer", + "iPod '08'", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPODVIDEO, + 0, + "Apple Computer", + "iPod Video", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPODNANO, + 0, + "Apple Computer", + "iPod Nano", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, + 0, + "Apple Computer", + "iPhone", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, + 0, + "Apple Computer", + "iPhone 3G", + }, + { + USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, + 0, + "Apple Computer", + "Ethernet A1277", + }, + { + USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, + 0, + "Arkmicro Technologies Inc.", + "ARK3116 Serial", + }, + { + USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO230, + 0, + "Asahi Optical", + "Digital camera", + }, + { + USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO330, + 0, + "Asahi Optical", + "Digital camera", + }, + { + USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA, + 0, + "Asante", + "Ethernet", + }, + { + USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, + 0, + "ASIX Electronics", + "10/100 Ethernet", + }, + { + USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, + 0, + "ASIX Electronics", + "AX88178", + }, + { + USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, + 0, + "ASIX Electronics", + "AX88772", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G, + 0, + "ASUSTeK Computer", + "WL-167g Wireless Adapter", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G, + 0, + "ASUSTeK Computer", + "WL-159g", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_A9T_WIFI, + 0, + "ASUSTeK Computer", + "A9T wireless", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1, + 0, + "ASUSTeK Computer", + "RT2573", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2, + 0, + "ASUSTeK Computer", + "RT2573", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_LCM, + 0, + "ASUSTeK Computer", + "LCM display", + }, + { + USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535, + 0, + "ASUSTeK Computer", + "ASUS P535 PDA", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC1284, + 0, + "ATEN International", + "Parallel printer", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T, + 0, + "ATEN International", + "10Mbps Ethernet", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, + 0, + "ATEN International", + "UC-110T Ethernet", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, + 0, + "ATEN International", + "Serial", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, + 0, + "ATEN International", + "UC-210T Ethernet", + }, + { + USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C, + 0, + "ATEN International", + "DSB-650C", + }, + { + USB_VENDOR_ATHEROS, USB_PRODUCT_ATHEROS_AR5523, + 0, + "Atheros Communications", + "AR5523", + }, + { + USB_VENDOR_ATHEROS, USB_PRODUCT_ATHEROS_AR5523_NF, + 0, + "Atheros Communications", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_1, + 0, + "Atheros Communications", + "AR5523", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_1_NF, + 0, + "Atheros Communications", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_2, + 0, + "Atheros Communications", + "AR5523", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_2_NF, + 0, + "Atheros Communications", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_3, + 0, + "Atheros Communications", + "AR5523", + }, + { + USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR5523_3_NF, + 0, + "Atheros Communications", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_UHB124, + 0, + "Atmel", + "UHB124 hub", + }, + { + USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_DWL120, + 0, + "Atmel", + "DWL-120 Wireless Adapter", + }, + { + USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_BW002, + 0, + "Atmel", + "BW002 Wireless Adapter", + }, + { + USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_WL1130USB, + 0, + "Atmel", + "WL-1130 USB", + }, + { + USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_AT76C505A, + 0, + "Atmel", + "AT76c505a Wireless Adapter", + }, + { + USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U, + 0, + "Avision", + "1200U scanner", + }, + { + USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM, + 0, + "Axesstel Co., Ltd.", + "Data Modem", + }, + { + USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, + 0, + "Baltech", + "Card reader", + }, + { + USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, + 0, + "B&B Electronics", + "RS-422/485", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D6050, + 0, + "Belkin Components", + "F5D6050 802.11b Wireless Adapter", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_FBT001V, + 0, + "Belkin Components", + "FBT001v2 Bluetooth", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_FBT003V, + 0, + "Belkin Components", + "FBT003v2 Bluetooth", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103, + 0, + "Belkin Components", + "F5U103 Serial", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, + 0, + "Belkin Components", + "F5U109 Serial", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2SCSI, + 0, + "Belkin Components", + "USB to SCSI", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, + 0, + "Belkin Components", + "USB to LAN", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U208, + 0, + "Belkin Components", + "F5U208 VideoBus II", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U237, + 0, + "Belkin Components", + "F5U237 USB 2.0 7-Port Hub", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, + 0, + "Belkin Components", + "F5U257 Serial", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, + 0, + "Belkin Components", + "F5U409 Serial", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR, + 0, + "Belkin Components", + "F6C550-AVR UPS", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120, + 0, + "Belkin Components", + "F5U120-PC Hub", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_ZD1211B, + 0, + "Belkin Components", + "ZD1211B", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, + 0, + "Belkin Components", + "F5D5055", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050, + 0, + "Belkin Components", + "F5D7050 Wireless Adapter", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051, + 0, + "Belkin Components", + "F5D7051 54g USB Network Adapter", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A, + 0, + "Belkin Components", + "F5D7050A Wireless Adapter", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050_V4000, + 0, + "Belkin Components", + "F5D7050 v4000 Wireless Adapter", + }, + { + USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3, + 0, + "Belkin Components", + "F5D9050 ver 3 Wireless Adapter", + }, + { + USB_VENDOR_BELKIN2, USB_PRODUCT_BELKIN2_F5U002, + 0, + "Belkin Components", + "F5U002 Parallel printer", + }, + { + USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, + 0, + "Billionton Systems", + "USB100N 10/100 FastEthernet", + }, + { + USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, + 0, + "Billionton Systems", + "USB100LP", + }, + { + USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, + 0, + "Billionton Systems", + "USB100EL", + }, + { + USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, + 0, + "Billionton Systems", + "USBE100", + }, + { + USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, + 0, + "Billionton Systems", + "USB2AR Ethernet", + }, + { + USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, + 0, + "Broadcom", + "BCM2033 Bluetooth USB dongle", + }, + { + USB_VENDOR_BROTHER, USB_PRODUCT_BROTHER_HL1050, + 0, + "Brother Industries", + "HL-1050 laser printer", + }, + { + USB_VENDOR_BTC, USB_PRODUCT_BTC_BTC7932, + 0, + "Behavior Tech. Computer", + "Keyboard with mouse port", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U, + 0, + "Canon", + "CanoScan N656U", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U, + 0, + "Canon", + "CanoScan N1220U", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U, + 0, + "Canon", + "CanoScan D660U", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U, + 0, + "Canon", + "CanoScan N676U", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U, + 0, + "Canon", + "CanoScan N1240U", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25, + 0, + "Canon", + "CanoScan LIDE 25", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_S10, + 0, + "Canon", + "PowerShot S10", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_S100, + 0, + "Canon", + "PowerShot S100", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_S200, + 0, + "Canon", + "PowerShot S200", + }, + { + USB_VENDOR_CANON, USB_PRODUCT_CANON_REBELXT, + 0, + "Canon", + "Digital Rebel XT", + }, + { + USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE, + 0, + "Computer Access Technology", + "Netmate Ethernet", + }, + { + USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2, + 0, + "Computer Access Technology", + "Netmate2 Ethernet", + }, + { + USB_VENDOR_CATC, USB_PRODUCT_CATC_CHIEF, + 0, + "Computer Access Technology", + "USB Chief Bus & Protocol Analyzer", + }, + { + USB_VENDOR_CATC, USB_PRODUCT_CATC_ANDROMEDA, + 0, + "Computer Access Technology", + "Andromeda hub", + }, + { + USB_VENDOR_CASIO, USB_PRODUCT_CASIO_QV_DIGICAM, + 0, + "CASIO", + "QV DigiCam", + }, + { + USB_VENDOR_CASIO, USB_PRODUCT_CASIO_EXS880, + 0, + "CASIO", + "Exilim EX-S880", + }, + { + USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300, + 0, + "CASIO", + "BE-300 PDA", + }, + { + USB_VENDOR_CASIO, USB_PRODUCT_CASIO_NAMELAND, + 0, + "CASIO", + "CASIO Nameland EZ-USB", + }, + { + USB_VENDOR_CCYU, USB_PRODUCT_CCYU_ED1064, + 0, + "CCYU Technology", + "EasyDisk ED1064", + }, + { + USB_VENDOR_CENTURY, USB_PRODUCT_CENTURY_EX35QUAT, + 0, + "Century Corp", + "Century USB Disk Enclosure", + }, + { + USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000KBD, + 0, + "Cherry Mikroschalter", + "My3000 keyboard", + }, + { + USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000HUB, + 0, + "Cherry Mikroschalter", + "My3000 hub", + }, + { + USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_CYBOARD, + 0, + "Cherry Mikroschalter", + "CyBoard Keyboard", + }, + { + USB_VENDOR_CHIC, USB_PRODUCT_CHIC_MOUSE1, + 0, + "Chic Technology", + "mouse", + }, + { + USB_VENDOR_CHIC, USB_PRODUCT_CHIC_CYPRESS, + 0, + "Chic Technology", + "Cypress USB Mouse", + }, + { + USB_VENDOR_CHICONY, USB_PRODUCT_CHICONY_KB8933, + 0, + "Chicony Electronics", + "KB-8933 keyboard", + }, + { + USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TWINKLECAM, + 0, + "Chicony", + "TwinkleCam USB camera", + }, + { + USB_VENDOR_CHPRODUCTS, USB_PRODUCT_CHPRODUCTS_PROTHROTTLE, + 0, + "CH Products", + "Pro Throttle", + }, + { + USB_VENDOR_CHPRODUCTS, USB_PRODUCT_CHPRODUCTS_PROPEDALS, + 0, + "CH Products", + "Pro Pedals", + }, + { + USB_VENDOR_CHPRODUCTS, USB_PRODUCT_CHPRODUCTS_FIGHTERSTICK, + 0, + "CH Products", + "Fighterstick", + }, + { + USB_VENDOR_CHPRODUCTS, USB_PRODUCT_CHPRODUCTS_FLIGHTYOKE, + 0, + "CH Products", + "Flight Sim Yoke", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G, + 0, + "Cisco-Linksys", + "WUSB54G Wireless Adapter", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP, + 0, + "Cisco-Linksys", + "WUSB54GP Wireless Adapter", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, + 0, + "Cisco-Linksys", + "USB200M v2", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS, + 0, + "Cisco-Linksys", + "HU200TS Wireless Adapter", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC, + 0, + "Cisco-Linksys", + "WUSB54GC", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR, + 0, + "Cisco-Linksys", + "WUSB54GR", + }, + { + USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSBF54G, + 0, + "Cisco-Linksys", + "WUSBF54G", + }, + { + USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CNU510, + 0, + "CMOTECH Co., Ltd.", + "CMOTECH CDMA Technologies USB modem", + }, + { + USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CNU550, + 0, + "CMOTECH Co., Ltd.", + "CDMA 2000 1xRTT/1xEVDO USB modem", + }, + { + USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, + 0, + "CMOTECH Co., Ltd.", + "CMOTECH CDMA Technologies USB modem", + }, + { + USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC, + 0, + "Compaq", + "iPAQ PocketPC", + }, + { + USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_PJB100, + 0, + "Compaq", + "Personal Jukebox PJB100", + }, + { + USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, + 0, + "Compaq", + "iPAQ Linux", + }, + { + USB_VENDOR_COMPOSITE, USB_PRODUCT_COMPOSITE_USBPS2, + 0, + "Composite", + "USB to PS2 Adaptor", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_PRISM_GT, + 0, + "Conceptronic", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_C11U, + 0, + "Conceptronic", + "C11U", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_WL210, + 0, + "Conceptronic", + "WL-210", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_AR5523_1, + 0, + "Conceptronic", + "AR5523", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_AR5523_1_NF, + 0, + "Conceptronic", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_AR5523_2, + 0, + "Conceptronic", + "AR5523", + }, + { + USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_AR5523_2_NF, + 0, + "Conceptronic", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU, + 0, + "Conceptronic", + "C54RU WLAN", + }, + { + USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2, + 0, + "Conceptronic", + "C54RU", + }, + { + USB_VENDOR_CONNECTIX, USB_PRODUCT_CONNECTIX_QUICKCAM, + 0, + "Connectix", + "QuickCam", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T, + 0, + "Corega", + "Ether USB-T", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, + 0, + "Corega", + "FEther USB-TX", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_WLAN_USB_USB_11, + 0, + "Corega", + "WirelessLAN USB-11", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, + 0, + "Corega", + "FEther USB-TXS", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_WLANUSB, + 0, + "Corega", + "Wireless LAN Stick-11", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, + 0, + "Corega", + "FEther USB2-TX", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_WLUSB_11_KEY, + 0, + "Corega", + "ULUSB-11 Key", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL, + 0, + "Corega", + "CG-WLUSB2GL", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX, + 0, + "Corega", + "CG-WLUSB2GPX", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_WLUSB_11_STICK, + 0, + "Corega", + "WLAN USB Stick 11", + }, + { + USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, + 0, + "Corega", + "FEther USB-TXC", + }, + { + USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_NOMAD_II, + 0, + "Creative Labs", + "Nomad II MP3 player", + }, + { + USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_NOMAD_IIMG, + 0, + "Creative Labs", + "Nomad II MG", + }, + { + USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_NOMAD, + 0, + "Creative Labs", + "Nomad", + }, + { + USB_VENDOR_CREATIVE2, USB_PRODUCT_CREATIVE2_VOIP_BLASTER, + 0, + "Creative Labs", + "Voip Blaster", + }, + { + USB_VENDOR_CREATIVE3, USB_PRODUCT_CREATIVE3_OPTICAL_MOUSE, + 0, + "Creative Labs", + "Notebook Optical Mouse", + }, + { + USB_VENDOR_CSR, USB_PRODUCT_CSR_BT_DONGLE, + 0, + "Cambridge Silicon Radio", + "Bluetooth USB dongle", + }, + { + USB_VENDOR_CSR, USB_PRODUCT_CSR_CSRDFU, + 0, + "Cambridge Silicon Radio", + "USB Bluetooth Device in DFU State", + }, + { + USB_VENDOR_CTX, USB_PRODUCT_CTX_EX1300, + 0, + "Chuntex", + "Ex1300 hub", + }, + { + USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_HX550C, + 0, + "Curitel Communications Inc", + "CDMA 2000 1xRTT USB modem (HX-550C)", + }, + { + USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_HX57XB, + 0, + "Curitel Communications Inc", + "CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600)", + }, + { + USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, + 0, + "Curitel Communications Inc", + "Broadband Wireless modem", + }, + { + USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_1500CAVRLCD, + 0, + "Cyber Power Systems, Inc.", + "1500CAVRLCD", + }, + { + USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB, + 0, + "CyberTAN Technology", + "TG54USB", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_MOUSE, + 0, + "Cypress Semiconductor", + "mouse", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_THERMO, + 0, + "Cypress Semiconductor", + "thermometer", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY1A, + 0, + "Cypress Semiconductor", + "MetaGeek Wi-Spy", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_KBDHUB, + 0, + "Cypress Semiconductor", + "Keyboard/Hub", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_FMRADIO, + 0, + "Cypress Semiconductor", + "FM Radio", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232, + 0, + "Cypress Semiconductor", + "USB-RS232 Interface", + }, + { + USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_SLIM_HUB, + 0, + "Cypress Semiconductor", + "Slim Hub", + }, + { + USB_VENDOR_DAISY, USB_PRODUCT_DAISY_DMC, + 0, + "Daisy Technology", + "USB MultiMedia Reader", + }, + { + USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, + 0, + "Dallas Semiconductor", + "J-6502 speakers", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_PORT, + 0, + "Dell", + "Port Replicator", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_AIO926, + 0, + "Dell", + "Photo AIO Printer 926", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_BC02, + 0, + "Dell", + "BC02 Bluetooth USB Adapter", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_1, + 0, + "Dell", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_TM350, + 0, + "Dell", + "TrueMobile 350 Bluetooth USB Adapter", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_2, + 0, + "Dell", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, + 0, + "Dell", + "Dell U740 CDMA", + }, + { + USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, + 0, + "DeLorme", + "Earthmate GPS", + }, + { + USB_VENDOR_DESKNOTE, USB_PRODUCT_DESKNOTE_UCR_61S2B, + 0, + "Desknote", + "UCR-61S2B", + }, + { + USB_VENDOR_DIAMOND, USB_PRODUCT_DIAMOND_RIO500USB, + 0, + "Diamond", + "Rio 500 USB", + }, + { + USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573, + 0, + "Dick Smith Electronics", + "RT2573", + }, + { + USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F, + 0, + "Dick Smith Electronics", + "C-Net CWD-854 rev F", + }, + { + USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT2, + 0, + "Digi International", + "AccelePort USB 2", + }, + { + USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT4, + 0, + "Digi International", + "AccelePort USB 4", + }, + { + USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT8, + 0, + "Digi International", + "AccelePort USB 8", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWL120E, + 0, + "D-Link", + "DWL-120 rev E", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWL122, + 0, + "D-Link", + "DWL-122", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG120, + 0, + "D-Link", + "DWL-G120", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWL120F, + 0, + "D-Link", + "DWL-120 rev F", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG132, + 0, + "D-Link", + "DWL-AG132", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG132_NF, + 0, + "D-Link", + "DWL-AG132 (no firmware)", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG132, + 0, + "D-Link", + "DWL-G132", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG132_NF, + 0, + "D-Link", + "DWL-G132 (no firmware)", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG122, + 0, + "D-Link", + "DWL-AG122", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG122_NF, + 0, + "D-Link", + "DWL-AG122 (no firmware)", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122, + 0, + "D-Link", + "DWL-G122 b1 Wireless Adapter", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, + 0, + "D-Link", + "DUB-E100 rev B1", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C, + 0, + "D-Link", + "10Mbps Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, + 0, + "D-Link", + "1/10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, + 0, + "D-Link", + "10/100 Ethernet", + }, + { + USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1, + 0, + "D-Link", + "DWL-G122 c1", + }, + { + USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340, + 0, + "D-Link", + "WUA-1340", + }, + { + USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111, + 0, + "D-Link", + "DWA-111", + }, + { + USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110, + 0, + "D-Link", + "DWA-110", + }, + { + USB_VENDOR_DMI, USB_PRODUCT_DMI_CFSM_RW, + 0, + "DMI", + "CF/SM Reader/Writer", + }, + { + USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550, + 0, + "DrayTek", + "Vigor550", + }, + { + USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, + 0, + "Dynastream Innovations", + "ANT dev board", + }, + { + USB_VENDOR_EIZO, USB_PRODUCT_EIZO_HUB, + 0, + "EIZO", + "hub", + }, + { + USB_VENDOR_EIZO, USB_PRODUCT_EIZO_MONITOR, + 0, + "EIZO", + "monitor", + }, + { + USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, + 0, + "ELCON Systemtechnik", + "Goldpfeil P-LAN", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_MOUSE29UO, + 0, + "Elecom", + "mouse 29UO", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, + 0, + "Elecom", + "LD-USB/TX", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, + 0, + "Elecom", + "LD-USB/TX", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, + 0, + "Elecom", + "LD-USBL/TX", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, + 0, + "Elecom", + "LD-USB/TX", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, + 0, + "Elecom", + "LD-USB20", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, + 0, + "Elecom", + "UC-SGT", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, + 0, + "Elecom", + "UC-SGT", + }, + { + USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, + 0, + "Elecom", + "LD-USB/TX", + }, + { + USB_VENDOR_ELSA, USB_PRODUCT_ELSA_MODEM1, + 0, + "ELSA", + "ELSA Modem Board", + }, + { + USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, + 0, + "ELSA", + "Microlink USB2Ethernet", + }, + { + USB_VENDOR_EMS, USB_PRODUCT_EMS_DUAL_SHOOTER, + 0, + "EMS Production", + "PSX gun controller converter", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S, + 0, + "Entrega", + "1S serial", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_2S, + 0, + "Entrega", + "2S serial", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S25, + 0, + "Entrega", + "1S25 serial", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_4S, + 0, + "Entrega", + "4S serial", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45, + 0, + "Entrega", + "E45 Ethernet", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_CENTRONICS, + 0, + "Entrega", + "Parallel Port", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1, + 0, + "Entrega", + "Ethernet", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S9, + 0, + "Entrega", + "1S9 serial", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_EZUSB, + 0, + "Entrega", + "EZ-USB", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_2U4S, + 0, + "Entrega", + "2U4S serial/usb hub", + }, + { + USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2, + 0, + "Entrega", + "Ethernet", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER1, + 0, + "Seiko Epson", + "USB Printer", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER2, + 0, + "Seiko Epson", + "ISD USB Smart Cable for Mac", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER3, + 0, + "Seiko Epson", + "ISD USB Smart Cable", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER5, + 0, + "Seiko Epson", + "USB Printer", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636, + 0, + "Seiko Epson", + "Perfection 636U / 636Photo scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610, + 0, + "Seiko Epson", + "Perfection 610 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200, + 0, + "Seiko Epson", + "Perfection 1200U / 1200Photo scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600, + 0, + "Seiko Epson", + "Expression 1600 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640, + 0, + "Seiko Epson", + "Perfection 1640SU scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240, + 0, + "Seiko Epson", + "Perfection 1240U / 1240Photo scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U, + 0, + "Seiko Epson", + "Perfection 640U scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250, + 0, + "Seiko Epson", + "Perfection 1250U / 1250Photo scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650, + 0, + "Seiko Epson", + "Perfection 1650 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F, + 0, + "Seiko Epson", + "GT-9700F scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF, + 0, + "Seiko Epson", + "GT-9300UF scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200, + 0, + "Seiko Epson", + "Perfection 3200 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260, + 0, + "Seiko Epson", + "Perfection 1260 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660, + 0, + "Seiko Epson", + "Perfection 1660 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670, + 0, + "Seiko Epson", + "Perfection 1670 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270, + 0, + "Seiko Epson", + "Perfection 1270 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480, + 0, + "Seiko Epson", + "Perfection 2480 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590, + 0, + "Seiko Epson", + "Perfection 3590 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990, + 0, + "Seiko Epson", + "Perfection 4990 Photo scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_875DC, + 0, + "Seiko Epson", + "Stylus Photo 875DC Card Reader", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_895, + 0, + "Seiko Epson", + "Stylus Photo 895 Card Reader", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400, + 0, + "Seiko Epson", + "CX5400 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500, + 0, + "Seiko Epson", + "CX-3500/3600/3650 MFP", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425, + 0, + "Seiko Epson", + "Stylus Photo RX425 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800, + 0, + "Seiko Epson", + "CX4800 MP scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200, + 0, + "Seiko Epson", + "CX4200 MP scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000, + 0, + "Seiko Epson", + "DX-50x0 MFP scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000, + 0, + "Seiko Epson", + "DX-60x0 MFP scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400, + 0, + "Seiko Epson", + "DX7400/CX7300 scanner", + }, + { + USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400, + 0, + "Seiko Epson", + "DX8400 scanner", + }, + { + USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM, + 0, + "e-TEK Labs", + "Serial", + }, + { + USB_VENDOR_EXTENDED, USB_PRODUCT_EXTENDED_XTNDACCESS, + 0, + "Extended Systems", + "XTNDAccess IrDA", + }, + { + USB_VENDOR_FEIYA, USB_PRODUCT_FEIYA_5IN1, + 0, + "Feiya", + "5-in-1 Card Reader", + }, + { + USB_VENDOR_FIBERLINE, USB_PRODUCT_FIBERLINE_WL430U, + 0, + "Fiberline", + "WL-430U", + }, + { + USB_VENDOR_FOSSIL, USB_PRODUCT_FOSSIL_WRISTPDA, + 0, + "Fossil, Inc", + "Wrist PDA", + }, + { + USB_VENDOR_FREECOM, USB_PRODUCT_FREECOM_DVD, + 0, + "Freecom", + "DVD drive", + }, + { + USB_VENDOR_FSC, USB_PRODUCT_FSC_E5400, + 0, + "Fujitsu Siemens Computers", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, + 0, + "Future Technology Devices", + "8U100AX Serial", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, + 0, + "Future Technology Devices", + "8U232AM Serial", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, + 0, + "Future Technology Devices", + "FT2232C Dual port Serial", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, + 0, + "Future Technology Devices", + "OpenPort 1.3 Mitsubishi", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, + 0, + "Future Technology Devices", + "OpenPort 1.3 Subaru", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, + 0, + "Future Technology Devices", + "OpenPort 1.3 Universal", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, + 0, + "Future Technology Devices", + "Expert ISDN Control USB", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, + 0, + "Future Technology Devices", + "USB-RS232 OptoBridge", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, + 0, + "Future Technology Devices", + "Expert mouseCLOCK USB II", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, + 0, + "Future Technology Devices", + "Precision Clock MSF USB", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, + 0, + "Future Technology Devices", + "Expert mouseCLOCK USB II HBG", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, + 0, + "Future Technology Devices", + "Matrix Orbital USB Serial", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, + 0, + "Future Technology Devices", + "Matrix Orbital MX2 or MX3", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, + 0, + "Future Technology Devices", + "Matrix Orbital MX4 or MX5", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, + 0, + "Future Technology Devices", + "Matrix Orbital VK/LK202 Family", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, + 0, + "Future Technology Devices", + "Matrix Orbital VK/LK204 Family", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, + 0, + "Future Technology Devices", + "Crystalfontz CFA-632 USB LCD", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, + 0, + "Future Technology Devices", + "Crystalfontz CFA-634 USB LCD", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, + 0, + "Future Technology Devices", + "Crystalfontz CFA-633 USB LCD", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, + 0, + "Future Technology Devices", + "Crystalfontz CFA-631 USB LCD", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, + 0, + "Future Technology Devices", + "Crystalfontz CFA-635 USB LCD", + }, + { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, + 0, + "Future Technology Devices", + "SEMC DSS-20 SyncStation", + }, + { + USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100, + 0, + "Fuji Photo Film", + "Mass Storage", + }, + { + USB_VENDOR_FUJITSU, USB_PRODUCT_FUJITSU_AH_F401U, + 0, + "Fujitsu", + "AH-F401U Air H device", + }, + { + USB_VENDOR_GARMIN, USB_PRODUCT_GARMIN_IQUE_3600, + 0, + "Garmin International", + "iQue 3600", + }, + { + USB_VENDOR_GENERALINSTMNTS, USB_PRODUCT_GENERALINSTMNTS_SB5100, + 0, + "General Instruments (Motorola)", + "SURFboard SB5100 Cable modem", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB, + 0, + "Genesys Logic", + "GL620USB Host-Host interface", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL650, + 0, + "Genesys Logic", + "GL650 Hub", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB, + 0, + "Genesys Logic", + "GL641USB CompactFlash Card Reader", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2, + 0, + "Genesys Logic", + "GL641USB USB-IDE Bridge No 2", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE, + 0, + "Genesys Logic", + "GL641USB USB-IDE Bridge", + }, + { + USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB_2, + 0, + "Genesys Logic", + "GL641USB 6-in-1 Card Reader", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G, + 0, + "GIGABYTE", + "GN-54G", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, + 0, + "GIGABYTE", + "GN-BR402W", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWLBM101, + 0, + "GIGABYTE", + "GN-WLBM101", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG, + 0, + "GIGABYTE", + "GN-WBKG", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS, + 0, + "GIGABYTE", + "GN-WB01GS", + }, + { + USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS, + 0, + "GIGABYTE", + "GN-WI05GS", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_WLAN, + 0, + "Gigaset", + "WLAN", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_SMCWUSBTG, + 0, + "Gigaset", + "SMCWUSBT-G", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_SMCWUSBTG_NF, + 0, + "Gigaset", + "SMCWUSBT-G (no firmware)", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_AR5523, + 0, + "Gigaset", + "AR5523", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_AR5523_NF, + 0, + "Gigaset", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573, + 0, + "Gigaset", + "RT2573", + }, + { + USB_VENDOR_GLOBALSUN, USB_PRODUCT_GLOBALSUN_AR5523_1, + 0, + "Global Sun Technology", + "AR5523", + }, + { + USB_VENDOR_GLOBALSUN, USB_PRODUCT_GLOBALSUN_AR5523_1_NF, + 0, + "Global Sun Technology", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_GLOBALSUN, USB_PRODUCT_GLOBALSUN_AR5523_2, + 0, + "Global Sun Technology", + "AR5523", + }, + { + USB_VENDOR_GLOBALSUN, USB_PRODUCT_GLOBALSUN_AR5523_2_NF, + 0, + "Global Sun Technology", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_1, + 0, + "Globespan", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_2, + 0, + "Globespan", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, + 0, + "G.Mate, Inc", + "YP3X00 PDA", + }, + { + USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232, + 0, + "GoHubs", + "GoCOM232 Serial", + }, + { + USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, + 0, + "Good Way Technology", + "GWUSB2E", + }, + { + USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573, + 0, + "Good Way Technology", + "RT2573", + }, + { + USB_VENDOR_GRAVIS, USB_PRODUCT_GRAVIS_GAMEPADPRO, + 0, + "Advanced Gravis Computer", + "GamePad Pro", + }, + { + USB_VENDOR_GREENHOUSE, USB_PRODUCT_GREENHOUSE_KANA21, + 0, + "GREENHOUSE", + "CF-writer with MP3", + }, + { + USB_VENDOR_GRIFFIN, USB_PRODUCT_GRIFFIN_IMATE, + 0, + "Griffin Technology", + "iMate, ADB Adapter", + }, + { + USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_DALEADER, + 0, + "Guillemot", + "DA Leader", + }, + { + USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254, + 0, + "Guillemot", + "HWGUSB2-54 WLAN", + }, + { + USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB, + 0, + "Guillemot", + "HWGUSB2-54-LB", + }, + { + USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP, + 0, + "Guillemot", + "HWGUSB2-54V2-AP", + }, + { + USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGSM, + 0, + "Hagiwara Sys-Com", + "FlashGate SmartMedia Card Reader", + }, + { + USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGCF, + 0, + "Hagiwara Sys-Com", + "FlashGate CompactFlash Card Reader", + }, + { + USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FG, + 0, + "Hagiwara Sys-Com", + "FlashGate", + }, + { + USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, + 0, + "HAL Corporation", + "Crossam2+USB IR commander", + }, + { + USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR, + 0, + "Handspring", + "Handspring Visor", + }, + { + USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO, + 0, + "Handspring", + "Handspring Treo", + }, + { + USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600, + 0, + "Handspring", + "Handspring Treo 600", + }, + { + USB_VENDOR_HAUPPAUGE, USB_PRODUCT_HAUPPAUGE_WINTV_USB_FM, + 0, + "Hauppauge Computer Works", + "WinTV USB FM", + }, + { + USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, + 0, + "Hawking", + "10/100 USB Ethernet", + }, + { + USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_DZ_MV100A, + 0, + "Hitachi", + "DVD-CAM DZ-MV100A Camcorder", + }, + { + USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, + 0, + "Hitachi", + "DVDCAM USB HS Interface", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_895C, + 0, + "Hewlett Packard", + "DeskJet 895C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_4100C, + 0, + "Hewlett Packard", + "Scanjet 4100C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_S20, + 0, + "Hewlett Packard", + "Photosmart S20", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_880C, + 0, + "Hewlett Packard", + "DeskJet 880C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_4200C, + 0, + "Hewlett Packard", + "ScanJet 4200C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_CDWRITERPLUS, + 0, + "Hewlett Packard", + "CD-Writer Plus", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_KBDHUB, + 0, + "Hewlett Packard", + "Multimedia Keyboard Hub", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_G55XI, + 0, + "Hewlett Packard", + "OfficeJet G55xi", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_HN210W, + 0, + "Hewlett Packard", + "HN210W 802.11b WLAN", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, + 0, + "Hewlett Packard", + "49g+ graphing calculator", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_6200C, + 0, + "Hewlett Packard", + "ScanJet 6200C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_S20b, + 0, + "Hewlett Packard", + "PhotoSmart S20", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_815C, + 0, + "Hewlett Packard", + "DeskJet 815C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_3300C, + 0, + "Hewlett Packard", + "ScanJet 3300C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200, + 0, + "Hewlett Packard", + "CD-Writer Plus 8200e", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_MMKEYB, + 0, + "Hewlett Packard", + "Multimedia keyboard", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_1220C, + 0, + "Hewlett Packard", + "DeskJet 1220C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_810C, + 0, + "Hewlett Packard", + "DeskJet 810C/812C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_4300C, + 0, + "Hewlett Packard", + "Scanjet 4300C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_CDW4E, + 0, + "Hewlett Packard", + "CD-Writer+ CD-4e", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_G85XI, + 0, + "Hewlett Packard", + "OfficeJet G85xi", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_1200, + 0, + "Hewlett Packard", + "LaserJet 1200", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_5200C, + 0, + "Hewlett Packard", + "Scanjet 5200C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_830C, + 0, + "Hewlett Packard", + "DeskJet 830C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE, + 0, + "Hewlett Packard", + "ScanJet 3400cse", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_6300C, + 0, + "Hewlett Packard", + "Scanjet 6300C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_840C, + 0, + "Hewlett Packard", + "DeskJet 840c", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_2200C, + 0, + "Hewlett Packard", + "ScanJet 2200C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_5300C, + 0, + "Hewlett Packard", + "Scanjet 5300C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_4400C, + 0, + "Hewlett Packard", + "Scanjet 4400C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_82x0C, + 0, + "Hewlett Packard", + "Scanjet 82x0C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_2300D, + 0, + "Hewlett Packard", + "Laserjet 2300d", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_970CSE, + 0, + "Hewlett Packard", + "Deskjet 970Cse", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_5400C, + 0, + "Hewlett Packard", + "Scanjet 5400C", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_2215, + 0, + "Hewlett Packard", + "iPAQ 22xx/Jornada 548", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_568J, + 0, + "Hewlett Packard", + "Jornada 568", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_930C, + 0, + "Hewlett Packard", + "DeskJet 930c", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_P2000U, + 0, + "Hewlett Packard", + "Inkjet P-2000U", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_640C, + 0, + "Hewlett Packard", + "DeskJet 640c", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_4670V, + 0, + "Hewlett Packard", + "ScanJet 4670v", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_P1100, + 0, + "Hewlett Packard", + "Photosmart P1100", + }, + { + USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, + 0, + "Hewlett Packard", + "Ethernet HN210E", + }, + { + USB_VENDOR_HP2, USB_PRODUCT_HP2_C500, + 0, + "Hewlett Packard", + "PhotoSmart C500", + }, + { + USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE, + 0, + "HTC", + "HTC USB Sync", + }, + { + USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM, + 0, + "HTC", + "PPC6700 Modem", + }, + { + USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE, + 0, + "HTC", + "SmartPhone USB Sync", + }, + { + USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, + 0, + "Huawei Technologies", + "Huawei Mobile", + }, + { + USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270, + 0, + "Huawei Technologies", + "Huawei HSPA modem", + }, + { + USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G, + 0, + "Huawei-3Com", + "Aolynk WUB320g", + }, + { + USB_VENDOR_IBM, USB_PRODUCT_IBM_USBCDROMDRIVE, + 0, + "IBM", + "USB CD-ROM Drive", + }, + { + USB_VENDOR_IMAGINATION, USB_PRODUCT_IMAGINATION_DBX1, + 0, + "Imagination Technologies", + "DBX1 DSP core", + }, + { + USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, + 0, + "Inside Out Networks", + "EdgePort/4 serial ports", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_F5U002, + 0, + "In-System Design", + "Parallel printer", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ATAPI, + 0, + "In-System Design", + "ATAPI Adapter", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ISD110, + 0, + "In-System Design", + "IDE Adapter ISD110", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ISD105, + 0, + "In-System Design", + "IDE Adapter ISD105", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE, + 0, + "In-System Design", + "USB cable", + }, + { + USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_STORAGE_V2, + 0, + "In-System Design", + "USB Storage Adapter V2", + }, + { + USB_VENDOR_INTEL, USB_PRODUCT_INTEL_EASYPC_CAMERA, + 0, + "Intel", + "Easy PC Camera", + }, + { + USB_VENDOR_INTEL, USB_PRODUCT_INTEL_TESTBOARD, + 0, + "Intel", + "82930 test board", + }, + { + USB_VENDOR_INTERSIL, USB_PRODUCT_INTERSIL_PRISM_GT, + 0, + "Intersil", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_INTERSIL, USB_PRODUCT_INTERSIL_PRISM_2X, + 0, + "Intersil", + "Prism2.x or Atmel WLAN", + }, + { + USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, + 0, + "Intrepid", + "ValueCAN CAN bus interface", + }, + { + USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, + 0, + "Intrepid", + "NeoVI Blue vehicle bus interface", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2, + 0, + "I-O Data", + "DVD Multi-plus unit iU-CD2", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8, + 0, + "I-O Data", + "DVD Multi-plus unit DVR-UEH8", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBSSMRW, + 0, + "I-O Data", + "USB-SSMRW SD-card", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBSDRW, + 0, + "I-O Data", + "USB-SDRW SD-card", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT, + 0, + "I-O Data", + "USB ETT", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, + 0, + "I-O Data", + "USB ETTX", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, + 0, + "I-O Data", + "USB ETTX", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBWNB11A, + 0, + "I-O Data", + "USB WN-B11", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBWNB11, + 0, + "I-O Data", + "USB Airport WN-B11", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, + 0, + "I-O Data", + "ETG-US2", + }, + { + USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, + 0, + "I-O Data", + "Serial USB-RSAQ1", + }, + { + USB_VENDOR_IODATA2, USB_PRODUCT_IODATA2_USB2SC, + 0, + "I-O Data", + "USB2.0-SCSI Bridge USB2-SC", + }, + { + USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100, + 0, + "Iomega", + "Zip 100", + }, + { + USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP250, + 0, + "Iomega", + "Zip 250", + }, + { + USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD2X20, + 0, + "I-Tuner Networks", + "USB-LCD 2x20", + }, + { + USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, + 0, + "Jablotron", + "PC-60B", + }, + { + USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA, + 0, + "Jaton", + "Ethernet", + }, + { + USB_VENDOR_JVC, USB_PRODUCT_JVC_GR_DX95, + 0, + "JVC", + "GR-DX95", + }, + { + USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, + 0, + "JVC", + "MP-PRX1 Ethernet", + }, + { + USB_VENDOR_JRC, USB_PRODUCT_JRC_AH_J3001V_J3002V, + 0, + "Japan Radio Company", + "AirH PHONE AH-J3001V/J3002V", + }, + { + USB_VENDOR_KAWATSU, USB_PRODUCT_KAWATSU_MH4000P, + 0, + "Kawatsu Semiconductor", + "MiniHub 4000P", + }, + { + USB_VENDOR_KEISOKUGIKEN, USB_PRODUCT_KEISOKUGIKEN_USBDAQ, + 0, + "Keisokugiken", + "HKS-0200 USBDAQ", + }, + { + USB_VENDOR_KENSINGTON, USB_PRODUCT_KENSINGTON_ORBIT, + 0, + "Kensington", + "Orbit USB/PS2 trackball", + }, + { + USB_VENDOR_KENSINGTON, USB_PRODUCT_KENSINGTON_TURBOBALL, + 0, + "Kensington", + "TurboBall", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-28 serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28X_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-28X serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-19 serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-18 serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18X_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-18X serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19W_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-19W serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19, + 0, + "Keyspan / InnoSys Inc.", + "USA-19 serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19W, + 0, + "Keyspan / InnoSys Inc.", + "USA-19W serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA49W_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-49W serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA49W, + 0, + "Keyspan / InnoSys Inc.", + "USA-49W serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19QI_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-19QI serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19QI, + 0, + "Keyspan / InnoSys Inc.", + "USA-19QI serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19Q_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-19Q serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19Q, + 0, + "Keyspan / InnoSys Inc.", + "USA-19Q serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28, + 0, + "Keyspan / InnoSys Inc.", + "USA-28 serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28XXB, + 0, + "Keyspan / InnoSys Inc.", + "USA-28X/XB serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18, + 0, + "Keyspan / InnoSys Inc.", + "USA-18 serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18X, + 0, + "Keyspan / InnoSys Inc.", + "USA-18X serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28XB_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-28XB serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28XA_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-28XB serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28XA, + 0, + "Keyspan / InnoSys Inc.", + "USA-28XA serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18XA_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-18XA serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18XA, + 0, + "Keyspan / InnoSys Inc.", + "USA-18XA serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19QW_NF, + 0, + "Keyspan / InnoSys Inc.", + "USA-19WQ serial Adapter (no firmware)", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19QW, + 0, + "Keyspan / InnoSys Inc.", + "USA-19WQ serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19HA, + 0, + "Keyspan / InnoSys Inc.", + "USA-19HS serial Adapter", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_UIA10, + 0, + "Keyspan / InnoSys Inc.", + "UIA-10 remote control", + }, + { + USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_UIA11, + 0, + "Keyspan / InnoSys Inc.", + "UIA-11 remote control", + }, + { + USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1, + 0, + "Kingston Technology", + "Ethernet", + }, + { + USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, + 0, + "Kingston Technology", + "KNU101TX USB Ethernet", + }, + { + USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT, + 0, + "Kawasaki LSI", + "USB Ethernet", + }, + { + USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN, + 0, + "Kawasaki LSI", + "USB Ethernet", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC220, + 0, + "Eastman Kodak", + "Digital Science DC220", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC260, + 0, + "Eastman Kodak", + "Digital Science DC260", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC265, + 0, + "Eastman Kodak", + "Digital Science DC265", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC290, + 0, + "Eastman Kodak", + "Digital Science DC290", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC240, + 0, + "Eastman Kodak", + "Digital Science DC240", + }, + { + USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC280, + 0, + "Eastman Kodak", + "Digital Science DC280", + }, + { + USB_VENDOR_KONICA, USB_PRODUCT_KONICA_CAMERA, + 0, + "Konica", + "Digital Color Camera", + }, + { + USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, + 0, + "KYE Systems", + "Niche mouse", + }, + { + USB_VENDOR_KYE, USB_PRODUCT_KYE_NETSCROLL, + 0, + "KYE Systems", + "Genius NetScroll mouse", + }, + { + USB_VENDOR_KYE, USB_PRODUCT_KYE_FLIGHT2000, + 0, + "KYE Systems", + "Flight 2000 joystick", + }, + { + USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO, + 0, + "KYE Systems", + "ColorPage Vivid-Pro scanner", + }, + { + USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S3X, + 0, + "Kyocera Wireless Corp.", + "Finecam S3x", + }, + { + USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S4, + 0, + "Kyocera Wireless Corp.", + "Finecam S4", + }, + { + USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S5, + 0, + "Kyocera Wireless Corp.", + "Finecam S5", + }, + { + USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_L3, + 0, + "Kyocera Wireless Corp.", + "Finecam L3", + }, + { + USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, + 0, + "Kyocera Wireless Corp.", + "AH-K3001V", + }, + { + USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, + 0, + "Kyocera Wireless Corp.", + "Qualcomm Kyocera CDMA Technologies MSM", + }, + { + USB_VENDOR_LACIE, USB_PRODUCT_LACIE_HD, + 0, + "LaCie", + "Hard Disk", + }, + { + USB_VENDOR_LACIE, USB_PRODUCT_LACIE_CDRW, + 0, + "LaCie", + "CD R/W", + }, + { + USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_JUMPSHOT, + 0, + "Lexar Media", + "jumpSHOT CompactFlash Reader", + }, + { + USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_CF_READER, + 0, + "Lexar Media", + "USB CF Reader", + }, + { + USB_VENDOR_LEXMARK, USB_PRODUCT_LEXMARK_S2450, + 0, + "Lexmark International", + "Optra S 2450", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_MAUSB2, + 0, + "Linksys", + "Camedia MAUSB-2", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, + 0, + "Linksys", + "USB10TX", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, + 0, + "Linksys", + "USB10T Ethernet", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, + 0, + "Linksys", + "USB100TX Ethernet", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, + 0, + "Linksys", + "USB100H1 Ethernet/HPNA", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, + 0, + "Linksys", + "USB10TA Ethernet", + }, + { + USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, + 0, + "Linksys", + "USB10TX", + }, + { + USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_WUSB11, + 0, + "Linksys", + "WUSB11 Wireless Adapter", + }, + { + USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, + 0, + "Linksys", + "USB 2.0 10/100 Ethernet", + }, + { + USB_VENDOR_LINKSYS3, USB_PRODUCT_LINKSYS3_WUSB11v28, + 0, + "Linksys", + "WUSB11 v2.8 Wireless Adapter", + }, + { + USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, + 0, + "Linksys", + "USB1000", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_M2452, + 0, + "Logitech", + "M2452 keyboard", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_M4848, + 0, + "Logitech", + "M4848 mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_PAGESCAN, + 0, + "Logitech", + "PageScan", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMWEB, + 0, + "Logitech", + "QuickCam Web", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO, + 0, + "Logitech", + "QuickCam Pro", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMEXP, + 0, + "Logitech", + "QuickCam Express", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAM, + 0, + "Logitech", + "QuickCam", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_N43, + 0, + "Logitech", + "N43", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_N48, + 0, + "Logitech", + "N48 mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_MBA47, + 0, + "Logitech", + "M-BA47 mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMMOUSE, + 0, + "Logitech", + "WingMan Gaming Mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_BD58, + 0, + "Logitech", + "BD58 mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN58A, + 0, + "Logitech", + "iFeel Mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN53B, + 0, + "Logitech", + "iFeel MouseMan", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMPAD, + 0, + "Logitech", + "WingMan GamePad Extreme", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMRPAD, + 0, + "Logitech", + "WingMan RumblePad", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMJOY, + 0, + "Logitech", + "WingMan Force joystick", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_BB13, + 0, + "Logitech", + "USB-PS/2 Trackball", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_RK53, + 0, + "Logitech", + "Cordless mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_RB6, + 0, + "Logitech", + "Cordless keyboard", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_MX700, + 0, + "Logitech", + "Cordless optical mouse", + }, + { + USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO2, + 0, + "Logitech", + "QuickCam Pro", + }, + { + USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2, + 0, + "Logitec", + "DVD Multi-plus unit LDR-H443SU2", + }, + { + USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2, + 0, + "Logitec", + "DVD Multi-plus unit LDR-H443U2", + }, + { + USB_VENDOR_LUCENT, USB_PRODUCT_LUCENT_EVALKIT, + 0, + "Lucent", + "USS-720 evaluation kit", + }, + { + USB_VENDOR_LUWEN, USB_PRODUCT_LUWEN_EASYDISK, + 0, + "Luwen", + "EasyDisc", + }, + { + USB_VENDOR_MACALLY, USB_PRODUCT_MACALLY_MOUSE1, + 0, + "Macally", + "mouse", + }, + { + USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, + 0, + "MCT", + "Hub", + }, + { + USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, + 0, + "MCT", + "D-Link DU-H3SP USB BAY Hub", + }, + { + USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, + 0, + "MCT", + "USB-232 Interface", + }, + { + USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, + 0, + "MCT", + "Sitecom USB-232 Products", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, + 0, + "Melco", + "LUA-TX Ethernet", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, + 0, + "Melco", + "LUA-TX Ethernet", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, + 0, + "Melco", + "LUA2-TX Ethernet", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, + 0, + "Melco", + "LUA-KTX Ethernet", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG, + 0, + "Melco", + "USB-IDE Bridge: DUB-PxxG", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, + 0, + "Melco", + "LUA-U2-KTX Ethernet", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB, + 0, + "Melco", + "WLI-U2-KG54-YB WLAN", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54, + 0, + "Melco", + "WLI-U2-KG54 WLAN", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI, + 0, + "Melco", + "WLI-U2-KG54-AI WLAN", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI, + 0, + "Melco", + "Nintendo Wi-Fi", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, + 0, + "Melco", + "PC-OP-RS1 RemoteStation", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP, + 0, + "Melco", + "WLI-U2-SG54HP", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP, + 0, + "Melco", + "WLI-U2-G54HP", + }, + { + USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54L, + 0, + "Melco", + "WLI-U2-KG54L", + }, + { + USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, + 0, + "Merlin", + "Merlin V620", + }, + { + USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY1B, + 0, + "MetaGeek", + "MetaGeek Wi-Spy", + }, + { + USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY24X, + 0, + "MetaGeek", + "MetaGeek Wi-Spy 2.4x", + }, + { + USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS, + 0, + "Metricom", + "Ricochet GS", + }, + { + USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, + 0, + "MGE UPS Systems", + "MGE UPS SYSTEMS PROTECTIONCENTER 1", + }, + { + USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, + 0, + "MGE UPS Systems", + "MGE UPS SYSTEMS PROTECTIONCENTER 2", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_BT_DONGLE, + 0, + "Micro Star International", + "Bluetooth USB dongle", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_UB11B, + 0, + "Micro Star International", + "UB11B", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570, + 0, + "Micro Star International", + "RT2570", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2, + 0, + "Micro Star International", + "RT2570", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3, + 0, + "Micro Star International", + "RT2570", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1, + 0, + "Micro Star International", + "RT2573", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2, + 0, + "Micro Star International", + "RT2573", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3, + 0, + "Micro Star International", + "RT2573", + }, + { + USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4, + 0, + "Micro Star International", + "RT2573", + }, + { + USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TWINKLECAM, + 0, + "Chicony", + "TwinkleCam USB camera", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_SIDEPREC, + 0, + "Microsoft", + "SideWinder Precision Pro", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INTELLIMOUSE, + 0, + "Microsoft", + "IntelliMouse", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_NATURALKBD, + 0, + "Microsoft", + "Natural Keyboard Elite", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_DDS80, + 0, + "Microsoft", + "Digital Sound System 80", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_SIDEWINDER, + 0, + "Microsoft", + "Sidewinder Precision Racing Wheel", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INETPRO, + 0, + "Microsoft", + "Internet Keyboard Pro", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TBEXPLORER, + 0, + "Microsoft", + "Trackball Explorer", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INTELLIEYE, + 0, + "Microsoft", + "IntelliEye mouse", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INETPRO2, + 0, + "Microsoft", + "Internet Keyboard Pro", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN510, + 0, + "Microsoft", + "MN510 Wireless", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, + 0, + "Microsoft", + "10/100 USB NIC", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE, + 0, + "Microsoft", + "Wireless Optical IntelliMouse", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK, + 0, + "Microsoft", + "Wireless Optical Mouse (Model 1023)", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK2, + 0, + "Microsoft", + "Wireless Optical Mouse 3000 (Model 1056)", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK3, + 0, + "Microsoft", + "Wireless Optical Mouse 3000 (Model 1049)", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLUSBMOUSE, + 0, + "Microsoft", + "Wireless USB Mouse", + }, + { + USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_XBOX360, + 0, + "Microsoft", + "XBOX 360 WLAN", + }, + { + USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIDB25, + 0, + "Microtech", + "USB-SCSI-DB25", + }, + { + USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIHD50, + 0, + "Microtech", + "USB-SCSI-HD50", + }, + { + USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM, + 0, + "Microtech", + "USB CameraMate", + }, + { + USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_FREECOM, + 0, + "Microtech", + "Freecom USB-IDE", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX, + 0, + "Microtek", + "Phantom 336CX - C3 scanner", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U, + 0, + "Microtek", + "ScanMaker X6 - X6U", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6, + 0, + "Microtek", + "Phantom C6 scanner", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2, + 0, + "Microtek", + "Phantom 336CX - C3 scanner", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL, + 0, + "Microtek", + "ScanMaker V6USL", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2, + 0, + "Microtek", + "ScanMaker V6USL", + }, + { + USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL, + 0, + "Microtek", + "ScanMaker V6UL", + }, + { + USB_VENDOR_MICROTUNE, USB_PRODUCT_MICROTUNE_BT_DONGLE, + 0, + "Microtune", + "Bluetooth USB dongle", + }, + { + USB_VENDOR_MIDIMAN, USB_PRODUCT_MIDIMAN_MIDISPORT2X2, + 0, + "Midiman", + "Midisport 2x2", + }, + { + USB_VENDOR_MINDSATWORK, USB_PRODUCT_MINDSATWORK_WALLET, + 0, + "Minds At Work", + "Digital Wallet", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_2300, + 0, + "Minolta", + "Dimage 2300", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_S304, + 0, + "Minolta", + "Dimage S304", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_X, + 0, + "Minolta", + "Dimage X", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400, + 0, + "Minolta", + "Dimage 5400", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_F300, + 0, + "Minolta", + "Dimage F300", + }, + { + USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_E223, + 0, + "Minolta", + "Dimage E223", + }, + { + USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_CDRRW, + 0, + "Mitsumi", + "CD-R/RW Drive", + }, + { + USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_BT_DONGLE, + 0, + "Mitsumi", + "Bluetooth USB dongle", + }, + { + USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_FDD, + 0, + "Mitsumi", + "USB FDD", + }, + { + USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA, + 0, + "Mobility", + "Ethernet", + }, + { + USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EASIDOCK, + 0, + "Mobility", + "EasiDock Ethernet", + }, + { + USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, + 0, + "MosChip Semiconductor", + "MCS7703 Serial Port Adapter", + }, + { + USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7830, + 0, + "MosChip Semiconductor", + "MCS7830 Ethernet", + }, + { + USB_VENDOR_MOTOROLA, USB_PRODUCT_MOTOROLA_MC141555, + 0, + "Motorola", + "MC141555 hub controller", + }, + { + USB_VENDOR_MOTOROLA, USB_PRODUCT_MOTOROLA_SB4100, + 0, + "Motorola", + "SB4100 USB Cable Modem", + }, + { + USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_A41XV32X, + 0, + "Motorola", + "A41x/V32x Mobile Phones", + }, + { + USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_E398, + 0, + "Motorola", + "E398 Mobile Phone", + }, + { + USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, + 0, + "Motorola", + "USBLAN", + }, + { + USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, + 0, + "Motorola", + "USBLAN", + }, + { + USB_VENDOR_MULTITECH, USB_PRODUCT_MULTITECH_ATLAS, + 0, + "MultiTech", + "MT5634ZBA-USB modem", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU, + 0, + "Mustek Systems", + "1200 CU scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU, + 0, + "Mustek Systems", + "600 CU scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB, + 0, + "Mustek Systems", + "1200 USB scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB, + 0, + "Mustek Systems", + "1200 UB scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS, + 0, + "Mustek Systems", + "1200 USB Plus scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS, + 0, + "Mustek Systems", + "1200 CU Plus scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F, + 0, + "Mustek Systems", + "BearPaw 1200F scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA, + 0, + "Mustek Systems", + "BearPaw 1200TA scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB, + 0, + "Mustek Systems", + "600 USB scanner", + }, + { + USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_MDC800, + 0, + "Mustek Systems", + "MDC-800 digital camera", + }, + { + USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY, + 0, + "M-Systems", + "DiskOnKey", + }, + { + USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2, + 0, + "M-Systems", + "DiskOnKey", + }, + { + USB_VENDOR_MYSON, USB_PRODUCT_MYSON_HEDEN, + 0, + "Myson Technology", + "USB-IDE", + }, + { + USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200, + 0, + "National Semiconductor", + "BearPaw 1200", + }, + { + USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400, + 0, + "National Semiconductor", + "BearPaw 2400", + }, + { + USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB, + 0, + "NEC", + "hub", + }, + { + USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB_B, + 0, + "NEC", + "hub", + }, + { + USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260, + 0, + "Neodio", + "8-in-1 Multi-format Flash Controller", + }, + { + USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND5010, + 0, + "Neodio", + "Multi-format Flash Controller", + }, + { + USB_VENDOR_NETAC, USB_PRODUCT_NETAC_CF_CARD, + 0, + "Netac", + "USB-CF-Card", + }, + { + USB_VENDOR_NETAC, USB_PRODUCT_NETAC_ONLYDISK, + 0, + "Netac", + "OnlyDisk", + }, + { + USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT, + 0, + "NetChip Technology", + "Turbo-Connect", + }, + { + USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_CLIK_40, + 0, + "NetChip Technology", + "USB Clik! 40", + }, + { + USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, + 0, + "NetChip Technology", + "Linux Ethernet/RNDIS gadget on pxa210/25x/26x", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101, + 0, + "BayNETGEAR", + "Ethernet", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X, + 0, + "BayNETGEAR", + "Ethernet", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, + 0, + "BayNETGEAR", + "Ethernet 10/100, USB1.1", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, + 0, + "BayNETGEAR", + "USB 2.0 Ethernet", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WG111V2_2, + 0, + "BayNETGEAR", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WG111U, + 0, + "BayNETGEAR", + "WG111U", + }, + { + USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WG111U_NF, + 0, + "BayNETGEAR", + "WG111U (no firmware)", + }, + { + USB_VENDOR_NETGEAR2, USB_PRODUCT_NETGEAR2_MA101, + 0, + "Netgear", + "MA101", + }, + { + USB_VENDOR_NETGEAR2, USB_PRODUCT_NETGEAR2_MA101B, + 0, + "Netgear", + "MA101 Rev B", + }, + { + USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WG111T, + 0, + "Netgear", + "WG111T", + }, + { + USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WG111T_NF, + 0, + "Netgear", + "WG111T (no firmware)", + }, + { + USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WPN111, + 0, + "Netgear", + "WPN111", + }, + { + USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WPN111_NF, + 0, + "Netgear", + "WPN111 (no firmware)", + }, + { + USB_VENDOR_NIKON, USB_PRODUCT_NIKON_E990, + 0, + "Nikon", + "Digital Camera E990", + }, + { + USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40, + 0, + "Nikon", + "CoolScan LS40 ED", + }, + { + USB_VENDOR_NIKON, USB_PRODUCT_NIKON_D300, + 0, + "Nikon", + "Digital Camera D300", + }, + { + USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902, + 0, + "NovaTech", + "NovaTech NV-902W", + }, + { + USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573, + 0, + "NovaTech", + "RT2573", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, + 0, + "Novatel Wireless", + "Merlin V620", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, + 0, + "Novatel Wireless", + "Novatel Wireless Merlin CDMA", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, + 0, + "Novatel Wireless", + "Merlin V620", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, + 0, + "Novatel Wireless", + "Merlin V740", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, + 0, + "Novatel Wireless", + "Merlin V720", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, + 0, + "Novatel Wireless", + "Merlin U740", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, + 0, + "Novatel Wireless", + "Merlin U740", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, + 0, + "Novatel Wireless", + "Merlin U870", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, + 0, + "Novatel Wireless", + "Merlin XU870", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, + 0, + "Novatel Wireless", + "Merlin X950D", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, + 0, + "Novatel Wireless", + "ES620 CDMA", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, + 0, + "Novatel Wireless", + "Merlin U720", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, + 0, + "Novatel Wireless", + "Merlin U727 CDMA", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U950D, + 0, + "Novatel Wireless", + "Novatel MC950D HSUPA", + }, + { + USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, + 0, + "Novatel Wireless", + "Novatel ZeroCD", + }, + { + USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, + 0, + "Novatel Wireless", + "NovAtel FlexPack GPS receiver", + }, + { + USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, + 0, + "Merlin", + "Merlin V620", + }, + { + USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, + 0, + "Olympus", + "C-1 Digital Camera", + }, + { + USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C700, + 0, + "Olympus", + "C-700 Ultra Zoom", + }, + { + USB_VENDOR_OMNIVISION, USB_PRODUCT_OMNIVISION_OV511, + 0, + "OmniVision", + "OV511 Camera", + }, + { + USB_VENDOR_OMNIVISION, USB_PRODUCT_OMNIVISION_OV511PLUS, + 0, + "OmniVision", + "OV511+ Camera", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDCFE_B_CF_READER, + 0, + "OnSpec", + "MDCFE-B USB CF Reader", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFMS_RW, + 0, + "OnSpec", + "SIIG/Datafab Memory Stick+CF Reader/Writer", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_READER, + 0, + "OnSpec", + "Datafab-based Reader", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER, + 0, + "OnSpec", + "PNY/Datafab CF+SM Reader", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER2, + 0, + "OnSpec", + "Simple Tech/Datafab CF+SM Reader", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDSM_B_READER, + 0, + "OnSpec", + "MDSM-B reader", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_COMBO, + 0, + "OnSpec", + "USB to CF + SM Combo (LC1)", + }, + { + USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100, + 0, + "OnSpec", + "FlashLink UCF-100 CompactFlash Reader", + }, + { + USB_VENDOR_ONSPEC2, USB_PRODUCT_ONSPEC2_IMAGEMATE_SDDR55, + 0, + "OnSpec Electronic Inc.", + "ImageMate SDDR55", + }, + { + USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, + 0, + "Option N.V:", + "Vodafone Mobile Connect 3G datacard", + }, + { + USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, + 0, + "Option N.V:", + "GlobeTrotter 3G datacard", + }, + { + USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, + 0, + "Option N.V:", + "GlobeTrotter 3G QUAD datacard", + }, + { + USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, + 0, + "Option N.V:", + "GlobeTrotter 3G+ datacard", + }, + { + USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, + 0, + "Option N.V:", + "GlobeTrotter Max 3.6 Modem", + }, + { + USB_VENDOR_OQO, USB_PRODUCT_OQO_WIFI01, + 0, + "OQO", + "model 01 WiFi interface", + }, + { + USB_VENDOR_OQO, USB_PRODUCT_OQO_BT01, + 0, + "OQO", + "model 01 Bluetooth interface", + }, + { + USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, + 0, + "OQO", + "model 01+ Ethernet", + }, + { + USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01, + 0, + "OQO", + "model 01 Ethernet interface", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_SERIAL, + 0, + "Palm Computing", + "USB Serial", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_M500, + 0, + "Palm Computing", + "Palm m500", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_M505, + 0, + "Palm Computing", + "Palm m505", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_M515, + 0, + "Palm Computing", + "Palm m515", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_I705, + 0, + "Palm Computing", + "Palm i705", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z, + 0, + "Palm Computing", + "Palm Tungsten Z", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_M125, + 0, + "Palm Computing", + "Palm m125", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_M130, + 0, + "Palm Computing", + "Palm m130", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T, + 0, + "Palm Computing", + "Palm Tungsten T", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31, + 0, + "Palm Computing", + "Palm Zire 31", + }, + { + USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE, + 0, + "Palm Computing", + "Palm Zire", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_LS120CAM, + 0, + "Panasonic (Matsushita)", + "LS-120 Camera", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXL840AN, + 0, + "Panasonic (Matsushita)", + "CD-R Drive KXL-840AN", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLRW32AN, + 0, + "Panasonic (Matsushita)", + "CD-R Drive KXL-RW32AN", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN, + 0, + "Panasonic (Matsushita)", + "CD-R Drive KXL-CB20AN", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN, + 0, + "Panasonic (Matsushita)", + "DVD-ROM & CD-R/RW", + }, + { + USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_SDCAAE, + 0, + "Panasonic (Matsushita)", + "MultiMediaCard", + }, + { + USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, + 0, + "Peracom Networks", + "Serial", + }, + { + USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET, + 0, + "Peracom Networks", + "Ethernet", + }, + { + USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3, + 0, + "Peracom Networks", + "At Home Ethernet", + }, + { + USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2, + 0, + "Peracom Networks", + "Ethernet", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS350, + 0, + "Philips", + "DSS 350 Digital Speaker System", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS, + 0, + "Philips", + "DSS XXX Digital Speaker System", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_HUB, + 0, + "Philips", + "hub", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCA646VC, + 0, + "Philips", + "PCA646VC PC Camera", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCVC680K, + 0, + "Philips", + "PCVC680K Vesta Pro PC Camera", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS150, + 0, + "Philips", + "DSS 150 Digital Speaker System", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_SNU5600, + 0, + "Philips", + "SNU5600", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_UM10016, + 0, + "Philips", + "ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit", + }, + { + USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DIVAUSB, + 0, + "Philips", + "DIVA USB mp3 player", + }, + { + USB_VENDOR_PHILIPSSEMI, USB_PRODUCT_PHILIPSSEMI_HUB1122, + 0, + "Philips Semiconductors", + "hub", + }, + { + USB_VENDOR_PIENGINEERING, USB_PRODUCT_PIENGINEERING_PS2USB, + 0, + "P.I. Engineering", + "PS2 to Mac USB Adapter", + }, + { + USB_VENDOR_PLANEX, USB_PRODUCT_PLANEX_GW_US11H, + 0, + "Planex Communications", + "GW-US11H WLAN", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US11S, + 0, + "Planex Communications", + "GW-US11S WLAN", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US54GXS, + 0, + "Planex Communications", + "GW-US54GXS WLAN", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP, + 0, + "Planex Communications", + "GW-US54HP", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2, + 0, + "Planex Communications", + "GW-US54Mini2", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54SG, + 0, + "Planex Communications", + "GW-US54SG", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL, + 0, + "Planex Communications", + "GW-US54GZL", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GD, + 0, + "Planex Communications", + "GW-US54GD", + }, + { + USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM, + 0, + "Planex Communications", + "GW-USMM", + }, + { + USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54GZ, + 0, + "Planex Communications", + "GW-US54GZ", + }, + { + USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, + 0, + "Planex Communications", + "GU-1000T", + }, + { + USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI, + 0, + "Planex Communications", + "GW-US54Mini", + }, + { + USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U, + 0, + "Plextor", + "PlexWriter 40/12/40U", + }, + { + USB_VENDOR_PLX, USB_PRODUCT_PLX_TESTBOARD, + 0, + "PLX", + "test board", + }, + { + USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE2, + 0, + "PNY", + "USB 2.0 Flash Drive", + }, + { + USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8, + 0, + "PortGear", + "Ethernet", + }, + { + USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9, + 0, + "PortGear", + "Ethernet", + }, + { + USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA, + 0, + "Portsmith", + "Express Ethernet", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300, + 0, + "Primax Electronics", + "G2-200 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300, + 0, + "Primax Electronics", + "G2E-300 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300, + 0, + "Primax Electronics", + "G2-300 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002, + 0, + "Primax Electronics", + "G2E-300 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600, + 0, + "Primax Electronics", + "Colorado USB 9600 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U, + 0, + "Primax Electronics", + "Colorado 600u scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200, + 0, + "Primax Electronics", + "Visioneer 6200 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200, + 0, + "Primax Electronics", + "Colorado USB 19200 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U, + 0, + "Primax Electronics", + "Colorado 1200u scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600, + 0, + "Primax Electronics", + "G2-600 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I, + 0, + "Primax Electronics", + "ReadyScan 636i", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600, + 0, + "Primax Electronics", + "G2-600 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600, + 0, + "Primax Electronics", + "G2E-600 scanner", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_COMFORT, + 0, + "Primax Electronics", + "Comfort", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_MOUSEINABOX, + 0, + "Primax Electronics", + "Mouse-in-a-Box", + }, + { + USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_PCGAUMS1, + 0, + "Primax Electronics", + "Sony PCGA-UMS1", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301, + 0, + "Prolific Technology", + "PL2301 Host-Host interface", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302, + 0, + "Prolific Technology", + "PL2302 Host-Host interface", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, + 0, + "Prolific Technology", + "PL2303 Serial (IODATA USB-RSAQ2)", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, + 0, + "Prolific Technology", + "PL2303 Serial (ATEN/IOGEAR UC232A)", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2305, + 0, + "Prolific Technology", + "Parallel printer", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_ATAPI4, + 0, + "Prolific Technology", + "ATAPI-4 Controller", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, + 0, + "Prolific Technology", + "PL2501 Host-Host interface", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, + 0, + "Prolific Technology", + "Prolific Pharos", + }, + { + USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, + 0, + "Prolific Technology", + "PL2303 Serial Adapter (IODATA USB-RSAQ3)", + }, + { + USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, + 0, + "Prolific", + "Willcom WSIM", + }, + { + USB_VENDOR_PUTERCOM, USB_PRODUCT_PUTERCOM_UPA100, + 0, + "Putercom", + "USB-1284 BRIDGE", + }, + { + USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573, + 0, + "Qcom", + "RT2573", + }, + { + USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2, + 0, + "Qcom", + "RT2573", + }, + { + USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM, + 0, + "Qualcomm", + "CDMA Technologies MSM phone", + }, + { + USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_RWT_FCT, + 0, + "Qualcomm", + "RWT FCT-CDMA 2000 1xRTT modem", + }, + { + USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM, + 0, + "Qualcomm", + "CDMA Technologies MSM modem", + }, + { + USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, + 0, + "Qualcomm, Incorporated", + "CDMA Technologies MSM modem", + }, + { + USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, + 0, + "Qtronix", + "Scorpion-980N keyboard", + }, + { + USB_VENDOR_QUICKSHOT, USB_PRODUCT_QUICKSHOT_STRIKEPAD, + 0, + "Quickshot", + "USB StrikePad", + }, + { + USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, + 0, + "Radio Shack", + "USB to Serial Cable", + }, + { + USB_VENDOR_RAINBOW, USB_PRODUCT_RAINBOW_IKEY2000, + 0, + "Rainbow Technologies", + "i-Key 2000", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570, + 0, + "Ralink Technology", + "RT2500USB Wireless Adapter", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2, + 0, + "Ralink Technology", + "RT2500USB Wireless Adapter", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, + 0, + "Ralink Technology", + "RT2501USB Wireless Adapter", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671, + 0, + "Ralink Technology", + "RT2601USB Wireless Adapter", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3, + 0, + "Ralink Technology", + "RT2500USB Wireless Adapter", + }, + { + USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2, + 0, + "Ralink Technology", + "RT2501USB Wireless Adapter", + }, + { + USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, + 0, + "Realtek", + "USBKR100 USB Ethernet", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC2, + 0, + "Ricoh", + "VGP-VCC2 Camera", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC3, + 0, + "Ricoh", + "VGP-VCC3 Camera", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC2_2, + 0, + "Ricoh", + "VGP-VCC2 Camera", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC2_3, + 0, + "Ricoh", + "VGP-VCC2 Camera", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC7, + 0, + "Ricoh", + "VGP-VCC7 Camera", + }, + { + USB_VENDOR_RICOH, USB_PRODUCT_RICOH_VGPVCC8, + 0, + "Ricoh", + "VGP-VCC8 Camera", + }, + { + USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM1, + 0, + "Roland", + "UM-1 MIDI I/F", + }, + { + USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM880N, + 0, + "Roland", + "EDIROL UM-880 MIDI I/F (native)", + }, + { + USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM880G, + 0, + "Roland", + "EDIROL UM-880 MIDI I/F (generic)", + }, + { + USB_VENDOR_ROCKFIRE, USB_PRODUCT_ROCKFIRE_GAMEPAD, + 0, + "Rockfire", + "gamepad 203USB", + }, + { + USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, + 0, + "RATOC Systems", + "REX-USB60", + }, + { + USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, + 0, + "Sagem", + "USB-Serial Controller", + }, + { + USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A, + 0, + "Sagem", + "XG-760A", + }, + { + USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG76NA, + 0, + "Sagem", + "XG-76NA", + }, + { + USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_ML6060, + 0, + "Samsung Electronics", + "ML-6060 laser printer", + }, + { + USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_YP_U2, + 0, + "Samsung Electronics", + "YP-U2 MP3 Player", + }, + { + USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_I500, + 0, + "Samsung Electronics", + "I500 Palm USB Phone", + }, + { + USB_VENDOR_SAMSUNG_TECHWIN, USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410, + 0, + "Samsung Techwin", + "Digimax 410", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05A, + 0, + "SanDisk", + "ImageMate SDDR-05a", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR31, + 0, + "SanDisk", + "ImageMate SDDR-31", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05, + 0, + "SanDisk", + "ImageMate SDDR-05", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR12, + 0, + "SanDisk", + "ImageMate SDDR-12", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR09, + 0, + "SanDisk", + "ImageMate SDDR-09", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR75, + 0, + "SanDisk", + "ImageMate SDDR-75", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ2_256, + 0, + "SanDisk", + "Cruzer Mini 256MB", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_128, + 0, + "SanDisk", + "Cruzer Micro 128MB", + }, + { + USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_256, + 0, + "SanDisk", + "Cruzer Micro 256MB", + }, + { + USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900, + 0, + "Sanyo Electric", + "Sanyo SCP-4900 USB Phone", + }, + { + USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, + 0, + "ScanLogic", + "SL11R IDE Adapter", + }, + { + USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX, + 0, + "ScanLogic", + "Phantom 336CX - C3 scanner", + }, + { + USB_VENDOR_SENAO, USB_PRODUCT_SENAO_NUB8301, + 0, + "Senao", + "NUB-8301", + }, + { + USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, + 0, + "ShanTou", + "ST268", + }, + { + USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, + 0, + "ShanTou", + "DM 9601", + }, + { + USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA, + 0, + "Shark", + "Pocket Adapter", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, + 0, + "Sharp", + "Zaurus SL-5500 PDA", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, + 0, + "Sharp", + "Zaurus SL-A300 PDA", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, + 0, + "Sharp", + "Zaurus SL-5600 PDA", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, + 0, + "Sharp", + "Zaurus SL-C700 PDA", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, + 0, + "Sharp", + "Zaurus SL-C750 PDA", + }, + { + USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES, + 0, + "Sharp", + "W-ZERO3 ES Smartphone", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB, + 0, + "Shuttle Technology", + "E-USB Bridge", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI, + 0, + "Shuttle Technology", + "eUSCSI Bridge", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SDDR09, + 0, + "Shuttle Technology", + "ImageMate SDDR09", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBCFSM, + 0, + "Shuttle Technology", + "eUSB SmartMedia / CompactFlash Adapter", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_ZIOMMC, + 0, + "Shuttle Technology", + "eUSB MultiMediaCard Adapter", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_HIFD, + 0, + "Shuttle Technology", + "Sony Hifd", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBATAPI, + 0, + "Shuttle Technology", + "eUSB ATA/ATAPI Adapter", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CF, + 0, + "Shuttle Technology", + "eUSB CompactFlash Adapter", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI_B, + 0, + "Shuttle Technology", + "eUSCSI Bridge", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI_C, + 0, + "Shuttle Technology", + "eUSCSI Bridge", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CDRW, + 0, + "Shuttle Technology", + "CD-RW Device", + }, + { + USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBORCA, + 0, + "Shuttle Technology", + "eUSB ORCA Quad Reader", + }, + { + USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, + 0, + "Siemens", + "SpeedStream", + }, + { + USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM22, + 0, + "Siemens", + "SpeedStream 1022", + }, + { + USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WLL013, + 0, + "Siemens", + "WLL013", + }, + { + USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_ES75, + 0, + "Siemens", + "GSM module MC35", + }, + { + USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G, + 0, + "Siemens", + "54g USB Network Adapter", + }, + { + USB_VENDOR_SIEMENS3, USB_PRODUCT_SIEMENS3_SX1, + 0, + "Siemens", + "SX1", + }, + { + USB_VENDOR_SIEMENS3, USB_PRODUCT_SIEMENS3_X65, + 0, + "Siemens", + "X65", + }, + { + USB_VENDOR_SIEMENS3, USB_PRODUCT_SIEMENS3_X75, + 0, + "Siemens", + "X75", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 580", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 595", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 595U", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 597E", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, + 0, + "Sierra Wireless", + "Sierra Wireless Compass 597", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 880", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 881", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 880E", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 881E", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 880U", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, + 0, + "Sierra Wireless", + "Sierra Wireless AirCard 881U", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, + 0, + "Sierra Wireless", + "EM5625", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, + 0, + "Sierra Wireless", + "MC5720 Wireless Modem", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, + 0, + "Sierra Wireless", + "MC5720", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, + 0, + "Sierra Wireless", + "MC5725", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, + 0, + "Sierra Wireless", + "Sierra Wireless miniPCI 5275", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, + 0, + "Sierra Wireless", + "MC8755", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, + 0, + "Sierra Wireless", + "MC8765", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, + 0, + "Sierra Wireless", + "MC8755", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, + 0, + "Sierra Wireless", + "AC875U HSDPA USB Modem", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, + 0, + "Sierra Wireless", + "MC8755 HSDPA", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, + 0, + "Sierra Wireless", + "MC8775", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, + 0, + "Sierra Wireless", + "Aircard 875 HSDPA", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, + 0, + "Sierra Wireless", + "MC8780", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, + 0, + "Sierra Wireless", + "MC8781", + }, + { + USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, + 0, + "Sierra Wireless", + "Aircard Tru Installer", + }, + { + USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100, + 0, + "Sigmatel", + "i-Bead 100 MP3 Player", + }, + { + USB_VENDOR_SIIG, USB_PRODUCT_SIIG_DIGIFILMREADER, + 0, + "SIIG", + "DigiFilm-Combo Reader", + }, + { + USB_VENDOR_SIIG, USB_PRODUCT_SIIG_WINTERREADER, + 0, + "SIIG", + "WINTERREADER Reader", + }, + { + USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, + 0, + "SIIG", + "USB TO Ethernet", + }, + { + USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, + 0, + "SIIG", + "Serial", + }, + { + USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E, + 0, + "Silicom", + "U2E", + }, + { + USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE, + 0, + "Silicom", + "Psion Gold Port Ethernet", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, + 0, + "Silicon Labs", + "Pololu Serial", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, + 0, + "Silicon Labs", + "Argussoft ISP", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, + 0, + "Silicon Labs", + "Crumb128 board", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, + 0, + "Silicon Labs", + "Degree Controls Inc", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, + 0, + "Silicon Labs", + "Track Systems Traqmate", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, + 0, + "Silicon Labs", + "Suunto Sports Instrument", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, + 0, + "Silicon Labs", + "Burnside Telecon Deskmobile", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, + 0, + "Silicon Labs", + "Helicomm IP-Link 1220-DVM", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, + 0, + "Silicon Labs", + "SILABS USB UART", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, + 0, + "Silicon Labs", + "Lipowsky Baby-JTAG", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, + 0, + "Silicon Labs", + "Lipowsky Baby-LIN", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, + 0, + "Silicon Labs", + "Lipowsky HARP-1", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, + 0, + "Silicon Labs", + "SILABS USB UARTa", + }, + { + USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, + 0, + "Silicon Labs", + "CP210x Serial", + }, + { + USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, + 0, + "SILABS2", + "DCU-11 clone", + }, + { + USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPH_NF, + 0, + "Silicon Portals", + "YAP Phone (no firmware)", + }, + { + USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, + 0, + "Silicon Portals", + "YAP Phone", + }, + { + USB_VENDOR_SIRIUS, USB_PRODUCT_SIRIUS_ROADSTER, + 0, + "Sirius Technologies", + "NetComm Roadster II 56 USB", + }, + { + USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, + 0, + "Sitecom", + "USB 2.0 Ethernet", + }, + { + USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, + 0, + "Sitecom", + "USB to serial cable (v2)", + }, + { + USB_VENDOR_SITECOM2, USB_PRODUCT_SITECOM2_WL022, + 0, + "Sitecom", + "WL-022", + }, + { + USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, + 0, + "Sitecom Europe", + "LN-028", + }, + { + USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113, + 0, + "Sitecom Europe", + "WL-113", + }, + { + USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_ZD1211B, + 0, + "Sitecom Europe", + "ZD1211B", + }, + { + USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172, + 0, + "Sitecom Europe", + "WL-172", + }, + { + USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2, + 0, + "Sitecom Europe", + "WL-113 rev 2", + }, + { + USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_MD_7425, + 0, + "Skanhex Technology, Inc.", + "MD 7425 Camera", + }, + { + USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_SX_520Z, + 0, + "Skanhex Technology, Inc.", + "SX 520z Camera", + }, + { + USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK, + 0, + "SmartBridges", + "SmartLink USB Ethernet", + }, + { + USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, + 0, + "SmartBridges", + "smartNIC 2 PnP Ethernet", + }, + { + USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB, + 0, + "Standard Microsystems", + "10Mbps Ethernet", + }, + { + USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, + 0, + "Standard Microsystems", + "10/100 Ethernet", + }, + { + USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, + 0, + "Standard Microsystems", + "EZ Connect USB Ethernet", + }, + { + USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG, + 0, + "Standard Microsystems", + "EZ Connect Wireless Adapter", + }, + { + USB_VENDOR_SMC2, USB_PRODUCT_SMC2_2020HUB, + 0, + "Standard Microsystems", + "USB Hub", + }, + { + USB_VENDOR_SMC3, USB_PRODUCT_SMC3_2662WUSB, + 0, + "Standard Microsystems", + "2662W-AR Wireless", + }, + { + USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, + 0, + "SOHOware", + "10/100 USB Ethernet", + }, + { + USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, + 0, + "SOHOware", + "10/100 USB Ethernet", + }, + { + USB_VENDOR_SOLIDYEAR, USB_PRODUCT_SOLIDYEAR_KEYBOARD, + 0, + "Solid Year", + "Solid Year USB keyboard", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, + 0, + "Sony", + "DSC cameras", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_NW_MS7, + 0, + "Sony", + "Memorystick NW-MS7", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_PORTABLE_HDD_V2, + 0, + "Sony", + "Portable USB Harddrive V2", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_MSACUS1, + 0, + "Sony", + "Memorystick MSAC-US1", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, + 0, + "Sony", + "Handycam", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, + 0, + "Sony", + "MSC memory stick slot", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35, + 0, + "Sony", + "Sony Clie v3.5", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_PEG_N760C, + 0, + "Sony", + "PEG N760c Memorystick", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40, + 0, + "Sony", + "Sony Clie v4.0", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_MSC_U03, + 0, + "Sony", + "Memorystick MSC-U03", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40_MS, + 0, + "Sony", + "Sony Clie v4.0 Memory Stick slot", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360, + 0, + "Sony", + "Sony Clie s360", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41_MS, + 0, + "Sony", + "Sony Clie v4.1 Memory Stick slot", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41, + 0, + "Sony", + "Sony Clie v4.1", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60, + 0, + "Sony", + "Sony Clie nx60", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TH55, + 0, + "Sony", + "Sony Clie th55", + }, + { + USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37, + 0, + "Sony", + "Sony Clie tj37", + }, + { + USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, + 0, + "Sony Ericsson", + "USB Cable", + }, + { + USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, + 0, + "SOURCENEXT", + "KeikaiDenwa 8", + }, + { + USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, + 0, + "SOURCENEXT", + "KeikaiDenwa 8 with charger", + }, + { + USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573, + 0, + "SparkLAN", + "RT2573", + }, + { + USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R, + 0, + "Sphairon Access Systems GmbH", + "UB801R", + }, + { + USB_VENDOR_STMICRO, USB_PRODUCT_STMICRO_BIOCPU, + 0, + "STMicroelectronics", + "Biometric Coprocessor", + }, + { + USB_VENDOR_STMICRO, USB_PRODUCT_STMICRO_COMMUNICATOR, + 0, + "STMicroelectronics", + "USB Communicator", + }, + { + USB_VENDOR_STSN, USB_PRODUCT_STSN_STSN0001, + 0, + "STSN", + "Internet Access Device", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, + 0, + "SUN Corporation", + "SUNTAC U-Cable type D2", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, + 0, + "SUN Corporation", + "SUNTAC U-Cable type P1", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, + 0, + "SUN Corporation", + "SUNTAC Slipper U", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, + 0, + "SUN Corporation", + "SUNTAC Ir-Trinity", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS64LX, + 0, + "SUN Corporation", + "SUNTAC U-Cable type A3", + }, + { + USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, + 0, + "SUN Corporation", + "SUNTAC U-Cable type A4", + }, + { + USB_VENDOR_SUN, USB_PRODUCT_SUN_KEYBOARD, + 0, + "Sun Microsystems", + "Type 6 USB keyboard", + }, + { + USB_VENDOR_SUN, USB_PRODUCT_SUN_MOUSE, + 0, + "Sun Microsystems", + "Type 6 USB mouse", + }, + { + USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_SUPRAEXPRESS56K, + 0, + "Diamond (Supra)", + "Supra Express 56K modem", + }, + { + USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_SUPRA2890, + 0, + "Diamond (Supra)", + "SupraMax 2890 56K Modem", + }, + { + USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO600USB, + 0, + "Diamond (Supra)", + "Rio 600 USB", + }, + { + USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO800USB, + 0, + "Diamond (Supra)", + "Rio 800 USB", + }, + { + USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570, + 0, + "Surecom Technology", + "RT2570", + }, + { + USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573, + 0, + "Surecom Technology", + "RT2573", + }, + { + USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211, + 0, + "Sweex", + "ZD1211", + }, + { + USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, + 0, + "System Talks", + "SGC-X2UL", + }, + { + USB_VENDOR_TAPWAVE, USB_PRODUCT_TAPWAVE_ZODIAC, + 0, + "Tapwave", + "Zodiac", + }, + { + USB_VENDOR_TAUGA, USB_PRODUCT_TAUGA_CAMERAMATE, + 0, + "Taugagreining HF", + "CameraMate (DPCM_USB)", + }, + { + USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA9664, + 0, + "TDK", + "USB-PDC Adapter UPA9664", + }, + { + USB_VENDOR_TDK, USB_PRODUCT_TDK_UCA1464, + 0, + "TDK", + "USB-cdmaOne Adapter UCA1464", + }, + { + USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, + 0, + "TDK", + "USB-PHS Adapter UHA6400", + }, + { + USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA6400, + 0, + "TDK", + "USB-PHS Adapter UPA6400", + }, + { + USB_VENDOR_TDK, USB_PRODUCT_TDK_BT_DONGLE, + 0, + "TDK", + "Bluetooth USB dongle", + }, + { + USB_VENDOR_TEAC, USB_PRODUCT_TEAC_FD05PUB, + 0, + "TEAC", + "FD-05PUB floppy", + }, + { + USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN, + 0, + "Tekram Technology", + "QuickWLAN", + }, + { + USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_1, + 0, + "Tekram Technology", + "ZD1211", + }, + { + USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_2, + 0, + "Tekram Technology", + "ZD1211", + }, + { + USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, + 0, + "Telex Communications", + "Enhanced USB Microphone", + }, + { + USB_VENDOR_TENX, USB_PRODUCT_TENX_UAUDIO0, + 0, + "Ten X Technology, Inc.", + "USB audio headset", + }, + { + USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, + 0, + "Texas Instruments", + "UT-USB41 hub", + }, + { + USB_VENDOR_TI, USB_PRODUCT_TI_TUSB2046, + 0, + "Texas Instruments", + "TUSB2046 hub", + }, + { + USB_VENDOR_THRUST, USB_PRODUCT_THRUST_FUSION_PAD, + 0, + "Thrustmaster", + "Fusion Digital Gamepad", + }, + { + USB_VENDOR_TOPRE, USB_PRODUCT_TOPRE_HHKB, + 0, + "Topre Corporation", + "HHKB Professional", + }, + { + USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740, + 0, + "Toshiba", + "PocketPC e740", + }, + { + USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE, + 0, + "Trek Technology", + "ThumbDrive", + }, + { + USB_VENDOR_TREK, USB_PRODUCT_TREK_MEMKEY, + 0, + "Trek Technology", + "IBM USB Memory Key", + }, + { + USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB, + 0, + "Trek Technology", + "ThumbDrive_8MB", + }, + { + USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, + 0, + "Tripp-Lite", + "Serial", + }, + { + USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_T33520, + 0, + "Trumpion Microelectronics", + "T33520 USB Flash Card Controller", + }, + { + USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_C3310, + 0, + "Trumpion Microelectronics", + "Comotron C3310 MP3 player", + }, + { + USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_MP3, + 0, + "Trumpion Microelectronics", + "MP3 player", + }, + { + USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240, + 0, + "TwinMOS", + "G240", + }, + { + USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_MDIV, + 0, + "TwinMOS", + "Memory Disk IV", + }, + { + USB_VENDOR_UBIQUAM, USB_PRODUCT_UBIQUAM_UALL, + 0, + "UBIQUAM Co., Ltd.", + "CDMA 1xRTT USB Modem (U-100/105/200/300/520)", + }, + { + USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS, + 0, + "Ultima", + "1200 UB Plus scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U, + 0, + "UMAX Data Systems", + "Astra 1236U Scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U, + 0, + "UMAX Data Systems", + "Astra 1220U Scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U, + 0, + "UMAX Data Systems", + "Astra 2000U Scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U, + 0, + "UMAX Data Systems", + "Astra 2100U Scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U, + 0, + "UMAX Data Systems", + "Astra 2200U Scanner", + }, + { + USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400, + 0, + "UMAX Data Systems", + "Astra 3400 Scanner", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW444UBEU, + 0, + "U-MEDIA Communications", + "TEW-444UB EU", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW444UBEU_NF, + 0, + "U-MEDIA Communications", + "TEW-444UB EU (no firmware)", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A, + 0, + "U-MEDIA Communications", + "TEW-429UB_A", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB, + 0, + "U-MEDIA Communications", + "TEW-429UB", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UBC1, + 0, + "U-MEDIA Communications", + "TEW-429UB C1", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_ALL0298V2, + 0, + "U-MEDIA Communications", + "ALL0298 v2", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_AR5523_2, + 0, + "U-MEDIA Communications", + "AR5523", + }, + { + USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_AR5523_2_NF, + 0, + "U-MEDIA Communications", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_UNIACCESS, USB_PRODUCT_UNIACCESS_PANACHE, + 0, + "Universal Access", + "Panache Surf USB ISDN Adapter", + }, + { + USB_VENDOR_USR, USB_PRODUCT_USR_USR5423, + 0, + "U.S. Robotics", + "USR5423 WLAN", + }, + { + USB_VENDOR_VIA, USB_PRODUCT_VIA_USB2IDEBRIDGE, + 0, + "VIA", + "USB 2.0 IDE Bridge", + }, + { + USB_VENDOR_USI, USB_PRODUCT_USI_MC60, + 0, + "USI", + "MC60 Serial", + }, + { + USB_VENDOR_VIDZMEDIA, USB_PRODUCT_VIDZMEDIA_MONSTERTV, + 0, + "VidzMedia Pte Ltd", + "MonsterTV P2H", + }, + { + USB_VENDOR_VISION, USB_PRODUCT_VISION_VC6452V002, + 0, + "VLSI Vision", + "CPiA Camera", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600, + 0, + "Visioneer", + "OneTouch 7600", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300, + 0, + "Visioneer", + "OneTouch 5300", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000, + 0, + "Visioneer", + "Scanport 3000", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100, + 0, + "Visioneer", + "OneTouch 6100", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200, + 0, + "Visioneer", + "OneTouch 6200", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100, + 0, + "Visioneer", + "OneTouch 8100", + }, + { + USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600, + 0, + "Visioneer", + "OneTouch 8600", + }, + { + USB_VENDOR_VIVITAR, USB_PRODUCT_VIVITAR_35XX, + 0, + "Vivitar", + "Vivicam 35Xx", + }, + { + USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570, + 0, + "VTech", + "RT2570", + }, + { + USB_VENDOR_VTECH, USB_PRODUCT_VTECH_ZD1211B, + 0, + "VTech", + "ZD1211B", + }, + { + USB_VENDOR_WACOM, USB_PRODUCT_WACOM_CT0405U, + 0, + "WACOM", + "CT-0405-U Tablet", + }, + { + USB_VENDOR_WACOM, USB_PRODUCT_WACOM_GRAPHIRE, + 0, + "WACOM", + "Graphire", + }, + { + USB_VENDOR_WACOM, USB_PRODUCT_WACOM_GRAPHIRE3_4X5, + 0, + "WACOM", + "Graphire 3 4x5", + }, + { + USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOSA5, + 0, + "WACOM", + "Intuos A5", + }, + { + USB_VENDOR_WACOM, USB_PRODUCT_WACOM_GD0912U, + 0, + "WACOM", + "Intuos 9x12 Graphics Tablet", + }, + { + USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, + 0, + "QinHeng Electronics", + "CH341/CH340 USB-Serial Bridge", + }, + { + USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_COMBO, + 0, + "Western Digital", + "Firewire USB Combo", + }, + { + USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_EXTHDD, + 0, + "Western Digital", + "External HDD", + }, + { + USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_HUB, + 0, + "Western Digital", + "USB HUB", + }, + { + USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_MYBOOK, + 0, + "Western Digital", + "MyBook External HDD", + }, + { + USB_VENDOR_WINBOND, USB_PRODUCT_WINBOND_UH104, + 0, + "Winbond", + "4-port USB Hub", + }, + { + USB_VENDOR_WINMAXGROUP, USB_PRODUCT_WINMAXGROUP_FLASH64MC, + 0, + "WinMaxGroup", + "USB Flash Disk 64M-C", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR045G, + 0, + "Wistron NeWeb", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G, + 0, + "Wistron NeWeb", + "UR055G", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_AR5523_1, + 0, + "Wistron NeWeb", + "AR5523", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_AR5523_1_NF, + 0, + "Wistron NeWeb", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_AR5523_2, + 0, + "Wistron NeWeb", + "AR5523", + }, + { + USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_AR5523_2_NF, + 0, + "Wistron NeWeb", + "AR5523 (no firmware)", + }, + { + USB_VENDOR_XEROX, USB_PRODUCT_XEROX_WCM15, + 0, + "Xerox", + "WorkCenter M15", + }, + { + USB_VENDOR_XIRLINK, USB_PRODUCT_XIRLINK_PCCAM, + 0, + "Xirlink", + "IBM PC Camera", + }, + { + USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_1, + 0, + "Xyratex", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_2, + 0, + "Xyratex", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_YEDATA, USB_PRODUCT_YEDATA_FLASHBUSTERU, + 0, + "Y-E Data", + "Flashbuster-U", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UX256, + 0, + "YAMAHA", + "UX256 MIDI I/F", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UX96, + 0, + "YAMAHA", + "UX96 MIDI I/F", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA54I, + 0, + "YAMAHA", + "NetVolante RTA54i Broadband&ISDN Router", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA55I, + 0, + "YAMAHA", + "NetVolante RTA55i Broadband VoIP Router", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65B, + 0, + "YAMAHA", + "NetVolante RTW65b Broadband Wireless Router", + }, + { + USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65I, + 0, + "YAMAHA", + "NetVolante RTW65i Broadband&ISDN Wireless Router", + }, + { + USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO, + 0, + "Yano", + "U640MO-03", + }, + { + USB_VENDOR_YANO, USB_PRODUCT_YANO_FW800HD, + 0, + "Yano", + "METALWEAR-HDD", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_M4Y750, + 0, + "Z-Com", + "M4Y-750", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_XI725, + 0, + "Z-Com", + "XI-725/726", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_XI735, + 0, + "Z-Com", + "XI-735", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_XG703A, + 0, + "Z-Com", + "PrismGT USB 2.0 WLAN", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211, + 0, + "Z-Com", + "ZD1211", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_AR5523, + 0, + "Z-Com", + "AR5523", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_AR5523_NF, + 0, + "Z-Com", + "AR5523 driver (no firmware)", + }, + { + USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211B, + 0, + "Z-Com", + "ZD1211B", + }, + { + USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570, + 0, + "Zinwell", + "RT2570", + }, + { + USB_VENDOR_ZOOM, USB_PRODUCT_ZOOM_2986L, + 0, + "Zoom Telephonics", + "2986L Fax modem", + }, + { + USB_VENDOR_ZORAN, USB_PRODUCT_ZORAN_EX20DSC, + 0, + "Zoran Microelectronics", + "Digital Camera EX-20 DSC", + }, + { + USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211, + 0, + "Zydas Technology Corporation", + "ZD1211 WLAN abg", + }, + { + USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211B, + 0, + "Zydas Technology Corporation", + "ZD1211B", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_OMNI56K, + 0, + "ZyXEL Communication", + "Omni 56K Plus", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_980N, + 0, + "ZyXEL Communication", + "Scorpion-980N keyboard", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220, + 0, + "ZyXEL Communication", + "ZyAIR G-220", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G200V2, + 0, + "ZyXEL Communication", + "G-200 v2", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_AG225H, + 0, + "ZyXEL Communication", + "AG-225H", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_M202, + 0, + "ZyXEL Communication", + "M-202", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G220V2, + 0, + "ZyXEL Communication", + "G-220 v2", + }, + { + USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G202, + 0, + "ZyXEL Communication", + "G-202", + }, + { + USB_VENDOR_UNKNOWN1, 0, + USB_KNOWNDEV_NOPROD, + "Unknown vendor", + NULL, + }, + { + USB_VENDOR_UNKNOWN2, 0, + USB_KNOWNDEV_NOPROD, + "Unknown vendor", + NULL, + }, + { + USB_VENDOR_EGALAX2, 0, + USB_KNOWNDEV_NOPROD, + "eGalax, Inc.", + NULL, + }, + { + USB_VENDOR_HUMAX, 0, + USB_KNOWNDEV_NOPROD, + "HUMAX", + NULL, + }, + { + USB_VENDOR_LTS, 0, + USB_KNOWNDEV_NOPROD, + "LTS", + NULL, + }, + { + USB_VENDOR_BWCT, 0, + USB_KNOWNDEV_NOPROD, + "Bernd Walter Computer Technology", + NULL, + }, + { + USB_VENDOR_AOX, 0, + USB_KNOWNDEV_NOPROD, + "AOX", + NULL, + }, + { + USB_VENDOR_THESYS, 0, + USB_KNOWNDEV_NOPROD, + "Thesys", + NULL, + }, + { + USB_VENDOR_DATABROADCAST, 0, + USB_KNOWNDEV_NOPROD, + "Data Broadcasting", + NULL, + }, + { + USB_VENDOR_ATMEL, 0, + USB_KNOWNDEV_NOPROD, + "Atmel", + NULL, + }, + { + USB_VENDOR_IWATSU, 0, + USB_KNOWNDEV_NOPROD, + "Iwatsu America", + NULL, + }, + { + USB_VENDOR_MITSUMI, 0, + USB_KNOWNDEV_NOPROD, + "Mitsumi", + NULL, + }, + { + USB_VENDOR_HP, 0, + USB_KNOWNDEV_NOPROD, + "Hewlett Packard", + NULL, + }, + { + USB_VENDOR_GENOA, 0, + USB_KNOWNDEV_NOPROD, + "Genoa", + NULL, + }, + { + USB_VENDOR_OAK, 0, + USB_KNOWNDEV_NOPROD, + "Oak", + NULL, + }, + { + USB_VENDOR_ADAPTEC, 0, + USB_KNOWNDEV_NOPROD, + "Adaptec", + NULL, + }, + { + USB_VENDOR_DIEBOLD, 0, + USB_KNOWNDEV_NOPROD, + "Diebold", + NULL, + }, + { + USB_VENDOR_SIEMENSELECTRO, 0, + USB_KNOWNDEV_NOPROD, + "Siemens Electromechanical", + NULL, + }, + { + USB_VENDOR_EPSONIMAGING, 0, + USB_KNOWNDEV_NOPROD, + "Epson Imaging", + NULL, + }, + { + USB_VENDOR_KEYTRONIC, 0, + USB_KNOWNDEV_NOPROD, + "KeyTronic", + NULL, + }, + { + USB_VENDOR_OPTI, 0, + USB_KNOWNDEV_NOPROD, + "OPTi", + NULL, + }, + { + USB_VENDOR_ELITEGROUP, 0, + USB_KNOWNDEV_NOPROD, + "Elitegroup", + NULL, + }, + { + USB_VENDOR_XILINX, 0, + USB_KNOWNDEV_NOPROD, + "Xilinx", + NULL, + }, + { + USB_VENDOR_FARALLON, 0, + USB_KNOWNDEV_NOPROD, + "Farallon Communications", + NULL, + }, + { + USB_VENDOR_NATIONAL, 0, + USB_KNOWNDEV_NOPROD, + "National Semiconductor", + NULL, + }, + { + USB_VENDOR_NATIONALREG, 0, + USB_KNOWNDEV_NOPROD, + "National Registry", + NULL, + }, + { + USB_VENDOR_ACERLABS, 0, + USB_KNOWNDEV_NOPROD, + "Acer Labs", + NULL, + }, + { + USB_VENDOR_FTDI, 0, + USB_KNOWNDEV_NOPROD, + "Future Technology Devices", + NULL, + }, + { + USB_VENDOR_NCR, 0, + USB_KNOWNDEV_NOPROD, + "NCR", + NULL, + }, + { + USB_VENDOR_SYNOPSYS2, 0, + USB_KNOWNDEV_NOPROD, + "Synopsys", + NULL, + }, + { + USB_VENDOR_FUJITSUICL, 0, + USB_KNOWNDEV_NOPROD, + "Fujitsu-ICL", + NULL, + }, + { + USB_VENDOR_FUJITSU2, 0, + USB_KNOWNDEV_NOPROD, + "Fujitsu Personal Systems", + NULL, + }, + { + USB_VENDOR_QUANTA, 0, + USB_KNOWNDEV_NOPROD, + "Quanta", + NULL, + }, + { + USB_VENDOR_NEC, 0, + USB_KNOWNDEV_NOPROD, + "NEC", + NULL, + }, + { + USB_VENDOR_KODAK, 0, + USB_KNOWNDEV_NOPROD, + "Eastman Kodak", + NULL, + }, + { + USB_VENDOR_WELTREND, 0, + USB_KNOWNDEV_NOPROD, + "Weltrend", + NULL, + }, + { + USB_VENDOR_VIA, 0, + USB_KNOWNDEV_NOPROD, + "VIA", + NULL, + }, + { + USB_VENDOR_MCCI, 0, + USB_KNOWNDEV_NOPROD, + "MCCI", + NULL, + }, + { + USB_VENDOR_MELCO, 0, + USB_KNOWNDEV_NOPROD, + "Melco", + NULL, + }, + { + USB_VENDOR_LEADTEK, 0, + USB_KNOWNDEV_NOPROD, + "Leadtek", + NULL, + }, + { + USB_VENDOR_WINBOND, 0, + USB_KNOWNDEV_NOPROD, + "Winbond", + NULL, + }, + { + USB_VENDOR_PHOENIX, 0, + USB_KNOWNDEV_NOPROD, + "Phoenix", + NULL, + }, + { + USB_VENDOR_CREATIVE, 0, + USB_KNOWNDEV_NOPROD, + "Creative Labs", + NULL, + }, + { + USB_VENDOR_NOKIA, 0, + USB_KNOWNDEV_NOPROD, + "Nokia", + NULL, + }, + { + USB_VENDOR_ADI, 0, + USB_KNOWNDEV_NOPROD, + "ADI Systems", + NULL, + }, + { + USB_VENDOR_CATC, 0, + USB_KNOWNDEV_NOPROD, + "Computer Access Technology", + NULL, + }, + { + USB_VENDOR_SMC2, 0, + USB_KNOWNDEV_NOPROD, + "Standard Microsystems", + NULL, + }, + { + USB_VENDOR_MOTOROLA_HK, 0, + USB_KNOWNDEV_NOPROD, + "Motorola HK", + NULL, + }, + { + USB_VENDOR_GRAVIS, 0, + USB_KNOWNDEV_NOPROD, + "Advanced Gravis Computer", + NULL, + }, + { + USB_VENDOR_CIRRUSLOGIC, 0, + USB_KNOWNDEV_NOPROD, + "Cirrus Logic", + NULL, + }, + { + USB_VENDOR_INNOVATIVE, 0, + USB_KNOWNDEV_NOPROD, + "Innovative Semiconductors", + NULL, + }, + { + USB_VENDOR_MOLEX, 0, + USB_KNOWNDEV_NOPROD, + "Molex", + NULL, + }, + { + USB_VENDOR_SUN, 0, + USB_KNOWNDEV_NOPROD, + "Sun Microsystems", + NULL, + }, + { + USB_VENDOR_UNISYS, 0, + USB_KNOWNDEV_NOPROD, + "Unisys", + NULL, + }, + { + USB_VENDOR_TAUGA, 0, + USB_KNOWNDEV_NOPROD, + "Taugagreining HF", + NULL, + }, + { + USB_VENDOR_AMD, 0, + USB_KNOWNDEV_NOPROD, + "Advanced Micro Devices", + NULL, + }, + { + USB_VENDOR_LEXMARK, 0, + USB_KNOWNDEV_NOPROD, + "Lexmark International", + NULL, + }, + { + USB_VENDOR_LG, 0, + USB_KNOWNDEV_NOPROD, + "LG Electronics", + NULL, + }, + { + USB_VENDOR_NANAO, 0, + USB_KNOWNDEV_NOPROD, + "NANAO", + NULL, + }, + { + USB_VENDOR_GATEWAY, 0, + USB_KNOWNDEV_NOPROD, + "Gateway 2000", + NULL, + }, + { + USB_VENDOR_NMB, 0, + USB_KNOWNDEV_NOPROD, + "NMB", + NULL, + }, + { + USB_VENDOR_ALPS, 0, + USB_KNOWNDEV_NOPROD, + "Alps Electric", + NULL, + }, + { + USB_VENDOR_THRUST, 0, + USB_KNOWNDEV_NOPROD, + "Thrustmaster", + NULL, + }, + { + USB_VENDOR_TI, 0, + USB_KNOWNDEV_NOPROD, + "Texas Instruments", + NULL, + }, + { + USB_VENDOR_ANALOGDEVICES, 0, + USB_KNOWNDEV_NOPROD, + "Analog Devices", + NULL, + }, + { + USB_VENDOR_SIS, 0, + USB_KNOWNDEV_NOPROD, + "Silicon Integrated Systems Corp.", + NULL, + }, + { + USB_VENDOR_KYE, 0, + USB_KNOWNDEV_NOPROD, + "KYE Systems", + NULL, + }, + { + USB_VENDOR_DIAMOND2, 0, + USB_KNOWNDEV_NOPROD, + "Diamond (Supra)", + NULL, + }, + { + USB_VENDOR_RENESAS, 0, + USB_KNOWNDEV_NOPROD, + "Renesas", + NULL, + }, + { + USB_VENDOR_MICROSOFT, 0, + USB_KNOWNDEV_NOPROD, + "Microsoft", + NULL, + }, + { + USB_VENDOR_PRIMAX, 0, + USB_KNOWNDEV_NOPROD, + "Primax Electronics", + NULL, + }, + { + USB_VENDOR_MGE, 0, + USB_KNOWNDEV_NOPROD, + "MGE UPS Systems", + NULL, + }, + { + USB_VENDOR_AMP, 0, + USB_KNOWNDEV_NOPROD, + "AMP", + NULL, + }, + { + USB_VENDOR_CHERRY, 0, + USB_KNOWNDEV_NOPROD, + "Cherry Mikroschalter", + NULL, + }, + { + USB_VENDOR_MEGATRENDS, 0, + USB_KNOWNDEV_NOPROD, + "American Megatrends", + NULL, + }, + { + USB_VENDOR_LOGITECH, 0, + USB_KNOWNDEV_NOPROD, + "Logitech", + NULL, + }, + { + USB_VENDOR_BTC, 0, + USB_KNOWNDEV_NOPROD, + "Behavior Tech. Computer", + NULL, + }, + { + USB_VENDOR_PHILIPS, 0, + USB_KNOWNDEV_NOPROD, + "Philips", + NULL, + }, + { + USB_VENDOR_SUN2, 0, + USB_KNOWNDEV_NOPROD, + "Sun Microsystems (offical)", + NULL, + }, + { + USB_VENDOR_SANYO, 0, + USB_KNOWNDEV_NOPROD, + "Sanyo Electric", + NULL, + }, + { + USB_VENDOR_SEAGATE, 0, + USB_KNOWNDEV_NOPROD, + "Seagate", + NULL, + }, + { + USB_VENDOR_CONNECTIX, 0, + USB_KNOWNDEV_NOPROD, + "Connectix", + NULL, + }, + { + USB_VENDOR_SEMTECH, 0, + USB_KNOWNDEV_NOPROD, + "Semtech", + NULL, + }, + { + USB_VENDOR_KENSINGTON, 0, + USB_KNOWNDEV_NOPROD, + "Kensington", + NULL, + }, + { + USB_VENDOR_LUCENT, 0, + USB_KNOWNDEV_NOPROD, + "Lucent", + NULL, + }, + { + USB_VENDOR_PLANTRONICS, 0, + USB_KNOWNDEV_NOPROD, + "Plantronics", + NULL, + }, + { + USB_VENDOR_KYOCERA, 0, + USB_KNOWNDEV_NOPROD, + "Kyocera Wireless Corp.", + NULL, + }, + { + USB_VENDOR_STMICRO, 0, + USB_KNOWNDEV_NOPROD, + "STMicroelectronics", + NULL, + }, + { + USB_VENDOR_FOXCONN, 0, + USB_KNOWNDEV_NOPROD, + "Foxconn", + NULL, + }, + { + USB_VENDOR_YAMAHA, 0, + USB_KNOWNDEV_NOPROD, + "YAMAHA", + NULL, + }, + { + USB_VENDOR_COMPAQ, 0, + USB_KNOWNDEV_NOPROD, + "Compaq", + NULL, + }, + { + USB_VENDOR_HITACHI, 0, + USB_KNOWNDEV_NOPROD, + "Hitachi", + NULL, + }, + { + USB_VENDOR_ACERP, 0, + USB_KNOWNDEV_NOPROD, + "Acer Peripherals", + NULL, + }, + { + USB_VENDOR_DAVICOM, 0, + USB_KNOWNDEV_NOPROD, + "Davicom", + NULL, + }, + { + USB_VENDOR_VISIONEER, 0, + USB_KNOWNDEV_NOPROD, + "Visioneer", + NULL, + }, + { + USB_VENDOR_CANON, 0, + USB_KNOWNDEV_NOPROD, + "Canon", + NULL, + }, + { + USB_VENDOR_NIKON, 0, + USB_KNOWNDEV_NOPROD, + "Nikon", + NULL, + }, + { + USB_VENDOR_PAN, 0, + USB_KNOWNDEV_NOPROD, + "Pan International", + NULL, + }, + { + USB_VENDOR_IBM, 0, + USB_KNOWNDEV_NOPROD, + "IBM", + NULL, + }, + { + USB_VENDOR_CYPRESS, 0, + USB_KNOWNDEV_NOPROD, + "Cypress Semiconductor", + NULL, + }, + { + USB_VENDOR_ROHM, 0, + USB_KNOWNDEV_NOPROD, + "ROHM", + NULL, + }, + { + USB_VENDOR_COMPAL, 0, + USB_KNOWNDEV_NOPROD, + "Compal", + NULL, + }, + { + USB_VENDOR_EPSON, 0, + USB_KNOWNDEV_NOPROD, + "Seiko Epson", + NULL, + }, + { + USB_VENDOR_RAINBOW, 0, + USB_KNOWNDEV_NOPROD, + "Rainbow Technologies", + NULL, + }, + { + USB_VENDOR_IODATA, 0, + USB_KNOWNDEV_NOPROD, + "I-O Data", + NULL, + }, + { + USB_VENDOR_TDK, 0, + USB_KNOWNDEV_NOPROD, + "TDK", + NULL, + }, + { + USB_VENDOR_3COMUSR, 0, + USB_KNOWNDEV_NOPROD, + "U.S. Robotics", + NULL, + }, + { + USB_VENDOR_METHODE, 0, + USB_KNOWNDEV_NOPROD, + "Methode Electronics Far East", + NULL, + }, + { + USB_VENDOR_MAXISWITCH, 0, + USB_KNOWNDEV_NOPROD, + "Maxi Switch", + NULL, + }, + { + USB_VENDOR_LOCKHEEDMER, 0, + USB_KNOWNDEV_NOPROD, + "Lockheed Martin Energy Research", + NULL, + }, + { + USB_VENDOR_FUJITSU, 0, + USB_KNOWNDEV_NOPROD, + "Fujitsu", + NULL, + }, + { + USB_VENDOR_TOSHIBAAM, 0, + USB_KNOWNDEV_NOPROD, + "Toshiba America", + NULL, + }, + { + USB_VENDOR_MICROMACRO, 0, + USB_KNOWNDEV_NOPROD, + "Micro Macro Technologies", + NULL, + }, + { + USB_VENDOR_KONICA, 0, + USB_KNOWNDEV_NOPROD, + "Konica", + NULL, + }, + { + USB_VENDOR_LITEON, 0, + USB_KNOWNDEV_NOPROD, + "Lite-On Technology", + NULL, + }, + { + USB_VENDOR_FUJIPHOTO, 0, + USB_KNOWNDEV_NOPROD, + "Fuji Photo Film", + NULL, + }, + { + USB_VENDOR_PHILIPSSEMI, 0, + USB_KNOWNDEV_NOPROD, + "Philips Semiconductors", + NULL, + }, + { + USB_VENDOR_TATUNG, 0, + USB_KNOWNDEV_NOPROD, + "Tatung Co. Of America", + NULL, + }, + { + USB_VENDOR_SCANLOGIC, 0, + USB_KNOWNDEV_NOPROD, + "ScanLogic", + NULL, + }, + { + USB_VENDOR_MYSON, 0, + USB_KNOWNDEV_NOPROD, + "Myson Technology", + NULL, + }, + { + USB_VENDOR_DIGI2, 0, + USB_KNOWNDEV_NOPROD, + "Digi", + NULL, + }, + { + USB_VENDOR_ITTCANON, 0, + USB_KNOWNDEV_NOPROD, + "ITT Canon", + NULL, + }, + { + USB_VENDOR_ALTEC, 0, + USB_KNOWNDEV_NOPROD, + "Altec Lansing", + NULL, + }, + { + USB_VENDOR_LSI, 0, + USB_KNOWNDEV_NOPROD, + "LSI", + NULL, + }, + { + USB_VENDOR_MENTORGRAPHICS, 0, + USB_KNOWNDEV_NOPROD, + "Mentor Graphics", + NULL, + }, + { + USB_VENDOR_ITUNERNET, 0, + USB_KNOWNDEV_NOPROD, + "I-Tuner Networks", + NULL, + }, + { + USB_VENDOR_HOLTEK, 0, + USB_KNOWNDEV_NOPROD, + "Holtek Semiconductor, Inc.", + NULL, + }, + { + USB_VENDOR_PANASONIC, 0, + USB_KNOWNDEV_NOPROD, + "Panasonic (Matsushita)", + NULL, + }, + { + USB_VENDOR_HUANHSIN, 0, + USB_KNOWNDEV_NOPROD, + "Huan Hsin", + NULL, + }, + { + USB_VENDOR_SHARP, 0, + USB_KNOWNDEV_NOPROD, + "Sharp", + NULL, + }, + { + USB_VENDOR_IIYAMA, 0, + USB_KNOWNDEV_NOPROD, + "Iiyama", + NULL, + }, + { + USB_VENDOR_SHUTTLE, 0, + USB_KNOWNDEV_NOPROD, + "Shuttle Technology", + NULL, + }, + { + USB_VENDOR_ELO, 0, + USB_KNOWNDEV_NOPROD, + "Elo TouchSystems", + NULL, + }, + { + USB_VENDOR_SAMSUNG, 0, + USB_KNOWNDEV_NOPROD, + "Samsung Electronics", + NULL, + }, + { + USB_VENDOR_NORTHSTAR, 0, + USB_KNOWNDEV_NOPROD, + "Northstar", + NULL, + }, + { + USB_VENDOR_TOKYOELECTRON, 0, + USB_KNOWNDEV_NOPROD, + "Tokyo Electron", + NULL, + }, + { + USB_VENDOR_ANNABOOKS, 0, + USB_KNOWNDEV_NOPROD, + "Annabooks", + NULL, + }, + { + USB_VENDOR_JVC, 0, + USB_KNOWNDEV_NOPROD, + "JVC", + NULL, + }, + { + USB_VENDOR_CHICONY, 0, + USB_KNOWNDEV_NOPROD, + "Chicony Electronics", + NULL, + }, + { + USB_VENDOR_ELAN, 0, + USB_KNOWNDEV_NOPROD, + "Elan", + NULL, + }, + { + USB_VENDOR_NEWNEX, 0, + USB_KNOWNDEV_NOPROD, + "Newnex", + NULL, + }, + { + USB_VENDOR_BROTHER, 0, + USB_KNOWNDEV_NOPROD, + "Brother Industries", + NULL, + }, + { + USB_VENDOR_DALLAS, 0, + USB_KNOWNDEV_NOPROD, + "Dallas Semiconductor", + NULL, + }, + { + USB_VENDOR_SUNPLUS, 0, + USB_KNOWNDEV_NOPROD, + "Sunplus", + NULL, + }, + { + USB_VENDOR_PFU, 0, + USB_KNOWNDEV_NOPROD, + "PFU", + NULL, + }, + { + USB_VENDOR_FUJIKURA, 0, + USB_KNOWNDEV_NOPROD, + "Fujikura/DDK", + NULL, + }, + { + USB_VENDOR_ACER, 0, + USB_KNOWNDEV_NOPROD, + "Acer", + NULL, + }, + { + USB_VENDOR_3COM, 0, + USB_KNOWNDEV_NOPROD, + "3Com", + NULL, + }, + { + USB_VENDOR_HOSIDEN, 0, + USB_KNOWNDEV_NOPROD, + "Hosiden Corporation", + NULL, + }, + { + USB_VENDOR_AZTECH, 0, + USB_KNOWNDEV_NOPROD, + "Aztech Systems", + NULL, + }, + { + USB_VENDOR_BELKIN, 0, + USB_KNOWNDEV_NOPROD, + "Belkin Components", + NULL, + }, + { + USB_VENDOR_KAWATSU, 0, + USB_KNOWNDEV_NOPROD, + "Kawatsu Semiconductor", + NULL, + }, + { + USB_VENDOR_FCI, 0, + USB_KNOWNDEV_NOPROD, + "FCI", + NULL, + }, + { + USB_VENDOR_LONGWELL, 0, + USB_KNOWNDEV_NOPROD, + "Longwell", + NULL, + }, + { + USB_VENDOR_COMPOSITE, 0, + USB_KNOWNDEV_NOPROD, + "Composite", + NULL, + }, + { + USB_VENDOR_STAR, 0, + USB_KNOWNDEV_NOPROD, + "Star Micronics", + NULL, + }, + { + USB_VENDOR_APC, 0, + USB_KNOWNDEV_NOPROD, + "American Power Conversion", + NULL, + }, + { + USB_VENDOR_SCIATLANTA, 0, + USB_KNOWNDEV_NOPROD, + "Scientific Atlanta", + NULL, + }, + { + USB_VENDOR_TSM, 0, + USB_KNOWNDEV_NOPROD, + "TSM", + NULL, + }, + { + USB_VENDOR_CONNECTEK, 0, + USB_KNOWNDEV_NOPROD, + "Advanced Connectek USA", + NULL, + }, + { + USB_VENDOR_NETCHIP, 0, + USB_KNOWNDEV_NOPROD, + "NetChip Technology", + NULL, + }, + { + USB_VENDOR_ALTRA, 0, + USB_KNOWNDEV_NOPROD, + "ALTRA", + NULL, + }, + { + USB_VENDOR_ATI, 0, + USB_KNOWNDEV_NOPROD, + "ATI Technologies", + NULL, + }, + { + USB_VENDOR_AKS, 0, + USB_KNOWNDEV_NOPROD, + "Aladdin Knowledge Systems", + NULL, + }, + { + USB_VENDOR_TEKOM, 0, + USB_KNOWNDEV_NOPROD, + "Tekom", + NULL, + }, + { + USB_VENDOR_CANONDEV, 0, + USB_KNOWNDEV_NOPROD, + "Canon", + NULL, + }, + { + USB_VENDOR_WACOMTECH, 0, + USB_KNOWNDEV_NOPROD, + "Wacom", + NULL, + }, + { + USB_VENDOR_INVENTEC, 0, + USB_KNOWNDEV_NOPROD, + "Inventec", + NULL, + }, + { + USB_VENDOR_SHYHSHIUN, 0, + USB_KNOWNDEV_NOPROD, + "Shyh Shiun Terminals", + NULL, + }, + { + USB_VENDOR_PREHWERKE, 0, + USB_KNOWNDEV_NOPROD, + "Preh Werke Gmbh & Co. KG", + NULL, + }, + { + USB_VENDOR_SYNOPSYS, 0, + USB_KNOWNDEV_NOPROD, + "Synopsys", + NULL, + }, + { + USB_VENDOR_UNIACCESS, 0, + USB_KNOWNDEV_NOPROD, + "Universal Access", + NULL, + }, + { + USB_VENDOR_VIEWSONIC, 0, + USB_KNOWNDEV_NOPROD, + "ViewSonic", + NULL, + }, + { + USB_VENDOR_XIRLINK, 0, + USB_KNOWNDEV_NOPROD, + "Xirlink", + NULL, + }, + { + USB_VENDOR_ANCHOR, 0, + USB_KNOWNDEV_NOPROD, + "Anchor Chips", + NULL, + }, + { + USB_VENDOR_SONY, 0, + USB_KNOWNDEV_NOPROD, + "Sony", + NULL, + }, + { + USB_VENDOR_FUJIXEROX, 0, + USB_KNOWNDEV_NOPROD, + "Fuji Xerox", + NULL, + }, + { + USB_VENDOR_VISION, 0, + USB_KNOWNDEV_NOPROD, + "VLSI Vision", + NULL, + }, + { + USB_VENDOR_ASAHIKASEI, 0, + USB_KNOWNDEV_NOPROD, + "Asahi Kasei Microsystems", + NULL, + }, + { + USB_VENDOR_ATEN, 0, + USB_KNOWNDEV_NOPROD, + "ATEN International", + NULL, + }, + { + USB_VENDOR_SAMSUNG2, 0, + USB_KNOWNDEV_NOPROD, + "Samsung Electronics", + NULL, + }, + { + USB_VENDOR_MUSTEK, 0, + USB_KNOWNDEV_NOPROD, + "Mustek Systems", + NULL, + }, + { + USB_VENDOR_TELEX, 0, + USB_KNOWNDEV_NOPROD, + "Telex Communications", + NULL, + }, + { + USB_VENDOR_CHINON, 0, + USB_KNOWNDEV_NOPROD, + "Chinon", + NULL, + }, + { + USB_VENDOR_PERACOM, 0, + USB_KNOWNDEV_NOPROD, + "Peracom Networks", + NULL, + }, + { + USB_VENDOR_ALCOR2, 0, + USB_KNOWNDEV_NOPROD, + "Alcor Micro", + NULL, + }, + { + USB_VENDOR_XYRATEX, 0, + USB_KNOWNDEV_NOPROD, + "Xyratex", + NULL, + }, + { + USB_VENDOR_WACOM, 0, + USB_KNOWNDEV_NOPROD, + "WACOM", + NULL, + }, + { + USB_VENDOR_ETEK, 0, + USB_KNOWNDEV_NOPROD, + "e-TEK Labs", + NULL, + }, + { + USB_VENDOR_EIZO, 0, + USB_KNOWNDEV_NOPROD, + "EIZO", + NULL, + }, + { + USB_VENDOR_ELECOM, 0, + USB_KNOWNDEV_NOPROD, + "Elecom", + NULL, + }, + { + USB_VENDOR_CONEXANT, 0, + USB_KNOWNDEV_NOPROD, + "Conexant", + NULL, + }, + { + USB_VENDOR_HAUPPAUGE, 0, + USB_KNOWNDEV_NOPROD, + "Hauppauge Computer Works", + NULL, + }, + { + USB_VENDOR_BAFO, 0, + USB_KNOWNDEV_NOPROD, + "BAFO/Quality Computer Accessories", + NULL, + }, + { + USB_VENDOR_YEDATA, 0, + USB_KNOWNDEV_NOPROD, + "Y-E Data", + NULL, + }, + { + USB_VENDOR_AVM, 0, + USB_KNOWNDEV_NOPROD, + "AVM", + NULL, + }, + { + USB_VENDOR_QUICKSHOT, 0, + USB_KNOWNDEV_NOPROD, + "Quickshot", + NULL, + }, + { + USB_VENDOR_ROLAND, 0, + USB_KNOWNDEV_NOPROD, + "Roland", + NULL, + }, + { + USB_VENDOR_ROCKFIRE, 0, + USB_KNOWNDEV_NOPROD, + "Rockfire", + NULL, + }, + { + USB_VENDOR_RATOC, 0, + USB_KNOWNDEV_NOPROD, + "RATOC Systems", + NULL, + }, + { + USB_VENDOR_ZYXEL, 0, + USB_KNOWNDEV_NOPROD, + "ZyXEL Communication", + NULL, + }, + { + USB_VENDOR_INFINEON, 0, + USB_KNOWNDEV_NOPROD, + "Infineon", + NULL, + }, + { + USB_VENDOR_MICREL, 0, + USB_KNOWNDEV_NOPROD, + "Micrel", + NULL, + }, + { + USB_VENDOR_ALCOR, 0, + USB_KNOWNDEV_NOPROD, + "Alcor Micro", + NULL, + }, + { + USB_VENDOR_OMRON, 0, + USB_KNOWNDEV_NOPROD, + "OMRON", + NULL, + }, + { + USB_VENDOR_ZORAN, 0, + USB_KNOWNDEV_NOPROD, + "Zoran Microelectronics", + NULL, + }, + { + USB_VENDOR_NIIGATA, 0, + USB_KNOWNDEV_NOPROD, + "Niigata", + NULL, + }, + { + USB_VENDOR_IOMEGA, 0, + USB_KNOWNDEV_NOPROD, + "Iomega", + NULL, + }, + { + USB_VENDOR_ATREND, 0, + USB_KNOWNDEV_NOPROD, + "A-Trend Technology", + NULL, + }, + { + USB_VENDOR_AID, 0, + USB_KNOWNDEV_NOPROD, + "Advanced Input Devices", + NULL, + }, + { + USB_VENDOR_LACIE, 0, + USB_KNOWNDEV_NOPROD, + "LaCie", + NULL, + }, + { + USB_VENDOR_FUJIFILM, 0, + USB_KNOWNDEV_NOPROD, + "Fuji Film", + NULL, + }, + { + USB_VENDOR_ARC, 0, + USB_KNOWNDEV_NOPROD, + "ARC", + NULL, + }, + { + USB_VENDOR_ORTEK, 0, + USB_KNOWNDEV_NOPROD, + "Ortek", + NULL, + }, + { + USB_VENDOR_BOSE, 0, + USB_KNOWNDEV_NOPROD, + "Bose", + NULL, + }, + { + USB_VENDOR_OMNIVISION, 0, + USB_KNOWNDEV_NOPROD, + "OmniVision", + NULL, + }, + { + USB_VENDOR_INSYSTEM, 0, + USB_KNOWNDEV_NOPROD, + "In-System Design", + NULL, + }, + { + USB_VENDOR_APPLE, 0, + USB_KNOWNDEV_NOPROD, + "Apple Computer", + NULL, + }, + { + USB_VENDOR_YCCABLE, 0, + USB_KNOWNDEV_NOPROD, + "Y.C. Cable", + NULL, + }, + { + USB_VENDOR_DIGITALPERSONA, 0, + USB_KNOWNDEV_NOPROD, + "DigitalPersona", + NULL, + }, + { + USB_VENDOR_3G, 0, + USB_KNOWNDEV_NOPROD, + "3G Green Green Globe", + NULL, + }, + { + USB_VENDOR_RAFI, 0, + USB_KNOWNDEV_NOPROD, + "RAFI", + NULL, + }, + { + USB_VENDOR_TYCO, 0, + USB_KNOWNDEV_NOPROD, + "Tyco", + NULL, + }, + { + USB_VENDOR_KAWASAKI, 0, + USB_KNOWNDEV_NOPROD, + "Kawasaki", + NULL, + }, + { + USB_VENDOR_DIGI, 0, + USB_KNOWNDEV_NOPROD, + "Digi International", + NULL, + }, + { + USB_VENDOR_QUALCOMM2, 0, + USB_KNOWNDEV_NOPROD, + "Qualcomm", + NULL, + }, + { + USB_VENDOR_QTRONIX, 0, + USB_KNOWNDEV_NOPROD, + "Qtronix", + NULL, + }, + { + USB_VENDOR_FOXLINK, 0, + USB_KNOWNDEV_NOPROD, + "Foxlink", + NULL, + }, + { + USB_VENDOR_RICOH, 0, + USB_KNOWNDEV_NOPROD, + "Ricoh", + NULL, + }, + { + USB_VENDOR_ELSA, 0, + USB_KNOWNDEV_NOPROD, + "ELSA", + NULL, + }, + { + USB_VENDOR_SCIWORX, 0, + USB_KNOWNDEV_NOPROD, + "sci-worx", + NULL, + }, + { + USB_VENDOR_BRAINBOXES, 0, + USB_KNOWNDEV_NOPROD, + "Brainboxes Limited", + NULL, + }, + { + USB_VENDOR_ULTIMA, 0, + USB_KNOWNDEV_NOPROD, + "Ultima", + NULL, + }, + { + USB_VENDOR_AXIOHM, 0, + USB_KNOWNDEV_NOPROD, + "Axiohm Transaction Solutions", + NULL, + }, + { + USB_VENDOR_MICROTEK, 0, + USB_KNOWNDEV_NOPROD, + "Microtek", + NULL, + }, + { + USB_VENDOR_SUNTAC, 0, + USB_KNOWNDEV_NOPROD, + "SUN Corporation", + NULL, + }, + { + USB_VENDOR_LEXAR, 0, + USB_KNOWNDEV_NOPROD, + "Lexar Media", + NULL, + }, + { + USB_VENDOR_ADDTRON, 0, + USB_KNOWNDEV_NOPROD, + "Addtron", + NULL, + }, + { + USB_VENDOR_SYMBOL, 0, + USB_KNOWNDEV_NOPROD, + "Symbol Technologies", + NULL, + }, + { + USB_VENDOR_SYNTEK, 0, + USB_KNOWNDEV_NOPROD, + "Syntek", + NULL, + }, + { + USB_VENDOR_GENESYS, 0, + USB_KNOWNDEV_NOPROD, + "Genesys Logic", + NULL, + }, + { + USB_VENDOR_FUJI, 0, + USB_KNOWNDEV_NOPROD, + "Fuji Electric", + NULL, + }, + { + USB_VENDOR_KEITHLEY, 0, + USB_KNOWNDEV_NOPROD, + "Keithley Instruments", + NULL, + }, + { + USB_VENDOR_EIZONANAO, 0, + USB_KNOWNDEV_NOPROD, + "EIZO Nanao", + NULL, + }, + { + USB_VENDOR_KLSI, 0, + USB_KNOWNDEV_NOPROD, + "Kawasaki LSI", + NULL, + }, + { + USB_VENDOR_FFC, 0, + USB_KNOWNDEV_NOPROD, + "FFC", + NULL, + }, + { + USB_VENDOR_ANKO, 0, + USB_KNOWNDEV_NOPROD, + "Anko Electronic", + NULL, + }, + { + USB_VENDOR_PIENGINEERING, 0, + USB_KNOWNDEV_NOPROD, + "P.I. Engineering", + NULL, + }, + { + USB_VENDOR_AOC, 0, + USB_KNOWNDEV_NOPROD, + "AOC International", + NULL, + }, + { + USB_VENDOR_CHIC, 0, + USB_KNOWNDEV_NOPROD, + "Chic Technology", + NULL, + }, + { + USB_VENDOR_BARCO, 0, + USB_KNOWNDEV_NOPROD, + "Barco Display Systems", + NULL, + }, + { + USB_VENDOR_BRIDGE, 0, + USB_KNOWNDEV_NOPROD, + "Bridge Information", + NULL, + }, + { + USB_VENDOR_SOLIDYEAR, 0, + USB_KNOWNDEV_NOPROD, + "Solid Year", + NULL, + }, + { + USB_VENDOR_BIORAD, 0, + USB_KNOWNDEV_NOPROD, + "Bio-Rad Laboratories", + NULL, + }, + { + USB_VENDOR_MACALLY, 0, + USB_KNOWNDEV_NOPROD, + "Macally", + NULL, + }, + { + USB_VENDOR_ACTLABS, 0, + USB_KNOWNDEV_NOPROD, + "Act Labs", + NULL, + }, + { + USB_VENDOR_ALARIS, 0, + USB_KNOWNDEV_NOPROD, + "Alaris", + NULL, + }, + { + USB_VENDOR_APEX, 0, + USB_KNOWNDEV_NOPROD, + "Apex", + NULL, + }, + { + USB_VENDOR_CREATIVE3, 0, + USB_KNOWNDEV_NOPROD, + "Creative Labs", + NULL, + }, + { + USB_VENDOR_VIVITAR, 0, + USB_KNOWNDEV_NOPROD, + "Vivitar", + NULL, + }, + { + USB_VENDOR_GUNZE, 0, + USB_KNOWNDEV_NOPROD, + "Gunze Electronics USA", + NULL, + }, + { + USB_VENDOR_AVISION, 0, + USB_KNOWNDEV_NOPROD, + "Avision", + NULL, + }, + { + USB_VENDOR_TEAC, 0, + USB_KNOWNDEV_NOPROD, + "TEAC", + NULL, + }, + { + USB_VENDOR_SGI, 0, + USB_KNOWNDEV_NOPROD, + "Silicon Graphics", + NULL, + }, + { + USB_VENDOR_SANWASUPPLY, 0, + USB_KNOWNDEV_NOPROD, + "Sanwa Supply", + NULL, + }, + { + USB_VENDOR_LINKSYS, 0, + USB_KNOWNDEV_NOPROD, + "Linksys", + NULL, + }, + { + USB_VENDOR_ACERSA, 0, + USB_KNOWNDEV_NOPROD, + "Acer Semiconductor America", + NULL, + }, + { + USB_VENDOR_SIGMATEL, 0, + USB_KNOWNDEV_NOPROD, + "Sigmatel", + NULL, + }, + { + USB_VENDOR_DRAYTEK, 0, + USB_KNOWNDEV_NOPROD, + "DrayTek", + NULL, + }, + { + USB_VENDOR_AIWA, 0, + USB_KNOWNDEV_NOPROD, + "Aiwa", + NULL, + }, + { + USB_VENDOR_ACARD, 0, + USB_KNOWNDEV_NOPROD, + "ACARD Technology", + NULL, + }, + { + USB_VENDOR_PROLIFIC, 0, + USB_KNOWNDEV_NOPROD, + "Prolific Technology", + NULL, + }, + { + USB_VENDOR_SIEMENS, 0, + USB_KNOWNDEV_NOPROD, + "Siemens", + NULL, + }, + { + USB_VENDOR_AVANCELOGIC, 0, + USB_KNOWNDEV_NOPROD, + "Avance Logic", + NULL, + }, + { + USB_VENDOR_SIEMENS2, 0, + USB_KNOWNDEV_NOPROD, + "Siemens", + NULL, + }, + { + USB_VENDOR_MINOLTA, 0, + USB_KNOWNDEV_NOPROD, + "Minolta", + NULL, + }, + { + USB_VENDOR_CHPRODUCTS, 0, + USB_KNOWNDEV_NOPROD, + "CH Products", + NULL, + }, + { + USB_VENDOR_HAGIWARA, 0, + USB_KNOWNDEV_NOPROD, + "Hagiwara Sys-Com", + NULL, + }, + { + USB_VENDOR_CTX, 0, + USB_KNOWNDEV_NOPROD, + "Chuntex", + NULL, + }, + { + USB_VENDOR_ASKEY, 0, + USB_KNOWNDEV_NOPROD, + "Askey Computer", + NULL, + }, + { + USB_VENDOR_SAITEK, 0, + USB_KNOWNDEV_NOPROD, + "Saitek", + NULL, + }, + { + USB_VENDOR_ALCATELT, 0, + USB_KNOWNDEV_NOPROD, + "Alcatel Telecom", + NULL, + }, + { + USB_VENDOR_AGFA, 0, + USB_KNOWNDEV_NOPROD, + "AGFA-Gevaert", + NULL, + }, + { + USB_VENDOR_ASIAMD, 0, + USB_KNOWNDEV_NOPROD, + "Asia Microelectronic Development", + NULL, + }, + { + USB_VENDOR_BIZLINK, 0, + USB_KNOWNDEV_NOPROD, + "Bizlink International", + NULL, + }, + { + USB_VENDOR_KEYSPAN, 0, + USB_KNOWNDEV_NOPROD, + "Keyspan / InnoSys Inc.", + NULL, + }, + { + USB_VENDOR_AASHIMA, 0, + USB_KNOWNDEV_NOPROD, + "Aashima Technology", + NULL, + }, + { + USB_VENDOR_MULTITECH, 0, + USB_KNOWNDEV_NOPROD, + "MultiTech", + NULL, + }, + { + USB_VENDOR_ADS, 0, + USB_KNOWNDEV_NOPROD, + "ADS Technologies", + NULL, + }, + { + USB_VENDOR_ALCATELM, 0, + USB_KNOWNDEV_NOPROD, + "Alcatel Microelectronics", + NULL, + }, + { + USB_VENDOR_SIRIUS, 0, + USB_KNOWNDEV_NOPROD, + "Sirius Technologies", + NULL, + }, + { + USB_VENDOR_GUILLEMOT, 0, + USB_KNOWNDEV_NOPROD, + "Guillemot", + NULL, + }, + { + USB_VENDOR_BOSTON, 0, + USB_KNOWNDEV_NOPROD, + "Boston Acoustics", + NULL, + }, + { + USB_VENDOR_SMC, 0, + USB_KNOWNDEV_NOPROD, + "Standard Microsystems", + NULL, + }, + { + USB_VENDOR_PUTERCOM, 0, + USB_KNOWNDEV_NOPROD, + "Putercom", + NULL, + }, + { + USB_VENDOR_MCT, 0, + USB_KNOWNDEV_NOPROD, + "MCT", + NULL, + }, + { + USB_VENDOR_IMATION, 0, + USB_KNOWNDEV_NOPROD, + "Imation", + NULL, + }, + { + USB_VENDOR_SONYERICSSON, 0, + USB_KNOWNDEV_NOPROD, + "Sony Ericsson", + NULL, + }, + { + USB_VENDOR_EICON, 0, + USB_KNOWNDEV_NOPROD, + "Eicon Networks", + NULL, + }, + { + USB_VENDOR_SYNTECH, 0, + USB_KNOWNDEV_NOPROD, + "Syntech Information", + NULL, + }, + { + USB_VENDOR_DIGITALSTREAM, 0, + USB_KNOWNDEV_NOPROD, + "Digital Stream", + NULL, + }, + { + USB_VENDOR_AUREAL, 0, + USB_KNOWNDEV_NOPROD, + "Aureal Semiconductor", + NULL, + }, + { + USB_VENDOR_MIDIMAN, 0, + USB_KNOWNDEV_NOPROD, + "Midiman", + NULL, + }, + { + USB_VENDOR_CYBERPOWER, 0, + USB_KNOWNDEV_NOPROD, + "Cyber Power Systems, Inc.", + NULL, + }, + { + USB_VENDOR_SURECOM, 0, + USB_KNOWNDEV_NOPROD, + "Surecom Technology", + NULL, + }, + { + USB_VENDOR_LINKSYS2, 0, + USB_KNOWNDEV_NOPROD, + "Linksys", + NULL, + }, + { + USB_VENDOR_GRIFFIN, 0, + USB_KNOWNDEV_NOPROD, + "Griffin Technology", + NULL, + }, + { + USB_VENDOR_SANDISK, 0, + USB_KNOWNDEV_NOPROD, + "SanDisk", + NULL, + }, + { + USB_VENDOR_JENOPTIK, 0, + USB_KNOWNDEV_NOPROD, + "Jenoptik", + NULL, + }, + { + USB_VENDOR_LOGITEC, 0, + USB_KNOWNDEV_NOPROD, + "Logitec", + NULL, + }, + { + USB_VENDOR_BRIMAX, 0, + USB_KNOWNDEV_NOPROD, + "Brimax", + NULL, + }, + { + USB_VENDOR_AXIS, 0, + USB_KNOWNDEV_NOPROD, + "Axis Communications", + NULL, + }, + { + USB_VENDOR_ABL, 0, + USB_KNOWNDEV_NOPROD, + "ABL Electronics", + NULL, + }, + { + USB_VENDOR_SAGEM, 0, + USB_KNOWNDEV_NOPROD, + "Sagem", + NULL, + }, + { + USB_VENDOR_SUNCOMM, 0, + USB_KNOWNDEV_NOPROD, + "Sun Communications, Inc.", + NULL, + }, + { + USB_VENDOR_ALFADATA, 0, + USB_KNOWNDEV_NOPROD, + "Alfadata Computer", + NULL, + }, + { + USB_VENDOR_NATIONALTECH, 0, + USB_KNOWNDEV_NOPROD, + "National Technical Systems", + NULL, + }, + { + USB_VENDOR_ONNTO, 0, + USB_KNOWNDEV_NOPROD, + "Onnto", + NULL, + }, + { + USB_VENDOR_BE, 0, + USB_KNOWNDEV_NOPROD, + "Be", + NULL, + }, + { + USB_VENDOR_ADMTEK, 0, + USB_KNOWNDEV_NOPROD, + "ADMtek", + NULL, + }, + { + USB_VENDOR_COREGA, 0, + USB_KNOWNDEV_NOPROD, + "Corega", + NULL, + }, + { + USB_VENDOR_FREECOM, 0, + USB_KNOWNDEV_NOPROD, + "Freecom", + NULL, + }, + { + USB_VENDOR_MICROTECH, 0, + USB_KNOWNDEV_NOPROD, + "Microtech", + NULL, + }, + { + USB_VENDOR_GENERALINSTMNTS, 0, + USB_KNOWNDEV_NOPROD, + "General Instruments (Motorola)", + NULL, + }, + { + USB_VENDOR_OLYMPUS, 0, + USB_KNOWNDEV_NOPROD, + "Olympus", + NULL, + }, + { + USB_VENDOR_ABOCOM, 0, + USB_KNOWNDEV_NOPROD, + "AboCom Systems", + NULL, + }, + { + USB_VENDOR_KEISOKUGIKEN, 0, + USB_KNOWNDEV_NOPROD, + "Keisokugiken", + NULL, + }, + { + USB_VENDOR_ONSPEC, 0, + USB_KNOWNDEV_NOPROD, + "OnSpec", + NULL, + }, + { + USB_VENDOR_APG, 0, + USB_KNOWNDEV_NOPROD, + "APG Cash Drawer", + NULL, + }, + { + USB_VENDOR_BUG, 0, + USB_KNOWNDEV_NOPROD, + "B.U.G.", + NULL, + }, + { + USB_VENDOR_ALLIEDTELESYN, 0, + USB_KNOWNDEV_NOPROD, + "Allied Telesyn International", + NULL, + }, + { + USB_VENDOR_AVERMEDIA, 0, + USB_KNOWNDEV_NOPROD, + "AVerMedia Technologies", + NULL, + }, + { + USB_VENDOR_SIIG, 0, + USB_KNOWNDEV_NOPROD, + "SIIG", + NULL, + }, + { + USB_VENDOR_CASIO, 0, + USB_KNOWNDEV_NOPROD, + "CASIO", + NULL, + }, + { + USB_VENDOR_DLINK2, 0, + USB_KNOWNDEV_NOPROD, + "D-Link", + NULL, + }, + { + USB_VENDOR_APTIO, 0, + USB_KNOWNDEV_NOPROD, + "Aptio Products", + NULL, + }, + { + USB_VENDOR_ARASAN, 0, + USB_KNOWNDEV_NOPROD, + "Arasan Chip Systems", + NULL, + }, + { + USB_VENDOR_ALLIEDCABLE, 0, + USB_KNOWNDEV_NOPROD, + "Allied Cable", + NULL, + }, + { + USB_VENDOR_STSN, 0, + USB_KNOWNDEV_NOPROD, + "STSN", + NULL, + }, + { + USB_VENDOR_CENTURY, 0, + USB_KNOWNDEV_NOPROD, + "Century Corp", + NULL, + }, + { + USB_VENDOR_ZOOM, 0, + USB_KNOWNDEV_NOPROD, + "Zoom Telephonics", + NULL, + }, + { + USB_VENDOR_PCS, 0, + USB_KNOWNDEV_NOPROD, + "Personal Communication Systems", + NULL, + }, + { + USB_VENDOR_BROADLOGIC, 0, + USB_KNOWNDEV_NOPROD, + "BroadLogic", + NULL, + }, + { + USB_VENDOR_HANDSPRING, 0, + USB_KNOWNDEV_NOPROD, + "Handspring", + NULL, + }, + { + USB_VENDOR_PALM, 0, + USB_KNOWNDEV_NOPROD, + "Palm Computing", + NULL, + }, + { + USB_VENDOR_SOURCENEXT, 0, + USB_KNOWNDEV_NOPROD, + "SOURCENEXT", + NULL, + }, + { + USB_VENDOR_ACTIONSTAR, 0, + USB_KNOWNDEV_NOPROD, + "Action Star Enterprise", + NULL, + }, + { + USB_VENDOR_SAMSUNG_TECHWIN, 0, + USB_KNOWNDEV_NOPROD, + "Samsung Techwin", + NULL, + }, + { + USB_VENDOR_ACCTON, 0, + USB_KNOWNDEV_NOPROD, + "Accton Technology", + NULL, + }, + { + USB_VENDOR_DIAMOND, 0, + USB_KNOWNDEV_NOPROD, + "Diamond", + NULL, + }, + { + USB_VENDOR_NETGEAR, 0, + USB_KNOWNDEV_NOPROD, + "BayNETGEAR", + NULL, + }, + { + USB_VENDOR_TOPRE, 0, + USB_KNOWNDEV_NOPROD, + "Topre Corporation", + NULL, + }, + { + USB_VENDOR_ACTIVEWIRE, 0, + USB_KNOWNDEV_NOPROD, + "ActiveWire", + NULL, + }, + { + USB_VENDOR_BBELECTRONICS, 0, + USB_KNOWNDEV_NOPROD, + "B&B Electronics", + NULL, + }, + { + USB_VENDOR_PORTGEAR, 0, + USB_KNOWNDEV_NOPROD, + "PortGear", + NULL, + }, + { + USB_VENDOR_NETGEAR2, 0, + USB_KNOWNDEV_NOPROD, + "Netgear", + NULL, + }, + { + USB_VENDOR_SYSTEMTALKS, 0, + USB_KNOWNDEV_NOPROD, + "System Talks", + NULL, + }, + { + USB_VENDOR_METRICOM, 0, + USB_KNOWNDEV_NOPROD, + "Metricom", + NULL, + }, + { + USB_VENDOR_ADESSOKBTEK, 0, + USB_KNOWNDEV_NOPROD, + "ADESSO/Kbtek America", + NULL, + }, + { + USB_VENDOR_JATON, 0, + USB_KNOWNDEV_NOPROD, + "Jaton", + NULL, + }, + { + USB_VENDOR_APT, 0, + USB_KNOWNDEV_NOPROD, + "APT Technologies", + NULL, + }, + { + USB_VENDOR_BOCARESEARCH, 0, + USB_KNOWNDEV_NOPROD, + "Boca Research", + NULL, + }, + { + USB_VENDOR_ANDREA, 0, + USB_KNOWNDEV_NOPROD, + "Andrea Electronics", + NULL, + }, + { + USB_VENDOR_BURRBROWN, 0, + USB_KNOWNDEV_NOPROD, + "Burr-Brown Japan", + NULL, + }, + { + USB_VENDOR_2WIRE, 0, + USB_KNOWNDEV_NOPROD, + "2Wire", + NULL, + }, + { + USB_VENDOR_AIPTEK, 0, + USB_KNOWNDEV_NOPROD, + "AIPTEK International", + NULL, + }, + { + USB_VENDOR_SMARTBRIDGES, 0, + USB_KNOWNDEV_NOPROD, + "SmartBridges", + NULL, + }, + { + USB_VENDOR_BILLIONTON, 0, + USB_KNOWNDEV_NOPROD, + "Billionton Systems", + NULL, + }, + { + USB_VENDOR_EXTENDED, 0, + USB_KNOWNDEV_NOPROD, + "Extended Systems", + NULL, + }, + { + USB_VENDOR_MSYSTEMS, 0, + USB_KNOWNDEV_NOPROD, + "M-Systems", + NULL, + }, + { + USB_VENDOR_AUTHENTEC, 0, + USB_KNOWNDEV_NOPROD, + "AuthenTec", + NULL, + }, + { + USB_VENDOR_AUDIOTECHNICA, 0, + USB_KNOWNDEV_NOPROD, + "Audio-Technica", + NULL, + }, + { + USB_VENDOR_TRUMPION, 0, + USB_KNOWNDEV_NOPROD, + "Trumpion Microelectronics", + NULL, + }, + { + USB_VENDOR_FEIYA, 0, + USB_KNOWNDEV_NOPROD, + "Feiya", + NULL, + }, + { + USB_VENDOR_ALATION, 0, + USB_KNOWNDEV_NOPROD, + "Alation Systems", + NULL, + }, + { + USB_VENDOR_GLOBESPAN, 0, + USB_KNOWNDEV_NOPROD, + "Globespan", + NULL, + }, + { + USB_VENDOR_CONCORDCAMERA, 0, + USB_KNOWNDEV_NOPROD, + "Concord Camera", + NULL, + }, + { + USB_VENDOR_GARMIN, 0, + USB_KNOWNDEV_NOPROD, + "Garmin International", + NULL, + }, + { + USB_VENDOR_GOHUBS, 0, + USB_KNOWNDEV_NOPROD, + "GoHubs", + NULL, + }, + { + USB_VENDOR_XEROX, 0, + USB_KNOWNDEV_NOPROD, + "Xerox", + NULL, + }, + { + USB_VENDOR_BIOMETRIC, 0, + USB_KNOWNDEV_NOPROD, + "American Biometric Company", + NULL, + }, + { + USB_VENDOR_TOSHIBA, 0, + USB_KNOWNDEV_NOPROD, + "Toshiba", + NULL, + }, + { + USB_VENDOR_PLEXTOR, 0, + USB_KNOWNDEV_NOPROD, + "Plextor", + NULL, + }, + { + USB_VENDOR_INTREPIDCS, 0, + USB_KNOWNDEV_NOPROD, + "Intrepid", + NULL, + }, + { + USB_VENDOR_YANO, 0, + USB_KNOWNDEV_NOPROD, + "Yano", + NULL, + }, + { + USB_VENDOR_KINGSTON, 0, + USB_KNOWNDEV_NOPROD, + "Kingston Technology", + NULL, + }, + { + USB_VENDOR_BLUEWATER, 0, + USB_KNOWNDEV_NOPROD, + "BlueWater Systems", + NULL, + }, + { + USB_VENDOR_AGILENT, 0, + USB_KNOWNDEV_NOPROD, + "Agilent Technologies", + NULL, + }, + { + USB_VENDOR_GUDE, 0, + USB_KNOWNDEV_NOPROD, + "Gude ADS", + NULL, + }, + { + USB_VENDOR_PORTSMITH, 0, + USB_KNOWNDEV_NOPROD, + "Portsmith", + NULL, + }, + { + USB_VENDOR_ACERW, 0, + USB_KNOWNDEV_NOPROD, + "Acer", + NULL, + }, + { + USB_VENDOR_ADIRONDACK, 0, + USB_KNOWNDEV_NOPROD, + "Adirondack Wire & Cable", + NULL, + }, + { + USB_VENDOR_BECKHOFF, 0, + USB_KNOWNDEV_NOPROD, + "Beckhoff", + NULL, + }, + { + USB_VENDOR_MINDSATWORK, 0, + USB_KNOWNDEV_NOPROD, + "Minds At Work", + NULL, + }, + { + USB_VENDOR_POINTCHIPS, 0, + USB_KNOWNDEV_NOPROD, + "PointChips", + NULL, + }, + { + USB_VENDOR_INTERSIL, 0, + USB_KNOWNDEV_NOPROD, + "Intersil", + NULL, + }, + { + USB_VENDOR_ALTIUS, 0, + USB_KNOWNDEV_NOPROD, + "Altius Solutions", + NULL, + }, + { + USB_VENDOR_ARRIS, 0, + USB_KNOWNDEV_NOPROD, + "Arris Interactive", + NULL, + }, + { + USB_VENDOR_ACTIVCARD, 0, + USB_KNOWNDEV_NOPROD, + "ACTIVCARD", + NULL, + }, + { + USB_VENDOR_ACTISYS, 0, + USB_KNOWNDEV_NOPROD, + "ACTiSYS", + NULL, + }, + { + USB_VENDOR_NOVATEL2, 0, + USB_KNOWNDEV_NOPROD, + "Novatel Wireless", + NULL, + }, + { + USB_VENDOR_AFOURTECH, 0, + USB_KNOWNDEV_NOPROD, + "A-FOUR TECH", + NULL, + }, + { + USB_VENDOR_AIMEX, 0, + USB_KNOWNDEV_NOPROD, + "AIMEX", + NULL, + }, + { + USB_VENDOR_ADDONICS, 0, + USB_KNOWNDEV_NOPROD, + "Addonics Technologies", + NULL, + }, + { + USB_VENDOR_AKAI, 0, + USB_KNOWNDEV_NOPROD, + "AKAI professional M.I.", + NULL, + }, + { + USB_VENDOR_ARESCOM, 0, + USB_KNOWNDEV_NOPROD, + "ARESCOM", + NULL, + }, + { + USB_VENDOR_BAY, 0, + USB_KNOWNDEV_NOPROD, + "Bay Associates", + NULL, + }, + { + USB_VENDOR_ALTERA, 0, + USB_KNOWNDEV_NOPROD, + "Altera", + NULL, + }, + { + USB_VENDOR_CSR, 0, + USB_KNOWNDEV_NOPROD, + "Cambridge Silicon Radio", + NULL, + }, + { + USB_VENDOR_TREK, 0, + USB_KNOWNDEV_NOPROD, + "Trek Technology", + NULL, + }, + { + USB_VENDOR_ASAHIOPTICAL, 0, + USB_KNOWNDEV_NOPROD, + "Asahi Optical", + NULL, + }, + { + USB_VENDOR_BOCASYSTEMS, 0, + USB_KNOWNDEV_NOPROD, + "Boca Systems", + NULL, + }, + { + USB_VENDOR_SHANTOU, 0, + USB_KNOWNDEV_NOPROD, + "ShanTou", + NULL, + }, + { + USB_VENDOR_MEDIAGEAR, 0, + USB_KNOWNDEV_NOPROD, + "MediaGear", + NULL, + }, + { + USB_VENDOR_BROADCOM, 0, + USB_KNOWNDEV_NOPROD, + "Broadcom", + NULL, + }, + { + USB_VENDOR_GREENHOUSE, 0, + USB_KNOWNDEV_NOPROD, + "GREENHOUSE", + NULL, + }, + { + USB_VENDOR_GEOCAST, 0, + USB_KNOWNDEV_NOPROD, + "Geocast Network Systems", + NULL, + }, + { + USB_VENDOR_IDQUANTIQUE, 0, + USB_KNOWNDEV_NOPROD, + "id Quantique", + NULL, + }, + { + USB_VENDOR_ZYDAS, 0, + USB_KNOWNDEV_NOPROD, + "Zydas Technology Corporation", + NULL, + }, + { + USB_VENDOR_NEODIO, 0, + USB_KNOWNDEV_NOPROD, + "Neodio", + NULL, + }, + { + USB_VENDOR_OPTION, 0, + USB_KNOWNDEV_NOPROD, + "Option N.V:", + NULL, + }, + { + USB_VENDOR_ASUS, 0, + USB_KNOWNDEV_NOPROD, + "ASUSTeK Computer", + NULL, + }, + { + USB_VENDOR_TODOS, 0, + USB_KNOWNDEV_NOPROD, + "Todos Data System", + NULL, + }, + { + USB_VENDOR_SIIG2, 0, + USB_KNOWNDEV_NOPROD, + "SIIG", + NULL, + }, + { + USB_VENDOR_TEKRAM, 0, + USB_KNOWNDEV_NOPROD, + "Tekram Technology", + NULL, + }, + { + USB_VENDOR_HAL, 0, + USB_KNOWNDEV_NOPROD, + "HAL Corporation", + NULL, + }, + { + USB_VENDOR_EMS, 0, + USB_KNOWNDEV_NOPROD, + "EMS Production", + NULL, + }, + { + USB_VENDOR_NEC2, 0, + USB_KNOWNDEV_NOPROD, + "NEC", + NULL, + }, + { + USB_VENDOR_ATI2, 0, + USB_KNOWNDEV_NOPROD, + "ATI", + NULL, + }, + { + USB_VENDOR_ZEEVO, 0, + USB_KNOWNDEV_NOPROD, + "Zeevo, Inc.", + NULL, + }, + { + USB_VENDOR_KURUSUGAWA, 0, + USB_KNOWNDEV_NOPROD, + "Kurusugawa Electronics, Inc.", + NULL, + }, + { + USB_VENDOR_ASIX, 0, + USB_KNOWNDEV_NOPROD, + "ASIX Electronics", + NULL, + }, + { + USB_VENDOR_O2MICRO, 0, + USB_KNOWNDEV_NOPROD, + "O2 Micro, Inc.", + NULL, + }, + { + USB_VENDOR_USR, 0, + USB_KNOWNDEV_NOPROD, + "U.S. Robotics", + NULL, + }, + { + USB_VENDOR_AMBIT, 0, + USB_KNOWNDEV_NOPROD, + "Ambit Microsystems", + NULL, + }, + { + USB_VENDOR_HTC, 0, + USB_KNOWNDEV_NOPROD, + "HTC", + NULL, + }, + { + USB_VENDOR_REALTEK, 0, + USB_KNOWNDEV_NOPROD, + "Realtek", + NULL, + }, + { + USB_VENDOR_ADDONICS2, 0, + USB_KNOWNDEV_NOPROD, + "Addonics Technology", + NULL, + }, + { + USB_VENDOR_FSC, 0, + USB_KNOWNDEV_NOPROD, + "Fujitsu Siemens Computers", + NULL, + }, + { + USB_VENDOR_AGATE, 0, + USB_KNOWNDEV_NOPROD, + "Agate Technologies", + NULL, + }, + { + USB_VENDOR_DMI, 0, + USB_KNOWNDEV_NOPROD, + "DMI", + NULL, + }, + { + USB_VENDOR_MICRODIA, 0, + USB_KNOWNDEV_NOPROD, + "Chicony", + NULL, + }, + { + USB_VENDOR_SEALEVEL, 0, + USB_KNOWNDEV_NOPROD, + "Sealevel System", + NULL, + }, + { + USB_VENDOR_LUWEN, 0, + USB_KNOWNDEV_NOPROD, + "Luwen", + NULL, + }, + { + USB_VENDOR_KYOCERA2, 0, + USB_KNOWNDEV_NOPROD, + "Kyocera Wireless Corp.", + NULL, + }, + { + USB_VENDOR_ZCOM, 0, + USB_KNOWNDEV_NOPROD, + "Z-Com", + NULL, + }, + { + USB_VENDOR_ATHEROS2, 0, + USB_KNOWNDEV_NOPROD, + "Atheros Communications", + NULL, + }, + { + USB_VENDOR_TANGTOP, 0, + USB_KNOWNDEV_NOPROD, + "Tangtop", + NULL, + }, + { + USB_VENDOR_SMC3, 0, + USB_KNOWNDEV_NOPROD, + "Standard Microsystems", + NULL, + }, + { + USB_VENDOR_ADDON, 0, + USB_KNOWNDEV_NOPROD, + "Add-on Technology", + NULL, + }, + { + USB_VENDOR_ACDC, 0, + USB_KNOWNDEV_NOPROD, + "American Computer & Digital Components", + NULL, + }, + { + USB_VENDOR_ABC, 0, + USB_KNOWNDEV_NOPROD, + "ABC", + NULL, + }, + { + USB_VENDOR_CONCEPTRONIC, 0, + USB_KNOWNDEV_NOPROD, + "Conceptronic", + NULL, + }, + { + USB_VENDOR_SKANHEX, 0, + USB_KNOWNDEV_NOPROD, + "Skanhex Technology, Inc.", + NULL, + }, + { + USB_VENDOR_MSI, 0, + USB_KNOWNDEV_NOPROD, + "Micro Star International", + NULL, + }, + { + USB_VENDOR_ELCON, 0, + USB_KNOWNDEV_NOPROD, + "ELCON Systemtechnik", + NULL, + }, + { + USB_VENDOR_NETAC, 0, + USB_KNOWNDEV_NOPROD, + "Netac", + NULL, + }, + { + USB_VENDOR_SITECOMEU, 0, + USB_KNOWNDEV_NOPROD, + "Sitecom Europe", + NULL, + }, + { + USB_VENDOR_MOBILEACTION, 0, + USB_KNOWNDEV_NOPROD, + "Mobile Action", + NULL, + }, + { + USB_VENDOR_SPEEDDRAGON, 0, + USB_KNOWNDEV_NOPROD, + "Speed Dragon Multimedia", + NULL, + }, + { + USB_VENDOR_HAWKING, 0, + USB_KNOWNDEV_NOPROD, + "Hawking", + NULL, + }, + { + USB_VENDOR_FOSSIL, 0, + USB_KNOWNDEV_NOPROD, + "Fossil, Inc", + NULL, + }, + { + USB_VENDOR_GMATE, 0, + USB_KNOWNDEV_NOPROD, + "G.Mate, Inc", + NULL, + }, + { + USB_VENDOR_OTI, 0, + USB_KNOWNDEV_NOPROD, + "Ours Technology", + NULL, + }, + { + USB_VENDOR_PILOTECH, 0, + USB_KNOWNDEV_NOPROD, + "Pilotech", + NULL, + }, + { + USB_VENDOR_NOVATECH, 0, + USB_KNOWNDEV_NOPROD, + "NovaTech", + NULL, + }, + { + USB_VENDOR_ITEGNO, 0, + USB_KNOWNDEV_NOPROD, + "iTegno", + NULL, + }, + { + USB_VENDOR_WINMAXGROUP, 0, + USB_KNOWNDEV_NOPROD, + "WinMaxGroup", + NULL, + }, + { + USB_VENDOR_TOD, 0, + USB_KNOWNDEV_NOPROD, + "TOD", + NULL, + }, + { + USB_VENDOR_EGALAX, 0, + USB_KNOWNDEV_NOPROD, + "eGalax, Inc.", + NULL, + }, + { + USB_VENDOR_AIRPRIME, 0, + USB_KNOWNDEV_NOPROD, + "AirPrime, Inc.", + NULL, + }, + { + USB_VENDOR_MICROTUNE, 0, + USB_KNOWNDEV_NOPROD, + "Microtune", + NULL, + }, + { + USB_VENDOR_VTECH, 0, + USB_KNOWNDEV_NOPROD, + "VTech", + NULL, + }, + { + USB_VENDOR_FALCOM, 0, + USB_KNOWNDEV_NOPROD, + "Falcom Wireless Communications GmbH", + NULL, + }, + { + USB_VENDOR_RIM, 0, + USB_KNOWNDEV_NOPROD, + "Research In Motion", + NULL, + }, + { + USB_VENDOR_DYNASTREAM, 0, + USB_KNOWNDEV_NOPROD, + "Dynastream Innovations", + NULL, + }, + { + USB_VENDOR_QUALCOMM, 0, + USB_KNOWNDEV_NOPROD, + "Qualcomm", + NULL, + }, + { + USB_VENDOR_DESKNOTE, 0, + USB_KNOWNDEV_NOPROD, + "Desknote", + NULL, + }, + { + USB_VENDOR_GIGABYTE, 0, + USB_KNOWNDEV_NOPROD, + "GIGABYTE", + NULL, + }, + { + USB_VENDOR_WESTERN, 0, + USB_KNOWNDEV_NOPROD, + "Western Digital", + NULL, + }, + { + USB_VENDOR_MOTOROLA, 0, + USB_KNOWNDEV_NOPROD, + "Motorola", + NULL, + }, + { + USB_VENDOR_CCYU, 0, + USB_KNOWNDEV_NOPROD, + "CCYU Technology", + NULL, + }, + { + USB_VENDOR_CURITEL, 0, + USB_KNOWNDEV_NOPROD, + "Curitel Communications Inc", + NULL, + }, + { + USB_VENDOR_SILABS2, 0, + USB_KNOWNDEV_NOPROD, + "SILABS2", + NULL, + }, + { + USB_VENDOR_USI, 0, + USB_KNOWNDEV_NOPROD, + "USI", + NULL, + }, + { + USB_VENDOR_PLX, 0, + USB_KNOWNDEV_NOPROD, + "PLX", + NULL, + }, + { + USB_VENDOR_ASANTE, 0, + USB_KNOWNDEV_NOPROD, + "Asante", + NULL, + }, + { + USB_VENDOR_SILABS, 0, + USB_KNOWNDEV_NOPROD, + "Silicon Labs", + NULL, + }, + { + USB_VENDOR_ANALOG, 0, + USB_KNOWNDEV_NOPROD, + "Analog Devices", + NULL, + }, + { + USB_VENDOR_TENX, 0, + USB_KNOWNDEV_NOPROD, + "Ten X Technology, Inc.", + NULL, + }, + { + USB_VENDOR_ISSC, 0, + USB_KNOWNDEV_NOPROD, + "Integrated System Solution Corp.", + NULL, + }, + { + USB_VENDOR_JRC, 0, + USB_KNOWNDEV_NOPROD, + "Japan Radio Company", + NULL, + }, + { + USB_VENDOR_SPHAIRON, 0, + USB_KNOWNDEV_NOPROD, + "Sphairon Access Systems GmbH", + NULL, + }, + { + USB_VENDOR_DELORME, 0, + USB_KNOWNDEV_NOPROD, + "DeLorme", + NULL, + }, + { + USB_VENDOR_SERVERWORKS, 0, + USB_KNOWNDEV_NOPROD, + "ServerWorks", + NULL, + }, + { + USB_VENDOR_ACERCM, 0, + USB_KNOWNDEV_NOPROD, + "Acer Communications & Multimedia", + NULL, + }, + { + USB_VENDOR_SIERRA, 0, + USB_KNOWNDEV_NOPROD, + "Sierra Wireless", + NULL, + }, + { + USB_VENDOR_TOPFIELD, 0, + USB_KNOWNDEV_NOPROD, + "Topfield Co., Ltd", + NULL, + }, + { + USB_VENDOR_SIEMENS3, 0, + USB_KNOWNDEV_NOPROD, + "Siemens", + NULL, + }, + { + USB_VENDOR_PROLIFIC2, 0, + USB_KNOWNDEV_NOPROD, + "Prolific", + NULL, + }, + { + USB_VENDOR_ALCATEL, 0, + USB_KNOWNDEV_NOPROD, + "Alcatel", + NULL, + }, + { + USB_VENDOR_UNKNOWN3, 0, + USB_KNOWNDEV_NOPROD, + "Unknown vendor", + NULL, + }, + { + USB_VENDOR_TSUNAMI, 0, + USB_KNOWNDEV_NOPROD, + "Tsunami", + NULL, + }, + { + USB_VENDOR_PHEENET, 0, + USB_KNOWNDEV_NOPROD, + "Pheenet", + NULL, + }, + { + USB_VENDOR_TARGUS, 0, + USB_KNOWNDEV_NOPROD, + "Targus", + NULL, + }, + { + USB_VENDOR_TWINMOS, 0, + USB_KNOWNDEV_NOPROD, + "TwinMOS", + NULL, + }, + { + USB_VENDOR_TENDA, 0, + USB_KNOWNDEV_NOPROD, + "Tenda", + NULL, + }, + { + USB_VENDOR_CREATIVE2, 0, + USB_KNOWNDEV_NOPROD, + "Creative Labs", + NULL, + }, + { + USB_VENDOR_BELKIN2, 0, + USB_KNOWNDEV_NOPROD, + "Belkin Components", + NULL, + }, + { + USB_VENDOR_CYBERTAN, 0, + USB_KNOWNDEV_NOPROD, + "CyberTAN Technology", + NULL, + }, + { + USB_VENDOR_HUAWEI, 0, + USB_KNOWNDEV_NOPROD, + "Huawei Technologies", + NULL, + }, + { + USB_VENDOR_ARANEUS, 0, + USB_KNOWNDEV_NOPROD, + "Araneus Information Systems", + NULL, + }, + { + USB_VENDOR_TAPWAVE, 0, + USB_KNOWNDEV_NOPROD, + "Tapwave", + NULL, + }, + { + USB_VENDOR_AINCOMM, 0, + USB_KNOWNDEV_NOPROD, + "Aincomm", + NULL, + }, + { + USB_VENDOR_MOBILITY, 0, + USB_KNOWNDEV_NOPROD, + "Mobility", + NULL, + }, + { + USB_VENDOR_DICKSMITH, 0, + USB_KNOWNDEV_NOPROD, + "Dick Smith Electronics", + NULL, + }, + { + USB_VENDOR_NETGEAR3, 0, + USB_KNOWNDEV_NOPROD, + "Netgear", + NULL, + }, + { + USB_VENDOR_BALTECH, 0, + USB_KNOWNDEV_NOPROD, + "Baltech", + NULL, + }, + { + USB_VENDOR_CISCOLINKSYS, 0, + USB_KNOWNDEV_NOPROD, + "Cisco-Linksys", + NULL, + }, + { + USB_VENDOR_SHARK, 0, + USB_KNOWNDEV_NOPROD, + "Shark", + NULL, + }, + { + USB_VENDOR_NOVATEL, 0, + USB_KNOWNDEV_NOPROD, + "Novatel Wireless", + NULL, + }, + { + USB_VENDOR_MERLIN, 0, + USB_KNOWNDEV_NOPROD, + "Merlin", + NULL, + }, + { + USB_VENDOR_WISTRONNEWEB, 0, + USB_KNOWNDEV_NOPROD, + "Wistron NeWeb", + NULL, + }, + { + USB_VENDOR_RADIOSHACK, 0, + USB_KNOWNDEV_NOPROD, + "Radio Shack", + NULL, + }, + { + USB_VENDOR_HUAWEI3COM, 0, + USB_KNOWNDEV_NOPROD, + "Huawei-3Com", + NULL, + }, + { + USB_VENDOR_SILICOM, 0, + USB_KNOWNDEV_NOPROD, + "Silicom", + NULL, + }, + { + USB_VENDOR_RALINK, 0, + USB_KNOWNDEV_NOPROD, + "Ralink Technology", + NULL, + }, + { + USB_VENDOR_IMAGINATION, 0, + USB_KNOWNDEV_NOPROD, + "Imagination Technologies", + NULL, + }, + { + USB_VENDOR_CONCEPTRONIC2, 0, + USB_KNOWNDEV_NOPROD, + "Conceptronic", + NULL, + }, + { + USB_VENDOR_PLANEX3, 0, + USB_KNOWNDEV_NOPROD, + "Planex Communications", + NULL, + }, + { + USB_VENDOR_SILICONPORTALS, 0, + USB_KNOWNDEV_NOPROD, + "Silicon Portals", + NULL, + }, + { + USB_VENDOR_UBIQUAM, 0, + USB_KNOWNDEV_NOPROD, + "UBIQUAM Co., Ltd.", + NULL, + }, + { + USB_VENDOR_UBLOX, 0, + USB_KNOWNDEV_NOPROD, + "U-blox", + NULL, + }, + { + USB_VENDOR_PNY, 0, + USB_KNOWNDEV_NOPROD, + "PNY", + NULL, + }, + { + USB_VENDOR_OQO, 0, + USB_KNOWNDEV_NOPROD, + "OQO", + NULL, + }, + { + USB_VENDOR_UMEDIA, 0, + USB_KNOWNDEV_NOPROD, + "U-MEDIA Communications", + NULL, + }, + { + USB_VENDOR_FIBERLINE, 0, + USB_KNOWNDEV_NOPROD, + "Fiberline", + NULL, + }, + { + USB_VENDOR_SPARKLAN, 0, + USB_KNOWNDEV_NOPROD, + "SparkLAN", + NULL, + }, + { + USB_VENDOR_SOHOWARE, 0, + USB_KNOWNDEV_NOPROD, + "SOHOware", + NULL, + }, + { + USB_VENDOR_UMAX, 0, + USB_KNOWNDEV_NOPROD, + "UMAX Data Systems", + NULL, + }, + { + USB_VENDOR_INSIDEOUT, 0, + USB_KNOWNDEV_NOPROD, + "Inside Out Networks", + NULL, + }, + { + USB_VENDOR_GOODWAY, 0, + USB_KNOWNDEV_NOPROD, + "Good Way Technology", + NULL, + }, + { + USB_VENDOR_ENTREGA, 0, + USB_KNOWNDEV_NOPROD, + "Entrega", + NULL, + }, + { + USB_VENDOR_ACTIONTEC, 0, + USB_KNOWNDEV_NOPROD, + "Actiontec Electronics", + NULL, + }, + { + USB_VENDOR_ATHEROS, 0, + USB_KNOWNDEV_NOPROD, + "Atheros Communications", + NULL, + }, + { + USB_VENDOR_GIGASET, 0, + USB_KNOWNDEV_NOPROD, + "Gigaset", + NULL, + }, + { + USB_VENDOR_GLOBALSUN, 0, + USB_KNOWNDEV_NOPROD, + "Global Sun Technology", + NULL, + }, + { + USB_VENDOR_ANYDATA, 0, + USB_KNOWNDEV_NOPROD, + "AnyDATA Corporation", + NULL, + }, + { + USB_VENDOR_JABLOTRON, 0, + USB_KNOWNDEV_NOPROD, + "Jablotron", + NULL, + }, + { + USB_VENDOR_CMOTECH, 0, + USB_KNOWNDEV_NOPROD, + "CMOTECH Co., Ltd.", + NULL, + }, + { + USB_VENDOR_AXESSTEL, 0, + USB_KNOWNDEV_NOPROD, + "Axesstel Co., Ltd.", + NULL, + }, + { + USB_VENDOR_LINKSYS4, 0, + USB_KNOWNDEV_NOPROD, + "Linksys", + NULL, + }, + { + USB_VENDOR_SENAO, 0, + USB_KNOWNDEV_NOPROD, + "Senao", + NULL, + }, + { + USB_VENDOR_METAGEEK, 0, + USB_KNOWNDEV_NOPROD, + "MetaGeek", + NULL, + }, + { + USB_VENDOR_AMIT, 0, + USB_KNOWNDEV_NOPROD, + "AMIT", + NULL, + }, + { + USB_VENDOR_QCOM, 0, + USB_KNOWNDEV_NOPROD, + "Qcom", + NULL, + }, + { + USB_VENDOR_LINKSYS3, 0, + USB_KNOWNDEV_NOPROD, + "Linksys", + NULL, + }, + { + USB_VENDOR_QUALCOMMINC, 0, + USB_KNOWNDEV_NOPROD, + "Qualcomm, Incorporated", + NULL, + }, + { + USB_VENDOR_DLINK, 0, + USB_KNOWNDEV_NOPROD, + "D-Link", + NULL, + }, + { + USB_VENDOR_PLANEX2, 0, + USB_KNOWNDEV_NOPROD, + "Planex Communications", + NULL, + }, + { + USB_VENDOR_ERICSSON, 0, + USB_KNOWNDEV_NOPROD, + "Ericsson", + NULL, + }, + { + USB_VENDOR_MOTOROLA2, 0, + USB_KNOWNDEV_NOPROD, + "Motorola", + NULL, + }, + { + USB_VENDOR_TRIPPLITE, 0, + USB_KNOWNDEV_NOPROD, + "Tripp-Lite", + NULL, + }, + { + USB_VENDOR_HIROSE, 0, + USB_KNOWNDEV_NOPROD, + "Hirose Electric", + NULL, + }, + { + USB_VENDOR_NHJ, 0, + USB_KNOWNDEV_NOPROD, + "NHJ", + NULL, + }, + { + USB_VENDOR_PLANEX, 0, + USB_KNOWNDEV_NOPROD, + "Planex Communications", + NULL, + }, + { + USB_VENDOR_VIDZMEDIA, 0, + USB_KNOWNDEV_NOPROD, + "VidzMedia Pte Ltd", + NULL, + }, + { + USB_VENDOR_AEI, 0, + USB_KNOWNDEV_NOPROD, + "AEI", + NULL, + }, + { + USB_VENDOR_HANK, 0, + USB_KNOWNDEV_NOPROD, + "Hank Connection", + NULL, + }, + { + USB_VENDOR_PQI, 0, + USB_KNOWNDEV_NOPROD, + "PQI", + NULL, + }, + { + USB_VENDOR_DAISY, 0, + USB_KNOWNDEV_NOPROD, + "Daisy Technology", + NULL, + }, + { + USB_VENDOR_NI, 0, + USB_KNOWNDEV_NOPROD, + "National Instruments", + NULL, + }, + { + USB_VENDOR_MICRONET, 0, + USB_KNOWNDEV_NOPROD, + "Micronet Communications", + NULL, + }, + { + USB_VENDOR_IODATA2, 0, + USB_KNOWNDEV_NOPROD, + "I-O Data", + NULL, + }, + { + USB_VENDOR_IRIVER, 0, + USB_KNOWNDEV_NOPROD, + "iRiver", + NULL, + }, + { + USB_VENDOR_DELL, 0, + USB_KNOWNDEV_NOPROD, + "Dell", + NULL, + }, + { + USB_VENDOR_WCH, 0, + USB_KNOWNDEV_NOPROD, + "QinHeng Electronics", + NULL, + }, + { + USB_VENDOR_ACEECA, 0, + USB_KNOWNDEV_NOPROD, + "Aceeca", + NULL, + }, + { + USB_VENDOR_AVERATEC, 0, + USB_KNOWNDEV_NOPROD, + "Averatec", + NULL, + }, + { + USB_VENDOR_SWEEX, 0, + USB_KNOWNDEV_NOPROD, + "Sweex", + NULL, + }, + { + USB_VENDOR_ONSPEC2, 0, + USB_KNOWNDEV_NOPROD, + "OnSpec Electronic Inc.", + NULL, + }, + { + USB_VENDOR_ZINWELL, 0, + USB_KNOWNDEV_NOPROD, + "Zinwell", + NULL, + }, + { + USB_VENDOR_SITECOM, 0, + USB_KNOWNDEV_NOPROD, + "Sitecom", + NULL, + }, + { + USB_VENDOR_ARKMICRO, 0, + USB_KNOWNDEV_NOPROD, + "Arkmicro Technologies Inc.", + NULL, + }, + { + USB_VENDOR_3COM2, 0, + USB_KNOWNDEV_NOPROD, + "3Com", + NULL, + }, + { + USB_VENDOR_INTEL, 0, + USB_KNOWNDEV_NOPROD, + "Intel", + NULL, + }, + { + USB_VENDOR_SITECOM2, 0, + USB_KNOWNDEV_NOPROD, + "Sitecom", + NULL, + }, + { + USB_VENDOR_MOSCHIP, 0, + USB_KNOWNDEV_NOPROD, + "MosChip Semiconductor", + NULL, + }, + { + USB_VENDOR_3COM3, 0, + USB_KNOWNDEV_NOPROD, + "3Com", + NULL, + }, + { + USB_VENDOR_HP2, 0, + USB_KNOWNDEV_NOPROD, + "Hewlett Packard", + NULL, + }, + { + USB_VENDOR_USRP, 0, + USB_KNOWNDEV_NOPROD, + "GNU Radio USRP", + NULL, + }, + { 0, 0, 0, NULL, NULL, } +}; diff --git a/sys/dev/usb2/include/usb2_endian.h b/sys/dev/usb2/include/usb2_endian.h new file mode 100644 index 000000000000..2f4900870b70 --- /dev/null +++ b/sys/dev/usb2/include/usb2_endian.h @@ -0,0 +1,119 @@ +/* $FreeBSD$ */ +/* + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_ENDIAN_H_ +#define _USB2_ENDIAN_H_ + +#include +#include + +/* + * Declare the basic USB record types. USB records have an alignment + * of 1 byte and are always packed. + */ +typedef uint8_t uByte; +typedef uint8_t uWord[2]; +typedef uint8_t uDWord[4]; +typedef uint8_t uQWord[8]; + +/* + * Define a set of macros that can get and set data independent of + * CPU endianness and CPU alignment requirements: + */ +#define UGETB(w) \ + ((w)[0]) + +#define UGETW(w) \ + ((w)[0] | \ + ((w)[1] << 8)) + +#define UGETDW(w) \ + ((w)[0] | \ + ((w)[1] << 8) | \ + ((w)[2] << 16) | \ + ((w)[3] << 24)) + +#define UGETQW(w) \ + ((w)[0] | \ + ((w)[1] << 8) | \ + ((w)[2] << 16) | \ + ((w)[3] << 24) | \ + (((uint64_t)((w)[4])) << 32) | \ + (((uint64_t)((w)[5])) << 40) | \ + (((uint64_t)((w)[6])) << 48) | \ + (((uint64_t)((w)[7])) << 56)) + +#define USETB(w,v) do { \ + (w)[0] = (uint8_t)(v); \ +} while (0) + +#define USETW(w,v) do { \ + (w)[0] = (uint8_t)(v); \ + (w)[1] = (uint8_t)((v) >> 8); \ +} while (0) + +#define USETDW(w,v) do { \ + (w)[0] = (uint8_t)(v); \ + (w)[1] = (uint8_t)((v) >> 8); \ + (w)[2] = (uint8_t)((v) >> 16); \ + (w)[3] = (uint8_t)((v) >> 24); \ +} while (0) + +#define USETQW(w,v) do { \ + (w)[0] = (uint8_t)(v); \ + (w)[1] = (uint8_t)((v) >> 8); \ + (w)[2] = (uint8_t)((v) >> 16); \ + (w)[3] = (uint8_t)((v) >> 24); \ + (w)[4] = (uint8_t)((v) >> 32); \ + (w)[5] = (uint8_t)((v) >> 40); \ + (w)[6] = (uint8_t)((v) >> 48); \ + (w)[7] = (uint8_t)((v) >> 56); \ +} while (0) + +#define USETW2(w,b1,b0) do { \ + (w)[0] = (uint8_t)(b0); \ + (w)[1] = (uint8_t)(b1); \ +} while (0) + +#define USETW4(w,b3,b2,b1,b0) do { \ + (w)[0] = (uint8_t)(b0); \ + (w)[1] = (uint8_t)(b1); \ + (w)[2] = (uint8_t)(b2); \ + (w)[3] = (uint8_t)(b3); \ +} while (0) + +#define USETW8(w,b7,b6,b5,b4,b3,b2,b1,b0) do { \ + (w)[0] = (uint8_t)(b0); \ + (w)[1] = (uint8_t)(b1); \ + (w)[2] = (uint8_t)(b2); \ + (w)[3] = (uint8_t)(b3); \ + (w)[4] = (uint8_t)(b4); \ + (w)[5] = (uint8_t)(b5); \ + (w)[6] = (uint8_t)(b6); \ + (w)[7] = (uint8_t)(b7); \ +} while (0) + +#endif /* _USB2_ENDIAN_H_ */ diff --git a/sys/dev/usb2/include/usb2_error.h b/sys/dev/usb2/include/usb2_error.h new file mode 100644 index 000000000000..86c62c2b56a7 --- /dev/null +++ b/sys/dev/usb2/include/usb2_error.h @@ -0,0 +1,68 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_ERROR_H_ +#define _USB2_ERROR_H_ + +/* + * The "USB_STATUS" macro defines all the USB error codes. + * NOTE: "USB_ERR_NORMAL_COMPLETION" is not an error code. + * NOTE: "USB_ERR_STARTING" is not an error code. + */ +#define USB_ERR(m,n)\ +m(n, USB_ERR_NORMAL_COMPLETION)\ +m(n, USB_ERR_PENDING_REQUESTS)\ +m(n, USB_ERR_NOT_STARTED)\ +m(n, USB_ERR_INVAL)\ +m(n, USB_ERR_NOMEM)\ +m(n, USB_ERR_CANCELLED)\ +m(n, USB_ERR_BAD_ADDRESS)\ +m(n, USB_ERR_BAD_BUFSIZE)\ +m(n, USB_ERR_BAD_FLAG)\ +m(n, USB_ERR_NO_CALLBACK)\ +m(n, USB_ERR_IN_USE)\ +m(n, USB_ERR_NO_ADDR)\ +m(n, USB_ERR_NO_PIPE)\ +m(n, USB_ERR_ZERO_NFRAMES)\ +m(n, USB_ERR_ZERO_MAXP)\ +m(n, USB_ERR_SET_ADDR_FAILED)\ +m(n, USB_ERR_NO_POWER)\ +m(n, USB_ERR_TOO_DEEP)\ +m(n, USB_ERR_IOERROR)\ +m(n, USB_ERR_NOT_CONFIGURED)\ +m(n, USB_ERR_TIMEOUT)\ +m(n, USB_ERR_SHORT_XFER)\ +m(n, USB_ERR_STALLED)\ +m(n, USB_ERR_INTERRUPTED)\ +m(n, USB_ERR_DMA_LOAD_FAILED)\ +m(n, USB_ERR_BAD_CONTEXT)\ +m(n, USB_ERR_NO_ROOT_HUB)\ +m(n, USB_ERR_NO_INTR_THREAD)\ +m(n, USB_ERR_NOT_LOCKED)\ + +USB_MAKE_ENUM(USB_ERR); + +#endif /* _USB2_ERROR_H_ */ diff --git a/sys/dev/usb2/include/usb2_hid.h b/sys/dev/usb2/include/usb2_hid.h new file mode 100644 index 000000000000..ee07a0386c1b --- /dev/null +++ b/sys/dev/usb2/include/usb2_hid.h @@ -0,0 +1,173 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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. + */ + +#ifndef _USB2_HID_H_ +#define _USB2_HID_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 + +struct usb2_hid_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord bcdHID; + uByte bCountryCode; + uByte bNumDescriptors; + struct { + uByte bDescriptorType; + uWord wDescriptorLength; + } descrs[1]; +} __packed; + +#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 /* _USB2_HID_H_ */ diff --git a/sys/dev/usb2/include/usb2_ioctl.h b/sys/dev/usb2/include/usb2_ioctl.h new file mode 100644 index 000000000000..0cab67a4b08b --- /dev/null +++ b/sys/dev/usb2/include/usb2_ioctl.h @@ -0,0 +1,301 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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. + */ + +#ifndef _USB2_IOCTL_H_ +#define _USB2_IOCTL_H_ + +#include + +#define USB_DEVICE_NAME "usb" +#define USB_GENERIC_NAME "ugen" + +/* definition of USB power mode */ +#define USB_POWER_MODE_OFF 0 /* turn off device */ +#define USB_POWER_MODE_ON 1 /* always on */ +#define USB_POWER_MODE_SAVE 2 /* automatic suspend and resume */ +#define USB_POWER_MODE_SUSPEND 3 /* force suspend */ +#define USB_POWER_MODE_RESUME 4 /* force resume */ + +struct usb2_read_dir { + void *urd_data; + uint32_t urd_startentry; + uint32_t urd_maxlen; +}; + +struct usb2_ctl_request { + void *ucr_data; + uint16_t ucr_flags; +#define USB_USE_POLLING 0x0001 /* internal flag */ +#define USB_SHORT_XFER_OK 0x0004 /* allow short reads */ +#define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */ +#define USB_USER_DATA_PTR 0x0020 /* internal flag */ + uint16_t ucr_actlen; /* actual length transferred */ + uint8_t ucr_addr; /* zero - currently not used */ + struct usb2_device_request ucr_request; +}; + +struct usb2_alt_interface { + uint8_t uai_interface_index; + uint8_t uai_alt_index; +}; + +struct usb2_gen_descriptor { + void *ugd_data; + uint16_t ugd_lang_id; + uint16_t ugd_maxlen; + uint16_t ugd_actlen; + uint16_t ugd_offset; + uint8_t ugd_config_index; + uint8_t ugd_string_index; + uint8_t ugd_iface_index; + uint8_t ugd_altif_index; + uint8_t ugd_endpt_index; + uint8_t ugd_report_type; + uint8_t reserved[8]; +}; + +struct usb2_device_names { + char *udn_devnames_ptr; /* userland pointer to comma separated + * list of device names */ + uint16_t udn_devnames_len; /* maximum string length including + * terminating zero */ +}; + +struct usb2_device_info { + uint16_t udi_productNo; + uint16_t udi_vendorNo; + uint16_t udi_releaseNo; + uint16_t udi_power; /* power consumption in mA, 0 if + * selfpowered */ + uint8_t udi_bus; + uint8_t udi_addr; /* device address */ + uint8_t udi_index; /* device index */ + uint8_t udi_class; + uint8_t udi_subclass; + uint8_t udi_protocol; + uint8_t udi_config_no; /* current config number */ + uint8_t udi_config_index; /* current config index */ + uint8_t udi_speed; /* see "USB_SPEED_XXX" */ + uint8_t udi_mode; /* see "USB_MODE_XXX" */ + uint8_t udi_nports; + uint8_t udi_hubaddr; /* parent HUB address */ + uint8_t udi_hubindex; /* parent HUB device index */ + uint8_t udi_hubport; /* parent HUB port */ + uint8_t udi_power_mode; /* see "USB_POWER_MODE_XXX" */ + uint8_t udi_suspended; /* set if device is suspended */ + uint8_t udi_reserved[16]; /* leave space for the future */ + char udi_product[128]; + char udi_vendor[128]; + char udi_serial[64]; + char udi_release[8]; +}; + +struct usb2_device_stats { + uint32_t uds_requests_ok[4]; /* Indexed by transfer type UE_XXX */ + uint32_t uds_requests_fail[4]; /* Indexed by transfer type UE_XXX */ +}; + +struct usb2_fs_start { + uint8_t ep_index; +}; + +struct usb2_fs_stop { + uint8_t ep_index; +}; + +struct usb2_fs_complete { + uint8_t ep_index; +}; + +/* This structure is used for all endpoint types */ +struct usb2_fs_endpoint { + /* + * NOTE: isochronous USB transfer only use one buffer, but can have + * multiple frame lengths ! + */ + void **ppBuffer; /* pointer to userland buffers */ + uint32_t *pLength; /* pointer to frame lengths, updated + * to actual length */ + uint32_t nFrames; /* number of frames */ + uint32_t aFrames; /* actual number of frames */ + uint16_t flags; + /* a single short frame will terminate */ +#define USB_FS_FLAG_SINGLE_SHORT_OK 0x0001 + /* multiple short frames are allowed */ +#define USB_FS_FLAG_MULTI_SHORT_OK 0x0002 + /* all frame(s) transmitted are short terminated */ +#define USB_FS_FLAG_FORCE_SHORT 0x0004 + /* will do a clear-stall before xfer */ +#define USB_FS_FLAG_CLEAR_STALL 0x0008 + uint16_t timeout; /* in milliseconds */ + /* isocronous completion time in milliseconds - used for echo cancel */ + uint16_t isoc_time_complete; + /* timeout value for no timeout */ +#define USB_FS_TIMEOUT_NONE 0 + uint8_t status; /* see USB_ERR_XXX */ +}; + +struct usb2_fs_init { + /* userland pointer to endpoints structure */ + struct usb2_fs_endpoint *pEndpoints; + /* maximum number of endpoints */ + uint8_t ep_index_max; +}; + +struct usb2_fs_uninit { + uint8_t dummy; /* zero */ +}; + +struct usb2_fs_open { +#define USB_FS_MAX_BUFSIZE (1 << 18) + uint32_t max_bufsize; +#define USB_FS_MAX_FRAMES (1 << 12) + uint32_t max_frames; + uint16_t max_packet_length; /* read only */ + uint8_t dev_index; /* currently unused */ + uint8_t ep_index; + uint8_t ep_no; /* bEndpointNumber */ +}; + +struct usb2_fs_close { + uint8_t ep_index; +}; + +struct usb2_fs_clear_stall_sync { + uint8_t ep_index; +}; + +struct usb2_dev_perm { + /* Access information */ + uint32_t user_id; + uint32_t group_id; + uint16_t mode; + + /* Device location */ + uint16_t bus_index; + uint16_t dev_index; + uint16_t iface_index; +}; + +struct usb2_gen_quirk { + uint16_t index; /* Quirk Index */ + uint16_t vid; /* Vendor ID */ + uint16_t pid; /* Product ID */ + uint16_t bcdDeviceLow; /* Low Device Revision */ + uint16_t bcdDeviceHigh; /* High Device Revision */ + uint16_t reserved[2]; + /* + * String version of quirk including terminating zero. See UQ_XXX in + * "usb2_quirk.h". + */ + char quirkname[64 - 14]; +}; + +/* USB controller */ +#define USB_REQUEST _IOWR('U', 1, struct usb2_ctl_request) +#define USB_SETDEBUG _IOW ('U', 2, int) +#define USB_DISCOVER _IO ('U', 3) +#define USB_DEVICEINFO _IOWR('U', 4, struct usb2_device_info) +#define USB_DEVICESTATS _IOR ('U', 5, struct usb2_device_stats) +#define USB_DEVICEENUMERATE _IOW ('U', 6, int) + +/* Generic HID device */ +#define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb2_gen_descriptor) +#define USB_SET_IMMED _IOW ('U', 22, int) +#define USB_GET_REPORT _IOWR('U', 23, struct usb2_gen_descriptor) +#define USB_SET_REPORT _IOW ('U', 24, struct usb2_gen_descriptor) +#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 usb2_alt_interface) +#define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb2_alt_interface) +#define USB_GET_DEVICE_DESC _IOR ('U', 105, struct usb2_device_descriptor) +#define USB_GET_CONFIG_DESC _IOR ('U', 106, struct usb2_config_descriptor) +#define USB_GET_RX_INTERFACE_DESC _IOR ('U', 107, struct usb2_interface_descriptor) +#define USB_GET_RX_ENDPOINT_DESC _IOR ('U', 108, struct usb2_endpoint_descriptor) +#define USB_GET_FULL_DESC _IOWR('U', 109, struct usb2_gen_descriptor) +#define USB_GET_STRING_DESC _IOWR('U', 110, struct usb2_gen_descriptor) +#define USB_DO_REQUEST _IOWR('U', 111, struct usb2_ctl_request) +#define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb2_device_info) +#define USB_SET_RX_SHORT_XFER _IOW ('U', 113, int) +#define USB_SET_RX_TIMEOUT _IOW ('U', 114, int) +#define USB_GET_RX_FRAME_SIZE _IOR ('U', 115, int) +#define USB_GET_RX_BUFFER_SIZE _IOR ('U', 117, int) +#define USB_SET_RX_BUFFER_SIZE _IOW ('U', 118, int) +#define USB_SET_RX_STALL_FLAG _IOW ('U', 119, int) +#define USB_SET_TX_STALL_FLAG _IOW ('U', 120, int) +#define USB_GET_DEVICENAMES _IOW ('U', 121, struct usb2_device_names) +#define USB_CLAIM_INTERFACE _IOW ('U', 122, int) +#define USB_RELEASE_INTERFACE _IOW ('U', 123, int) +#define USB_IFACE_DRIVER_ACTIVE _IOW ('U', 124, int) +#define USB_IFACE_DRIVER_DETACH _IOW ('U', 125, int) +#define USB_GET_PLUGTIME _IOR ('U', 126, uint32_t) +#define USB_READ_DIR _IOW ('U', 127, struct usb2_read_dir) +#define USB_SET_ROOT_PERM _IOW ('U', 128, struct usb2_dev_perm) +#define USB_SET_BUS_PERM _IOW ('U', 129, struct usb2_dev_perm) +#define USB_SET_DEVICE_PERM _IOW ('U', 130, struct usb2_dev_perm) +#define USB_SET_IFACE_PERM _IOW ('U', 131, struct usb2_dev_perm) +#define USB_GET_ROOT_PERM _IOWR('U', 132, struct usb2_dev_perm) +#define USB_GET_BUS_PERM _IOWR('U', 133, struct usb2_dev_perm) +#define USB_GET_DEVICE_PERM _IOWR('U', 134, struct usb2_dev_perm) +#define USB_GET_IFACE_PERM _IOWR('U', 135, struct usb2_dev_perm) +#define USB_SET_TX_FORCE_SHORT _IOW ('U', 136, int) +#define USB_SET_TX_TIMEOUT _IOW ('U', 137, int) +#define USB_GET_TX_FRAME_SIZE _IOR ('U', 138, int) +#define USB_GET_TX_BUFFER_SIZE _IOR ('U', 139, int) +#define USB_SET_TX_BUFFER_SIZE _IOW ('U', 140, int) +#define USB_GET_TX_INTERFACE_DESC _IOR ('U', 141, struct usb2_interface_descriptor) +#define USB_GET_TX_ENDPOINT_DESC _IOR ('U', 142, struct usb2_endpoint_descriptor) +#define USB_SET_PORT_ENABLE _IOW ('U', 143, int) +#define USB_SET_PORT_DISABLE _IOW ('U', 144, int) +#define USB_SET_POWER_MODE _IOW ('U', 145, int) +#define USB_GET_POWER_MODE _IOR ('U', 146, int) + +/* Modem device */ +#define USB_GET_CM_OVER_DATA _IOR ('U', 180, int) +#define USB_SET_CM_OVER_DATA _IOW ('U', 181, int) + +/* USB file system interface */ +#define USB_FS_START _IOW ('U', 192, struct usb2_fs_start) +#define USB_FS_STOP _IOW ('U', 193, struct usb2_fs_stop) +#define USB_FS_COMPLETE _IOR ('U', 194, struct usb2_fs_complete) +#define USB_FS_INIT _IOW ('U', 195, struct usb2_fs_init) +#define USB_FS_UNINIT _IOW ('U', 196, struct usb2_fs_uninit) +#define USB_FS_OPEN _IOWR('U', 197, struct usb2_fs_open) +#define USB_FS_CLOSE _IOW ('U', 198, struct usb2_fs_close) +#define USB_FS_CLEAR_STALL_SYNC _IOW ('U', 199, struct usb2_fs_clear_stall_sync) + +/* USB quirk system interface */ +#define USB_DEV_QUIRK_GET _IOWR('Q', 0, struct usb2_gen_quirk) +#define USB_QUIRK_NAME_GET _IOWR('Q', 1, struct usb2_gen_quirk) +#define USB_DEV_QUIRK_ADD _IOW ('Q', 2, struct usb2_gen_quirk) +#define USB_DEV_QUIRK_REMOVE _IOW ('Q', 3, struct usb2_gen_quirk) + +#endif /* _USB2_IOCTL_H_ */ diff --git a/sys/dev/usb2/include/usb2_mfunc.h b/sys/dev/usb2/include/usb2_mfunc.h new file mode 100644 index 000000000000..37be051c5586 --- /dev/null +++ b/sys/dev/usb2/include/usb2_mfunc.h @@ -0,0 +1,86 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 file contains various macro functions. */ + +#ifndef _USB2_MFUNC_H_ +#define _USB2_MFUNC_H_ + +#define USB_MAKE_001(n,ENUM) ENUM, +#define USB_MAKE_ENUM(m) \ +enum { m(USB_MAKE_001,) m##_MAX } + +#define USB_MAKE_002(n,ENUM) #ENUM, +#define USB_MAKE_DEBUG_TABLE(m) \ +static const char * m[m##_MAX] = { m(USB_MAKE_002,) } + +#define USB_LOG2(n) ( \ +((x) <= (1<<0x00)) ? 0x00 : \ +((x) <= (1<<0x01)) ? 0x01 : \ +((x) <= (1<<0x02)) ? 0x02 : \ +((x) <= (1<<0x03)) ? 0x03 : \ +((x) <= (1<<0x04)) ? 0x04 : \ +((x) <= (1<<0x05)) ? 0x05 : \ +((x) <= (1<<0x06)) ? 0x06 : \ +((x) <= (1<<0x07)) ? 0x07 : \ +((x) <= (1<<0x08)) ? 0x08 : \ +((x) <= (1<<0x09)) ? 0x09 : \ +((x) <= (1<<0x0A)) ? 0x0A : \ +((x) <= (1<<0x0B)) ? 0x0B : \ +((x) <= (1<<0x0C)) ? 0x0C : \ +((x) <= (1<<0x0D)) ? 0x0D : \ +((x) <= (1<<0x0E)) ? 0x0E : \ +((x) <= (1<<0x0F)) ? 0x0F : \ +((x) <= (1<<0x10)) ? 0x10 : \ +((x) <= (1<<0x11)) ? 0x11 : \ +((x) <= (1<<0x12)) ? 0x12 : \ +((x) <= (1<<0x13)) ? 0x13 : \ +((x) <= (1<<0x14)) ? 0x14 : \ +((x) <= (1<<0x15)) ? 0x15 : \ +((x) <= (1<<0x16)) ? 0x16 : \ +((x) <= (1<<0x17)) ? 0x17 : \ +((x) <= (1<<0x18)) ? 0x18 : \ +((x) <= (1<<0x19)) ? 0x19 : \ +((x) <= (1<<0x1A)) ? 0x1A : \ +((x) <= (1<<0x1B)) ? 0x1B : \ +((x) <= (1<<0x1C)) ? 0x1C : \ +((x) <= (1<<0x1D)) ? 0x1D : \ +((x) <= (1<<0x1E)) ? 0x1E : \ +0x1F) + + +/* helper for converting pointers to integers */ +#define USB_P2U(ptr) \ + (((const uint8_t *)(ptr)) - ((const uint8_t *)0)) + +/* helper for computing offsets */ +#define USB_ADD_BYTES(ptr,size) \ + ((void *)(USB_P2U(ptr) + (size))) + +/* debug macro */ +#define USB_ASSERT KASSERT + +#endif /* _USB2_MFUNC_H_ */ diff --git a/sys/dev/usb2/include/usb2_revision.h b/sys/dev/usb2/include/usb2_revision.h new file mode 100644 index 000000000000..b57946c00c59 --- /dev/null +++ b/sys/dev/usb2/include/usb2_revision.h @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_REVISION_H_ +#define _USB2_REVISION_H_ + +#include + +/* + * The "USB_SPEED" macro defines all the supported USB speeds. + */ +#define USB_SPEED(m,n)\ +m(n, USB_SPEED_VARIABLE)\ +m(n, USB_SPEED_LOW)\ +m(n, USB_SPEED_FULL)\ +m(n, USB_SPEED_HIGH)\ +m(n, USB_SPEED_SUPER)\ + +USB_MAKE_ENUM(USB_SPEED); + +/* + * The "USB_REV" macro defines all the supported USB revisions. + */ +#define USB_REV(m,n)\ +m(n, USB_REV_UNKNOWN)\ +m(n, USB_REV_PRE_1_0)\ +m(n, USB_REV_1_0)\ +m(n, USB_REV_1_1)\ +m(n, USB_REV_2_0)\ +m(n, USB_REV_2_5)\ +m(n, USB_REV_3_0)\ + +USB_MAKE_ENUM(USB_REV); + +/* + * The "USB_MODE" macro defines all the supported USB modes. + */ +#define USB_MODE(m,n)\ +m(n, USB_MODE_HOST)\ +m(n, USB_MODE_DEVICE)\ + +USB_MAKE_ENUM(USB_MODE); + +#endif /* _USB2_REVISION_H_ */ diff --git a/sys/dev/usb2/include/usb2_standard.h b/sys/dev/usb2/include/usb2_standard.h new file mode 100644 index 000000000000..05ca31496d1d --- /dev/null +++ b/sys/dev/usb2/include/usb2_standard.h @@ -0,0 +1,497 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. 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. + */ + +#ifndef _USB2_STANDARD_H_ +#define _USB2_STANDARD_H_ + +#include + +/* + * Minimum time a device needs to be powered down to go through a + * power cycle. These values are not in the USB specification. + */ +#define USB_POWER_DOWN_TIME 200 /* ms */ +#define USB_PORT_POWER_DOWN_TIME 100 /* ms */ + +#if 0 +/* These are the values from the USB specification. */ +#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 and 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 */ + +/* + * USB record layout in memory: + * + * - USB config 0 + * - USB interfaces + * - USB alternative interfaces + * - USB pipes + * + * - USB config 1 + * - USB interfaces + * - USB alternative interfaces + * - USB pipes + */ + +/* Declaration of USB records */ + +struct usb2_device_request { + uByte bmRequestType; + uByte bRequest; + uWord wValue; + uWord wIndex; + uWord wLength; +} __packed; + +#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 USB_LANGUAGE_TABLE 0x00 /* Index of the language ID table + * string */ +#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 + +/* 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 + +/* Feature numbers */ +#define UF_ENDPOINT_HALT 0 +#define UF_DEVICE_REMOTE_WAKEUP 1 +#define UF_TEST_MODE 2 + +/* HUB specific 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 + +struct usb2_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; +} __packed; + +struct usb2_device_descriptor { + 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; +} __packed; + +/* 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 + +struct usb2_config_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord wTotalLength; + uByte bNumInterface; + uByte bConfigurationValue; +#define USB_UNCONFIG_NO 0 + 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 +} __packed; + +struct usb2_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bInterfaceNumber; + uByte bAlternateSetting; + uByte bNumEndpoints; + uByte bInterfaceClass; + uByte bInterfaceSubClass; + uByte bInterfaceProtocol; + uByte iInterface; +} __packed; + +/* Interface class codes */ +#define UICLASS_UNSPEC 0x00 +#define UICLASS_AUDIO 0x01 /* audio */ +#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 UISUBCLASS_WIRELESS_HANDSET_CM 8 +#define UISUBCLASS_DEVICE_MGMT 9 +#define UISUBCLASS_MOBILE_DIRECT_LINE_MODEL 10 +#define UISUBCLASS_OBEX 11 +#define UISUBCLASS_ETHERNET_EMULATION_MODEL 12 + +#define UIPROTO_CDC_AT 1 +#define UIPROTO_CDC_ETH_512X4 0x76 /* FreeBSD specific */ + +#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 UISUBCLASS_SIC 1 /* still image class */ +#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 + +struct usb2_endpoint_descriptor { + 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_DIR_ANY 0xff /* for internal use only! */ +#define UE_ADDR 0x0f +#define UE_ADDR_ANY 0xff /* for internal use only! */ +#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_BULK_INTR 0xfe /* for internal use only! */ +#define UE_TYPE_ANY 0xff /* for internal use only! */ +#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_ZERO_MPS 0xFFFF /* for internal use only */ + uByte bInterval; +} __packed; + +struct usb2_string_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord bString[126]; + uByte bUnused; +} __packed; + +#define USB_MAKE_STRING_DESC(m,name) \ +struct name { \ + uByte bLength; \ + uByte bDescriptorType; \ + uByte bData[sizeof((uint8_t []){m})]; \ +} __packed; \ +static const struct name name = { \ + .bLength = sizeof(struct name), \ + .bDescriptorType = UDESC_STRING, \ + .bData = { m }, \ +} + +struct usb2_hub_descriptor { + 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]; +} __packed; + +/* minimum HUB descriptor (8-ports maximum) */ +struct usb2_hub_descriptor_min { + uByte bDescLength; + uByte bDescriptorType; + uByte bNbrPorts; + uWord wHubCharacteristics; + uByte bPwrOn2PwrGood; + uByte bHubContrCurrent; + uByte DeviceRemovable[1]; + uByte PortPowerCtrlMask[1]; +} __packed; + +struct usb2_device_qualifier { + uByte bLength; + uByte bDescriptorType; + uWord bcdUSB; + uByte bDeviceClass; + uByte bDeviceSubClass; + uByte bDeviceProtocol; + uByte bMaxPacketSize0; + uByte bNumConfigurations; + uByte bReserved; +} __packed; + +struct usb2_otg_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bmAttributes; +#define UOTG_SRP 0x01 +#define UOTG_HNP 0x02 +} __packed; + +/* OTG feature selectors */ +#define UOTG_B_HNP_ENABLE 3 +#define UOTG_A_HNP_SUPPORT 4 +#define UOTG_A_ALT_HNP_SUPPORT 5 + +struct usb2_status { + uWord wStatus; +/* Device status flags */ +#define UDS_SELF_POWERED 0x0001 +#define UDS_REMOTE_WAKEUP 0x0002 +/* Endpoint status flags */ +#define UES_HALT 0x0001 +} __packed; + +struct usb2_hub_status { + uWord wHubStatus; +#define UHS_LOCAL_POWER 0x0001 +#define UHS_OVER_CURRENT 0x0002 + uWord wHubChange; +} __packed; + +struct usb2_port_status { + 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_MODE_DEVICE 0x0020 /* currently FreeBSD specific */ +#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 +} __packed; + +#endif /* _USB2_STANDARD_H_ */ diff --git a/sys/dev/usb2/input/uhid2.c b/sys/dev/usb2/input/uhid2.c new file mode 100644 index 000000000000..f5111f5e79e4 --- /dev/null +++ b/sys/dev/usb2/input/uhid2.c @@ -0,0 +1,822 @@ +/* $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 +__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 + */ + +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uhid_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#if USB_DEBUG +static int uhid_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid"); +SYSCTL_INT(_hw_usb2_uhid, OID_AUTO, debug, CTLFLAG_RW, + &uhid_debug, 0, "Debug level"); +#endif + +#define UHID_N_TRANSFER 4 /* units */ +#define UHID_BSIZE 1024 /* bytes, buffer size */ +#define UHID_FRAME_NUM 50 /* bytes, frame number */ + +struct uhid_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + + struct usb2_xfer *sc_xfer[UHID_N_TRANSFER]; + struct usb2_device *sc_udev; + void *sc_repdesc_ptr; + + uint32_t sc_isize; + uint32_t sc_osize; + uint32_t sc_fsize; + + uint16_t sc_repdesc_size; + + uint8_t sc_iface_no; + uint8_t sc_iface_index; + uint8_t sc_iid; + uint8_t sc_oid; + uint8_t sc_fid; + uint8_t sc_flags; +#define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ +#define UHID_FLAG_INTR_STALL 0x02 /* set if interrupt transfer stalled */ +#define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are + * static */ +}; + +static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()}; +static const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()}; +static const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()}; + +/* prototypes */ + +static device_probe_t uhid_probe; +static device_attach_t uhid_attach; +static device_detach_t uhid_detach; + +static usb2_callback_t uhid_intr_callback; +static usb2_callback_t uhid_intr_clear_stall_callback; +static usb2_callback_t uhid_write_callback; +static usb2_callback_t uhid_read_callback; + +static usb2_fifo_cmd_t uhid_start_read; +static usb2_fifo_cmd_t uhid_stop_read; +static usb2_fifo_cmd_t uhid_start_write; +static usb2_fifo_cmd_t uhid_stop_write; +static usb2_fifo_open_t uhid_open; +static usb2_fifo_close_t uhid_close; +static usb2_fifo_ioctl_t uhid_ioctl; + +static struct usb2_fifo_methods uhid_fifo_methods = { + .f_open = &uhid_open, + .f_close = &uhid_close, + .f_ioctl = &uhid_ioctl, + .f_start_read = &uhid_start_read, + .f_stop_read = &uhid_stop_read, + .f_start_write = &uhid_start_write, + .f_stop_write = &uhid_stop_write, + .basename[0] = "uhid", +}; + +static void +uhid_intr_callback(struct usb2_xfer *xfer) +{ + struct uhid_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("transferred!\n"); + + if (xfer->actlen >= sc->sc_isize) { + usb2_fifo_put_data( + sc->sc_fifo.fp[USB_FIFO_RX], + xfer->frbuffers, + 0, sc->sc_isize, 1); + } else { + /* ignore it */ + DPRINTF("ignored short transfer, " + "%d bytes\n", xfer->actlen); + } + + case USB_ST_SETUP: + if (sc->sc_flags & UHID_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[1]); + } else { + if (usb2_fifo_put_bytes_max( + sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UHID_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[1]); + } + return; + } +} + +static void +uhid_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uhid_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UHID_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uhid_fill_set_report(struct usb2_device_request *req, uint8_t iface_no, + uint8_t type, uint8_t id, uint16_t size) +{ + req->bmRequestType = UT_WRITE_CLASS_INTERFACE; + req->bRequest = UR_SET_REPORT; + USETW2(req->wValue, type, id); + req->wIndex[0] = iface_no; + req->wIndex[1] = 0; + USETW(req->wLength, size); + return; +} + +static void +uhid_fill_get_report(struct usb2_device_request *req, uint8_t iface_no, + uint8_t type, uint8_t id, uint16_t size) +{ + req->bmRequestType = UT_READ_CLASS_INTERFACE; + req->bRequest = UR_GET_REPORT; + USETW2(req->wValue, type, id); + req->wIndex[0] = iface_no; + req->wIndex[1] = 0; + USETW(req->wLength, size); + return; +} + +static void +uhid_write_callback(struct usb2_xfer *xfer) +{ + struct uhid_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + uint32_t size = sc->sc_osize; + uint32_t actlen; + uint8_t id; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: + /* try to extract the ID byte */ + if (sc->sc_oid) { + + if (usb2_fifo_get_data( + sc->sc_fifo.fp[USB_FIFO_TX], + xfer->frbuffers, + 0, 1, &actlen, 0)) { + if (actlen != 1) { + goto tr_error; + } + usb2_copy_out(xfer->frbuffers, 0, &id, 1); + + } else { + return; + } + if (size) { + size--; + } + } else { + id = 0; + } + + if (usb2_fifo_get_data( + sc->sc_fifo.fp[USB_FIFO_TX], + xfer->frbuffers + 1, + 0, UHID_BSIZE, &actlen, 1)) { + if (actlen != size) { + goto tr_error; + } + uhid_fill_set_report + (&req, sc->sc_iface_no, + UHID_OUTPUT_REPORT, id, size); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = size; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + usb2_start_hardware(xfer); + } + return; + + default: +tr_error: + /* bomb out */ + usb2_fifo_get_data_error(sc->sc_fifo.fp[USB_FIFO_TX]); + return; + } +} + +static void +uhid_read_callback(struct usb2_xfer *xfer) +{ + struct uhid_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], xfer->frbuffers, + sizeof(req), sc->sc_isize, 1); + return; + + case USB_ST_SETUP: + + if (usb2_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) > 0) { + + uhid_fill_get_report + (&req, sc->sc_iface_no, UHID_INPUT_REPORT, + sc->sc_iid, sc->sc_isize); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = sc->sc_isize; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + /* bomb out */ + usb2_fifo_put_data_error(sc->sc_fifo.fp[USB_FIFO_RX]); + return; + } +} + +static const struct usb2_config uhid_config[UHID_N_TRANSFER] = { + + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &uhid_intr_callback, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uhid_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request) + UHID_BSIZE, + .mh.callback = &uhid_write_callback, + .mh.timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request) + UHID_BSIZE, + .mh.callback = &uhid_read_callback, + .mh.timeout = 1000, /* 1 second */ + }, +}; + +static void +uhid_start_read(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + if (sc->sc_flags & UHID_FLAG_IMMED) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + usb2_transfer_start(sc->sc_xfer[0]); + } + return; +} + +static void +uhid_stop_read(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +uhid_start_write(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[2]); + return; +} + +static void +uhid_stop_write(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[2]); + return; +} + +static int +uhid_get_report(struct uhid_softc *sc, uint8_t type, + uint8_t id, void *kern_data, void *user_data, + uint16_t len) +{ + int err; + uint8_t free_data = 0; + + if (kern_data == NULL) { + kern_data = malloc(len, M_USBDEV, M_WAITOK); + if (kern_data == NULL) { + err = ENOMEM; + goto done; + } + free_data = 1; + } + err = usb2_req_get_report(sc->sc_udev, NULL, kern_data, + len, sc->sc_iface_index, type, id); + if (err) { + err = ENXIO; + goto done; + } + if (user_data) { + /* dummy buffer */ + err = copyout(kern_data, user_data, len); + if (err) { + goto done; + } + } +done: + if (free_data) { + free(kern_data, M_USBDEV); + } + return (err); +} + +static int +uhid_set_report(struct uhid_softc *sc, uint8_t type, + uint8_t id, void *kern_data, void *user_data, + uint16_t len) +{ + int err; + uint8_t free_data = 0; + + if (kern_data == NULL) { + kern_data = malloc(len, M_USBDEV, M_WAITOK); + if (kern_data == NULL) { + err = ENOMEM; + goto done; + } + free_data = 1; + err = copyin(user_data, kern_data, len); + if (err) { + goto done; + } + } + err = usb2_req_set_report(sc->sc_udev, NULL, kern_data, + len, sc->sc_iface_index, type, id); + if (err) { + err = ENXIO; + goto done; + } +done: + if (free_data) { + free(kern_data, M_USBDEV); + } + return (err); +} + +static int +uhid_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + /* + * The buffers are one byte larger than maximum so that one + * can detect too large read/writes and short transfers: + */ + if (fflags & FREAD) { + /* reset flags */ + sc->sc_flags &= ~UHID_FLAG_IMMED; + + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_isize + 1, UHID_FRAME_NUM)) { + return (ENOMEM); + } + } + if (fflags & FWRITE) { + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_osize + 1, UHID_FRAME_NUM)) { + return (ENOMEM); + } + } + return (0); +} + +static void +uhid_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & (FREAD | FWRITE)) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +static int +uhid_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, + int fflags, struct thread *td) +{ + struct uhid_softc *sc = fifo->priv_sc0; + struct usb2_gen_descriptor *ugd; + uint32_t size; + int error = 0; + uint8_t id; + + switch (cmd) { + case USB_GET_REPORT_DESC: + ugd = addr; + if (sc->sc_repdesc_size > ugd->ugd_maxlen) { + size = ugd->ugd_maxlen; + } else { + size = sc->sc_repdesc_size; + } + ugd->ugd_actlen = size; + error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size); + break; + + case USB_SET_IMMED: + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + if (*(int *)addr) { + + /* do a test read */ + + error = uhid_get_report(sc, UHID_INPUT_REPORT, + sc->sc_iid, NULL, NULL, sc->sc_isize); + if (error) { + break; + } + mtx_lock(&sc->sc_mtx); + sc->sc_flags |= UHID_FLAG_IMMED; + mtx_unlock(&sc->sc_mtx); + } else { + mtx_lock(&sc->sc_mtx); + sc->sc_flags &= ~UHID_FLAG_IMMED; + mtx_unlock(&sc->sc_mtx); + } + break; + + case USB_GET_REPORT: + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + ugd = addr; + switch (ugd->ugd_report_type) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + id = sc->sc_iid; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + id = sc->sc_oid; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + id = sc->sc_fid; + break; + default: + return (EINVAL); + } + error = uhid_get_report(sc, ugd->ugd_report_type, id, + NULL, ugd->ugd_data, size); + break; + + case USB_SET_REPORT: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + ugd = addr; + switch (ugd->ugd_report_type) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + id = sc->sc_iid; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + id = sc->sc_oid; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + id = sc->sc_fid; + break; + default: + return (EINVAL); + } + error = uhid_set_report(sc, ugd->ugd_report_type, id, + NULL, ugd->ugd_data, size); + break; + + case USB_GET_REPORT_ID: + *(int *)addr = 0; /* XXX: we only support reportid 0? */ + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +static int +uhid_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->use_generic == 0) { + /* give Mouse and Keyboard drivers a try first */ + return (ENXIO); + } + if (uaa->info.bInterfaceClass != UICLASS_HID) { + + /* the Xbox 360 gamepad doesn't use the HID class */ + + if ((uaa->info.bInterfaceClass != UICLASS_VENDOR) || + (uaa->info.bInterfaceSubClass != UISUBCLASS_XBOX360_CONTROLLER) || + (uaa->info.bInterfaceProtocol != UIPROTO_XBOX360_GAMEPAD)) { + return (ENXIO); + } + } + if (usb2_test_quirk(uaa, UQ_HID_IGNORE)) { + return (ENXIO); + } + return (0); +} + +static int +uhid_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uhid_softc *sc = device_get_softc(dev); + int unit = device_get_unit(dev); + int error = 0; + + DPRINTFN(10, "sc=%p\n", sc); + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE); + + sc->sc_udev = uaa->device; + + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = uaa->info.bIfaceIndex; + + error = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config, + UHID_N_TRANSFER, sc, &sc->sc_mtx); + + if (error) { + DPRINTF("error=%s\n", usb2_errstr(error)); + goto detach; + } + if (uaa->info.idVendor == USB_VENDOR_WACOM) { + + /* the report descriptor for the Wacom Graphire is broken */ + + if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) { + + sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr); + sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_graphire_report_descr, 0); + sc->sc_flags |= UHID_FLAG_STATIC_DESC; + + } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { + + static uint8_t reportbuf[] = {2, 2, 2}; + + /* + * The Graphire3 needs 0x0202 to be written to + * feature report ID 2 before it'll start + * returning digitizer data. + */ + error = usb2_req_set_report + (uaa->device, &Giant, reportbuf, sizeof(reportbuf), + uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2); + + if (error) { + DPRINTF("set report failed, error=%s (ignored)\n", + usb2_errstr(error)); + } + sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr); + sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_graphire3_4x5_report_descr, 0); + sc->sc_flags |= UHID_FLAG_STATIC_DESC; + } + } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) && + (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) && + (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) { + + /* the Xbox 360 gamepad has no report descriptor */ + sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr); + sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_xb360gp_report_descr, 0); + sc->sc_flags |= UHID_FLAG_STATIC_DESC; + } + if (sc->sc_repdesc_ptr == NULL) { + + error = usb2_req_get_hid_desc + (uaa->device, &Giant, &sc->sc_repdesc_ptr, + &sc->sc_repdesc_size, M_USBDEV, uaa->info.bIfaceIndex); + + if (error) { + device_printf(dev, "no report descriptor\n"); + goto detach; + } + } + error = usb2_req_set_idle(uaa->device, &Giant, + uaa->info.bIfaceIndex, 0, 0); + + if (error) { + DPRINTF("set idle failed, error=%s (ignored)\n", + usb2_errstr(error)); + } + sc->sc_isize = hid_report_size + (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid); + + sc->sc_osize = hid_report_size + (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid); + + sc->sc_fsize = hid_report_size + (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid); + + if (sc->sc_isize > UHID_BSIZE) { + DPRINTF("input size is too large, " + "%d bytes (truncating)\n", + sc->sc_isize); + sc->sc_isize = UHID_BSIZE; + } + if (sc->sc_osize > UHID_BSIZE) { + DPRINTF("output size is too large, " + "%d bytes (truncating)\n", + sc->sc_osize); + sc->sc_osize = UHID_BSIZE; + } + if (sc->sc_fsize > UHID_BSIZE) { + DPRINTF("feature size is too large, " + "%d bytes (truncating)\n", + sc->sc_fsize); + sc->sc_fsize = UHID_BSIZE; + } + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &uhid_fifo_methods, &sc->sc_fifo, + unit, 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + return (0); /* success */ + +detach: + uhid_detach(dev); + return (ENOMEM); +} + +static int +uhid_detach(device_t dev) +{ + struct uhid_softc *sc = device_get_softc(dev); + + usb2_fifo_detach(&sc->sc_fifo); + + usb2_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER); + + if (sc->sc_repdesc_ptr) { + if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) { + free(sc->sc_repdesc_ptr, M_USBDEV); + } + } + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static devclass_t uhid_devclass; + +static device_method_t uhid_methods[] = { + DEVMETHOD(device_probe, uhid_probe), + DEVMETHOD(device_attach, uhid_attach), + DEVMETHOD(device_detach, uhid_detach), + {0, 0} +}; + +static driver_t uhid_driver = { + .name = "uhid", + .methods = uhid_methods, + .size = sizeof(struct uhid_softc), +}; + +DRIVER_MODULE(uhid, ushub, uhid_driver, uhid_devclass, NULL, 0); +MODULE_DEPEND(uhid, usb2_input, 1, 1, 1); +MODULE_DEPEND(uhid, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/input/ukbd2.c b/sys/dev/usb2/input/ukbd2.c new file mode 100644 index 000000000000..ed7546558abf --- /dev/null +++ b/sys/dev/usb2/input/ukbd2.c @@ -0,0 +1,1503 @@ +#include +__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 + */ + +#include "opt_compat.h" +#include "opt_kbd.h" +#include "opt_ukbd.h" + +#include +#include +#include +#include + +#define USB_DEBUG_VAR ukbd_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +/* 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 + +/* the following file must be included after "ukbdmap.h" */ +#include + +#if USB_DEBUG +static int ukbd_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd"); +SYSCTL_INT(_hw_usb2_ukbd, OID_AUTO, debug, CTLFLAG_RW, + &ukbd_debug, 0, "Debug level"); +#endif + +#define UPROTO_BOOT_KEYBOARD 1 + +#define UKBD_EMULATE_ATSCANCODE 1 +#define UKBD_DRIVER_NAME "ukbd" +#define UKBD_NMOD 8 /* units */ +#define UKBD_NKEYCODE 6 /* units */ +#define UKBD_N_TRANSFER 3 /* units */ +#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */ +#define UKBD_IN_BUF_FULL (UKBD_IN_BUF_SIZE / 2) /* bytes */ +#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ + +struct ukbd_data { + uint8_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 + uint8_t reserved; + uint8_t keycode[UKBD_NKEYCODE]; +} __packed; + +struct ukbd_softc { + keyboard_t sc_kbd; + keymap_t sc_keymap; + accentmap_t sc_accmap; + fkeytab_t sc_fkeymap[UKBD_NFKEY]; + struct usb2_callout sc_callout; + struct ukbd_data sc_ndata; + struct ukbd_data sc_odata; + + struct usb2_device *sc_udev; + struct usb2_interface *sc_iface; + struct usb2_xfer *sc_xfer[UKBD_N_TRANSFER]; + + uint32_t sc_ntime[UKBD_NKEYCODE]; + uint32_t sc_otime[UKBD_NKEYCODE]; + uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */ + uint32_t sc_time_ms; + uint32_t sc_composed_char; /* composed char code, if non-zero */ +#ifdef UKBD_EMULATE_ATSCANCODE + uint32_t sc_buffered_char[2]; +#endif + uint32_t sc_flags; /* flags */ +#define UKBD_FLAG_COMPOSE 0x0001 +#define UKBD_FLAG_POLLING 0x0002 +#define UKBD_FLAG_SET_LEDS 0x0004 +#define UKBD_FLAG_INTR_STALL 0x0008 +#define UKBD_FLAG_ATTACHED 0x0010 +#define UKBD_FLAG_GONE 0x0020 + + int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ + int32_t sc_state; /* shift/lock key state */ + int32_t sc_accents; /* accent key index (> 0) */ + + uint16_t sc_inputs; + uint16_t sc_inputhead; + uint16_t sc_inputtail; + + uint8_t sc_leds; /* store for async led requests */ + uint8_t sc_iface_index; + uint8_t sc_iface_no; +}; + +#define KEY_ERROR 0x01 + +#define KEY_PRESS 0 +#define KEY_RELEASE 0x400 +#define KEY_INDEX(c) ((c) & 0xFF) + +#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) + +struct ukbd_mods { + uint32_t mask, key; +}; + +static const struct ukbd_mods ukbd_mods[UKBD_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 const uint8_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 */ +}; + +/* prototypes */ +static void ukbd_timeout(void *arg); +static void ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds); +static int ukbd_set_typematic(keyboard_t *kbd, int code); + +#ifdef UKBD_EMULATE_ATSCANCODE +static int +ukbd_key2scan(struct ukbd_softc *sc, int keycode, + int shift, int up); + +#endif +static uint32_t ukbd_read_char(keyboard_t *kbd, int wait); +static void ukbd_clear_state(keyboard_t *kbd); +static int ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg); +static int ukbd_enable(keyboard_t *kbd); +static int ukbd_disable(keyboard_t *kbd); +static void ukbd_interrupt(struct ukbd_softc *sc); + +static device_probe_t ukbd_probe; +static device_attach_t ukbd_attach; +static device_detach_t ukbd_detach; +static device_resume_t ukbd_resume; + +static void +ukbd_put_key(struct ukbd_softc *sc, uint32_t key) +{ + mtx_assert(&Giant, MA_OWNED); + + DPRINTF("0x%02x (%d) %s\n", key, key, + (key & KEY_RELEASE) ? "released" : "pressed"); + + if (sc->sc_inputs < UKBD_IN_BUF_SIZE) { + sc->sc_input[sc->sc_inputtail] = key; + ++(sc->sc_inputs); + ++(sc->sc_inputtail); + if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) { + sc->sc_inputtail = 0; + } + } else { + DPRINTF("input buffer is full\n"); + } + return; +} + +static int32_t +ukbd_get_key(struct ukbd_softc *sc, uint8_t wait) +{ + int32_t c; + + mtx_assert(&Giant, MA_OWNED); + + if (sc->sc_inputs == 0) { + /* start transfer, if not already started */ + usb2_transfer_start(sc->sc_xfer[0]); + } + if (sc->sc_flags & UKBD_FLAG_POLLING) { + DPRINTFN(2, "polling\n"); + + while (sc->sc_inputs == 0) { + + usb2_do_poll(sc->sc_xfer, UKBD_N_TRANSFER); + + DELAY(1000); /* delay 1 ms */ + + sc->sc_time_ms++; + + /* support repetition of keys: */ + + ukbd_interrupt(sc); + + if (!wait) { + break; + } + } + } + if (sc->sc_inputs == 0) { + c = -1; + } else { + c = sc->sc_input[sc->sc_inputhead]; + --(sc->sc_inputs); + ++(sc->sc_inputhead); + if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) { + sc->sc_inputhead = 0; + } + } + return (c); +} + +static void +ukbd_interrupt(struct ukbd_softc *sc) +{ + uint32_t n_mod; + uint32_t o_mod; + uint32_t now = sc->sc_time_ms; + uint32_t dtime; + uint32_t c; + uint8_t key; + uint8_t i; + uint8_t j; + + if (sc->sc_ndata.keycode[0] == KEY_ERROR) { + goto done; + } + n_mod = sc->sc_ndata.modifiers; + o_mod = sc->sc_odata.modifiers; + if (n_mod != o_mod) { + for (i = 0; i < UKBD_NMOD; i++) { + if ((n_mod & ukbd_mods[i].mask) != + (o_mod & ukbd_mods[i].mask)) { + ukbd_put_key(sc, ukbd_mods[i].key | + ((n_mod & ukbd_mods[i].mask) ? + KEY_PRESS : KEY_RELEASE)); + } + } + } + /* Check for released keys. */ + for (i = 0; i < UKBD_NKEYCODE; i++) { + key = sc->sc_odata.keycode[i]; + if (key == 0) { + continue; + } + for (j = 0; j < UKBD_NKEYCODE; j++) { + if (sc->sc_ndata.keycode[j] == 0) { + continue; + } + if (key == sc->sc_ndata.keycode[j]) { + goto rfound; + } + } + ukbd_put_key(sc, key | KEY_RELEASE); +rfound: ; + } + + /* Check for pressed keys. */ + for (i = 0; i < UKBD_NKEYCODE; i++) { + key = sc->sc_ndata.keycode[i]; + if (key == 0) { + continue; + } + sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1; + for (j = 0; j < UKBD_NKEYCODE; j++) { + if (sc->sc_odata.keycode[j] == 0) { + continue; + } + if (key == sc->sc_odata.keycode[j]) { + + /* key is still pressed */ + + sc->sc_ntime[i] = sc->sc_otime[j]; + dtime = (sc->sc_otime[j] - now); + + if (!(dtime & 0x80000000)) { + /* time has not elapsed */ + goto pfound; + } + sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2; + break; + } + } + ukbd_put_key(sc, 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 != UKBD_NKEYCODE; j++) { + if (j != i) + sc->sc_ntime[j] = now + (100 * 1000); + } +pfound: ; + } + + sc->sc_odata = sc->sc_ndata; + + bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime)); + + if (sc->sc_inputs == 0) { + goto done; + } + if (sc->sc_flags & UKBD_FLAG_POLLING) { + goto done; + } + if (KBD_IS_ACTIVE(&sc->sc_kbd) && + KBD_IS_BUSY(&sc->sc_kbd)) { + /* let the callback function process the input */ + (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, + sc->sc_kbd.kb_callback.kc_arg); + } else { + /* read and discard the input, no one is waiting for it */ + do { + c = ukbd_read_char(&sc->sc_kbd, 0); + } while (c != NOKEY); + } +done: + return; +} + +static void +ukbd_timeout(void *arg) +{ + struct ukbd_softc *sc = arg; + + mtx_assert(&Giant, MA_OWNED); + + if (!(sc->sc_flags & UKBD_FLAG_POLLING)) { + sc->sc_time_ms += 25; /* milliseconds */ + } + ukbd_interrupt(sc); + + usb2_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc); + + mtx_unlock(&Giant); + + return; +} + +static void +ukbd_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ukbd_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ukbd_intr_callback(struct usb2_xfer *xfer) +{ + struct ukbd_softc *sc = xfer->priv_sc; + uint16_t len = xfer->actlen; + uint8_t i; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("actlen=%d bytes\n", len); + + if (len > sizeof(sc->sc_ndata)) { + len = sizeof(sc->sc_ndata); + } + if (len) { + bzero(&sc->sc_ndata, sizeof(sc->sc_ndata)); + usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len); +#if USB_DEBUG + if (sc->sc_ndata.modifiers) { + DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers); + } + for (i = 0; i < UKBD_NKEYCODE; i++) { + if (sc->sc_ndata.keycode[i]) { + DPRINTF("[%d] = %d\n", i, sc->sc_ndata.keycode[i]); + } + } +#endif /* USB_DEBUG */ + ukbd_interrupt(sc); + } + case USB_ST_SETUP: + if (sc->sc_flags & UKBD_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[1]); + return; + } + if (sc->sc_inputs < UKBD_IN_BUF_FULL) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } else { + DPRINTF("input queue is full!\n"); + } + return; + + default: /* Error */ + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UKBD_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[1]); + } + return; + } +} + +static void +ukbd_set_leds_callback(struct usb2_xfer *xfer) +{ + struct usb2_device_request req; + uint8_t buf[1]; + struct ukbd_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: + if (sc->sc_flags & UKBD_FLAG_SET_LEDS) { + sc->sc_flags &= ~UKBD_FLAG_SET_LEDS; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, UHID_OUTPUT_REPORT, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + + buf[0] = sc->sc_leds; + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = sizeof(buf); + xfer->nframes = 2; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + DPRINTFN(0, "error=%s\n", usb2_errstr(xfer->error)); + return; + } +} + +static const struct usb2_config ukbd_config[UKBD_N_TRANSFER] = { + + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &ukbd_intr_callback, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ukbd_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request) + 1, + .mh.callback = &ukbd_set_leds_callback, + .mh.timeout = 1000, /* 1 second */ + }, +}; + +static int +ukbd_probe(device_t dev) +{ + keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + DPRINTFN(11, "\n"); + + if (sw == NULL) { + return (ENXIO); + } + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* check that the keyboard speaks the boot protocol: */ + if ((uaa->info.bInterfaceClass == UICLASS_HID) + && (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) + && (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) { + if (usb2_test_quirk(uaa, UQ_KBD_IGNORE)) + return (ENXIO); + else + return (0); + } + return (ENXIO); +} + +static int +ukbd_attach(device_t dev) +{ + struct ukbd_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + int32_t unit = device_get_unit(dev); + keyboard_t *kbd = &sc->sc_kbd; + usb2_error_t err; + uint16_t n; + + if (sc == NULL) { + return (ENOMEM); + } + mtx_assert(&Giant, MA_OWNED); + + kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0); + + kbd->kb_data = (void *)sc; + + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + sc->sc_iface = uaa->iface; + sc->sc_iface_index = uaa->info.bIfaceIndex; + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_mode = K_XLATE; + sc->sc_iface = uaa->iface; + + usb2_callout_init_mtx(&sc->sc_callout, &Giant, + CALLOUT_RETURNUNLOCKED); + + err = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config, + UKBD_N_TRANSFER, sc, &Giant); + + if (err) { + DPRINTF("error=%s\n", usb2_errstr(err)); + goto detach; + } + /* setup default keyboard maps */ + + sc->sc_keymap = key_map; + sc->sc_accmap = accent_map; + for (n = 0; n < UKBD_NFKEY; n++) { + sc->sc_fkeymap[n] = fkey_tab[n]; + } + + kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, + sc->sc_fkeymap, UKBD_NFKEY); + + KBD_FOUND_DEVICE(kbd); + + ukbd_clear_state(kbd); + + /* + * FIXME: set the initial value for lock keys in "sc_state" + * according to the BIOS data? + */ + KBD_PROBE_DONE(kbd); + + /* ignore if SETIDLE fails, hence it is not crucial */ + err = usb2_req_set_idle(sc->sc_udev, &Giant, sc->sc_iface_index, 0, 0); + + ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state); + + KBD_INIT_DONE(kbd); + + if (kbd_register(kbd) < 0) { + goto detach; + } + KBD_CONFIG_DONE(kbd); + + ukbd_enable(kbd); + +#ifdef KBD_INSTALL_CDEV + if (kbd_attach(kbd)) { + goto detach; + } +#endif + sc->sc_flags |= UKBD_FLAG_ATTACHED; + + if (bootverbose) { + genkbd_diag(kbd, bootverbose); + } + /* lock keyboard mutex */ + + mtx_lock(&Giant); + + /* start the keyboard */ + + usb2_transfer_start(sc->sc_xfer[0]); + + /* start the timer */ + + ukbd_timeout(sc); /* will unlock mutex */ + + return (0); /* success */ + +detach: + ukbd_detach(dev); + return (ENXIO); /* error */ +} + +int +ukbd_detach(device_t dev) +{ + struct ukbd_softc *sc = device_get_softc(dev); + int error; + + mtx_assert(&Giant, MA_OWNED); + + DPRINTF("\n"); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + panic("cannot detach polled keyboard!\n"); + } + sc->sc_flags |= UKBD_FLAG_GONE; + + usb2_callout_stop(&sc->sc_callout); + + ukbd_disable(&sc->sc_kbd); + +#ifdef KBD_INSTALL_CDEV + if (sc->sc_flags & UKBD_FLAG_ATTACHED) { + error = kbd_detach(&sc->sc_kbd); + if (error) { + /* usb attach cannot return an error */ + device_printf(dev, "WARNING: kbd_detach() " + "returned non-zero! (ignored)\n"); + } + } +#endif + if (KBD_IS_CONFIGURED(&sc->sc_kbd)) { + error = kbd_unregister(&sc->sc_kbd); + if (error) { + /* usb attach cannot return an error */ + device_printf(dev, "WARNING: kbd_unregister() " + "returned non-zero! (ignored)\n"); + } + } + sc->sc_kbd.kb_flags = 0; + + usb2_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER); + + usb2_callout_drain(&sc->sc_callout); + + DPRINTF("%s: disconnected\n", + device_get_nameunit(dev)); + + return (0); +} + +static int +ukbd_resume(device_t dev) +{ + struct ukbd_softc *sc = device_get_softc(dev); + + mtx_assert(&Giant, MA_OWNED); + + ukbd_clear_state(&sc->sc_kbd); + + return (0); +} + +/* early keyboard probe, not supported */ +static int +ukbd_configure(int flags) +{ + return (0); +} + +/* detect a keyboard, not used */ +static int +ukbd__probe(int unit, void *arg, int flags) +{ + mtx_assert(&Giant, MA_OWNED); + return (ENXIO); +} + +/* reset and initialize the device, not used */ +static int +ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) +{ + mtx_assert(&Giant, MA_OWNED); + return (ENXIO); +} + +/* test the interface to the device, not used */ +static int +ukbd_test_if(keyboard_t *kbd) +{ + mtx_assert(&Giant, MA_OWNED); + return (0); +} + +/* finish using this keyboard, not used */ +static int +ukbd_term(keyboard_t *kbd) +{ + mtx_assert(&Giant, MA_OWNED); + return (ENXIO); +} + +/* keyboard interrupt routine, not used */ +static int +ukbd_intr(keyboard_t *kbd, void *arg) +{ + mtx_assert(&Giant, MA_OWNED); + return (0); +} + +/* lock the access to the keyboard, not used */ +static int +ukbd_lock(keyboard_t *kbd, int lock) +{ + mtx_assert(&Giant, MA_OWNED); + return (1); +} + +/* + * 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) +{ + mtx_assert(&Giant, MA_OWNED); + KBD_ACTIVATE(kbd); + return (0); +} + +/* disallow the access to the device */ +static int +ukbd_disable(keyboard_t *kbd) +{ + mtx_assert(&Giant, MA_OWNED); + KBD_DEACTIVATE(kbd); + return (0); +} + +/* check if data is waiting */ +static int +ukbd_check(keyboard_t *kbd) +{ + struct ukbd_softc *sc = kbd->kb_data; + + if (!mtx_owned(&Giant)) { + return (0); /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + + if (!KBD_IS_ACTIVE(kbd)) { + return (0); + } +#ifdef UKBD_EMULATE_ATSCANCODE + if (sc->sc_buffered_char[0]) { + return (1); + } +#endif + if (sc->sc_inputs > 0) { + return (1); + } + return (0); +} + +/* check if char is waiting */ +static int +ukbd_check_char(keyboard_t *kbd) +{ + struct ukbd_softc *sc = kbd->kb_data; + + if (!mtx_owned(&Giant)) { + return (0); /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + + if (!KBD_IS_ACTIVE(kbd)) { + return (0); + } + if ((sc->sc_composed_char > 0) && + (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { + return (1); + } + return (ukbd_check(kbd)); +} + + +/* read one byte from the keyboard if it's allowed */ +static int +ukbd_read(keyboard_t *kbd, int wait) +{ + struct ukbd_softc *sc = kbd->kb_data; + int32_t usbcode; + +#ifdef UKBD_EMULATE_ATSCANCODE + uint32_t keycode; + uint32_t scancode; + +#endif + + if (!mtx_owned(&Giant)) { + return -1; /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + +#ifdef UKBD_EMULATE_ATSCANCODE + if (sc->sc_buffered_char[0]) { + scancode = sc->sc_buffered_char[0]; + if (scancode & SCAN_PREFIX) { + sc->sc_buffered_char[0] &= ~SCAN_PREFIX; + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } + sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; + sc->sc_buffered_char[1] = 0; + return (scancode); + } +#endif /* UKBD_EMULATE_ATSCANCODE */ + + /* XXX */ + usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); + 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; + } + return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, + (usbcode & KEY_RELEASE))); +#else /* !UKBD_EMULATE_ATSCANCODE */ + return (usbcode); +#endif /* UKBD_EMULATE_ATSCANCODE */ +} + +/* read char from the keyboard */ +static uint32_t +ukbd_read_char(keyboard_t *kbd, int wait) +{ + struct ukbd_softc *sc = kbd->kb_data; + uint32_t action; + uint32_t keycode; + int32_t usbcode; + +#ifdef UKBD_EMULATE_ATSCANCODE + uint32_t scancode; + +#endif + if (!mtx_owned(&Giant)) { + return (NOKEY); /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + +next_code: + + /* do we have a composed char to return ? */ + + if ((sc->sc_composed_char > 0) && + (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { + + action = sc->sc_composed_char; + sc->sc_composed_char = 0; + + if (action > 0xFF) { + goto errkey; + } + goto done; + } +#ifdef UKBD_EMULATE_ATSCANCODE + + /* do we have a pending raw scan code? */ + + if (sc->sc_mode == K_RAW) { + scancode = sc->sc_buffered_char[0]; + if (scancode) { + if (scancode & SCAN_PREFIX) { + sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX); + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } + sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; + sc->sc_buffered_char[1] = 0; + return (scancode); + } + } +#endif /* UKBD_EMULATE_ATSCANCODE */ + + /* see if there is something in the keyboard port */ + /* XXX */ + usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); + 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 (sc->sc_mode == K_RAW) { + return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, + (usbcode & KEY_RELEASE))); + } +#else /* !UKBD_EMULATE_ATSCANCODE */ + + /* return the byte as is for the K_RAW mode */ + if (sc->sc_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 (sc->sc_flags & UKBD_FLAG_COMPOSE) { + sc->sc_flags &= ~UKBD_FLAG_COMPOSE; + + if (sc->sc_composed_char > 0xFF) { + sc->sc_composed_char = 0; + } + } + } else { + if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) { + sc->sc_flags |= UKBD_FLAG_COMPOSE; + sc->sc_composed_char = 0; + } + } + break; + /* XXX: I don't like these... */ + case 0x5c: /* print screen */ + if (sc->sc_flags & ALTS) { + keycode = 0x54; /* sysrq */ + } + break; + case 0x68: /* pause/break */ + if (sc->sc_flags & CTLS) { + keycode = 0x6c; /* break */ + } + break; + } + + /* return the key code in the K_CODE mode */ + if (usbcode & KEY_RELEASE) { + keycode |= SCAN_RELEASE; + } + if (sc->sc_mode == K_CODE) { + return (keycode); + } + /* compose a character code */ + if (sc->sc_flags & UKBD_FLAG_COMPOSE) { + switch (keycode) { + /* key pressed, process it */ + case 0x47: + case 0x48: + case 0x49: /* keypad 7,8,9 */ + sc->sc_composed_char *= 10; + sc->sc_composed_char += keycode - 0x40; + goto check_composed; + + case 0x4B: + case 0x4C: + case 0x4D: /* keypad 4,5,6 */ + sc->sc_composed_char *= 10; + sc->sc_composed_char += keycode - 0x47; + goto check_composed; + + case 0x4F: + case 0x50: + case 0x51: /* keypad 1,2,3 */ + sc->sc_composed_char *= 10; + sc->sc_composed_char += keycode - 0x4E; + goto check_composed; + + case 0x52: /* keypad 0 */ + sc->sc_composed_char *= 10; + goto check_composed; + + /* 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 (sc->sc_composed_char > 0) { + sc->sc_flags &= ~UKBD_FLAG_COMPOSE; + sc->sc_composed_char = 0; + goto errkey; + } + break; + } + } + /* keycode to key action */ + action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), + (keycode & SCAN_RELEASE), + &sc->sc_state, &sc->sc_accents); + if (action == NOKEY) { + goto next_code; + } +done: + return (action); + +check_composed: + if (sc->sc_composed_char <= 0xFF) { + goto next_code; + } +errkey: + return (ERRKEY); +} + +/* some useful control functions */ +static int +ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + /* translate LED_XXX bits into the device specific bits */ + static const uint8_t ledmap[8] = { + 0, 2, 1, 3, 4, 6, 5, 7, + }; + struct ukbd_softc *sc = kbd->kb_data; + int i; + +#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD4) || defined(COMPAT_43) + int ival; + +#endif + if (!mtx_owned(&Giant)) { + /* + * XXX big problem: If scroll lock is pressed and "printf()" + * is called, the CPU will get here, to un-scroll lock the + * keyboard. But if "printf()" acquires the "Giant" lock, + * there will be a locking order reversal problem, so the + * keyboard system must get out of "Giant" first, before the + * CPU can proceed here ... + */ + return (EINVAL); + } + mtx_assert(&Giant, MA_OWNED); + + switch (cmd) { + case KDGKBMODE: /* get keyboard mode */ + *(int *)arg = sc->sc_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 (sc->sc_mode != K_XLATE) { + /* make lock key state and LED state match */ + sc->sc_state &= ~LOCK_MASK; + sc->sc_state |= KBD_LED_VAL(kbd); + } + /* FALLTHROUGH */ + case K_RAW: + case K_CODE: + if (sc->sc_mode != *(int *)arg) { + ukbd_clear_state(kbd); + sc->sc_mode = *(int *)arg; + } + break; + default: + 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 "sc_state" won't be changed */ + if (*(int *)arg & ~LOCK_MASK) { + return (EINVAL); + } + i = *(int *)arg; + /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ + if (sc->sc_mode == K_XLATE && + kbd->kb_keymap->n_keys > ALTGR_OFFSET) { + if (i & ALKED) + i |= CLKED; + else + i &= ~CLKED; + } + if (KBD_HAS_DEVICE(kbd)) { + ukbd_set_leds(sc, ledmap[i & LED_MASK]); + } + KBD_LED_VAL(kbd) = *(int *)arg; + break; + case KDGKBSTATE: /* get lock key state */ + *(int *)arg = sc->sc_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) { + return (EINVAL); + } + sc->sc_state &= ~LOCK_MASK; + sc->sc_state |= *(int *)arg; + + /* set LEDs and quit */ + return (ukbd_ioctl(kbd, KDSETLED, arg)); + + case KDSETREPEAT: /* set keyboard repeat rate (new + * interface) */ + if (!KBD_HAS_DEVICE(kbd)) { + return (0); + } + if (((int *)arg)[1] < 0) { + return (EINVAL); + } + if (((int *)arg)[0] < 0) { + return (EINVAL); + } + if (((int *)arg)[0] < 200) /* 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) */ + return (ukbd_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 */ + sc->sc_accents = 0; + /* FALLTHROUGH */ + default: + return (genkbd_commonioctl(kbd, cmd, arg)); + } + + return (0); +} + +/* clear the internal state of the keyboard */ +static void +ukbd_clear_state(keyboard_t *kbd) +{ + struct ukbd_softc *sc = kbd->kb_data; + + if (!mtx_owned(&Giant)) { + return; /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + + sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING); + sc->sc_state &= LOCK_MASK; /* preserve locking key state */ + sc->sc_accents = 0; + sc->sc_composed_char = 0; +#ifdef UKBD_EMULATE_ATSCANCODE + sc->sc_buffered_char[0] = 0; + sc->sc_buffered_char[1] = 0; +#endif + bzero(&sc->sc_ndata, sizeof(sc->sc_ndata)); + bzero(&sc->sc_odata, sizeof(sc->sc_odata)); + bzero(&sc->sc_ntime, sizeof(sc->sc_ntime)); + bzero(&sc->sc_otime, sizeof(sc->sc_otime)); + return; +} + +/* save the internal state, not used */ +static int +ukbd_get_state(keyboard_t *kbd, void *buf, size_t len) +{ + mtx_assert(&Giant, MA_OWNED); + return (len == 0) ? 1 : -1; +} + +/* set the internal state, not used */ +static int +ukbd_set_state(keyboard_t *kbd, void *buf, size_t len) +{ + mtx_assert(&Giant, MA_OWNED); + return (EINVAL); +} + +static int +ukbd_poll(keyboard_t *kbd, int on) +{ + struct ukbd_softc *sc = kbd->kb_data; + + if (!mtx_owned(&Giant)) { + return (0); /* XXX */ + } + mtx_assert(&Giant, MA_OWNED); + + if (on) { + sc->sc_flags |= UKBD_FLAG_POLLING; + } else { + sc->sc_flags &= ~UKBD_FLAG_POLLING; + } + return (0); +} + +/* local functions */ + +static void +ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds) +{ + DPRINTF("leds=0x%02x\n", leds); + + sc->sc_leds = leds; + sc->sc_flags |= UKBD_FLAG_SET_LEDS; + + /* start transfer, if not already started */ + + usb2_transfer_start(sc->sc_xfer[2]); + + return; +} + +static int +ukbd_set_typematic(keyboard_t *kbd, int code) +{ + static const int delays[] = {250, 500, 750, 1000}; + static const 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 +ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up) +{ + static const 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, + }; + + if ((code >= 89) && (code < (89 + (sizeof(scan) / sizeof(scan[0]))))) { + code = scan[code - 89] | SCAN_PREFIX_E0; + } + /* Pause/Break */ + if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) { + code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL); + } + if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) { + code &= ~SCAN_PREFIX_SHIFT; + } + code |= (up ? SCAN_RELEASE : SCAN_PRESS); + + if (code & SCAN_PREFIX) { + if (code & SCAN_PREFIX_CTL) { + /* Ctrl */ + sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE)); + sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX); + } else if (code & SCAN_PREFIX_SHIFT) { + /* Shift */ + sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE)); + sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT); + } else { + sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX); + sc->sc_buffered_char[1] = 0; + } + return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } + return (code); + +} + +#endif /* UKBD_EMULATE_ATSCANCODE */ + +keyboard_switch_t ukbdsw = { + .probe = &ukbd__probe, + .init = &ukbd_init, + .term = &ukbd_term, + .intr = &ukbd_intr, + .test_if = &ukbd_test_if, + .enable = &ukbd_enable, + .disable = &ukbd_disable, + .read = &ukbd_read, + .check = &ukbd_check, + .read_char = &ukbd_read_char, + .check_char = &ukbd_check_char, + .ioctl = &ukbd_ioctl, + .lock = &ukbd_lock, + .clear_state = &ukbd_clear_state, + .get_state = &ukbd_get_state, + .set_state = &ukbd_set_state, + .get_fkeystr = &genkbd_get_fkeystr, + .poll = &ukbd_poll, + .diag = &genkbd_diag, +}; + +KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure); + +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 (0); +} + +static devclass_t ukbd_devclass; + +static device_method_t ukbd_methods[] = { + DEVMETHOD(device_probe, ukbd_probe), + DEVMETHOD(device_attach, ukbd_attach), + DEVMETHOD(device_detach, ukbd_detach), + DEVMETHOD(device_resume, ukbd_resume), + {0, 0} +}; + +static driver_t ukbd_driver = { + .name = "ukbd", + .methods = ukbd_methods, + .size = sizeof(struct ukbd_softc), +}; + +DRIVER_MODULE(ukbd, ushub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0); +MODULE_DEPEND(ukbd, usb2_input, 1, 1, 1); +MODULE_DEPEND(ukbd, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/input/ums2.c b/sys/dev/usb2/input/ums2.c new file mode 100644 index 000000000000..a04e7c849b0e --- /dev/null +++ b/sys/dev/usb2/input/ums2.c @@ -0,0 +1,911 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +/* + * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR ums_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#if USB_DEBUG +static int ums_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); +SYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW, + &ums_debug, 0, "Debug level"); +#endif + +#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) +#define MOUSE_FLAGS (HIO_RELATIVE) + +#define UMS_BUF_SIZE 8 /* bytes */ +#define UMS_IFQ_MAXLEN 50 /* units */ +#define UMS_N_TRANSFER 2 /* units */ +#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ +#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) + +struct ums_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + struct usb2_callout sc_callout; + struct hid_location sc_loc_w; + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; + struct hid_location sc_loc_t; + struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; + mousehw_t sc_hw; + mousemode_t sc_mode; + mousestatus_t sc_status; + + struct usb2_xfer *sc_xfer[UMS_N_TRANSFER]; + + uint32_t sc_flags; +#define UMS_FLAG_X_AXIS 0x0001 +#define UMS_FLAG_Y_AXIS 0x0002 +#define UMS_FLAG_Z_AXIS 0x0004 +#define UMS_FLAG_T_AXIS 0x0008 +#define UMS_FLAG_SBU 0x0010 /* spurious button up events */ +#define UMS_FLAG_INTR_STALL 0x0020 /* set if transfer error */ +#define UMS_FLAG_REVZ 0x0040 /* Z-axis is reversed */ +#define UMS_FLAG_W_AXIS 0x0080 + + uint8_t sc_buttons; + uint8_t sc_iid; + uint8_t sc_temp[64]; +}; + +static void ums_put_queue_timeout(void *__sc); + +static usb2_callback_t ums_clear_stall_callback; +static usb2_callback_t ums_intr_callback; + +static device_probe_t ums_probe; +static device_attach_t ums_attach; +static device_detach_t ums_detach; + +static usb2_fifo_cmd_t ums_start_read; +static usb2_fifo_cmd_t ums_stop_read; +static usb2_fifo_open_t ums_open; +static usb2_fifo_close_t ums_close; +static usb2_fifo_ioctl_t ums_ioctl; + +static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons); + +static struct usb2_fifo_methods ums_fifo_methods = { + .f_open = &ums_open, + .f_close = &ums_close, + .f_ioctl = &ums_ioctl, + .f_start_read = &ums_start_read, + .f_stop_read = &ums_stop_read, + .basename[0] = "ums", +}; + +static void +ums_put_queue_timeout(void *__sc) +{ + struct ums_softc *sc = __sc; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + ums_put_queue(sc, 0, 0, 0, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ums_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ums_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMS_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ums_intr_callback(struct usb2_xfer *xfer) +{ + struct ums_softc *sc = xfer->priv_sc; + uint8_t *buf = sc->sc_temp; + uint16_t len = xfer->actlen; + int32_t buttons = 0; + int32_t dw; + int32_t dx; + int32_t dy; + int32_t dz; + int32_t dt; + uint8_t i; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); + + if (len > sizeof(sc->sc_temp)) { + DPRINTFN(6, "truncating large packet to %zu bytes\n", + sizeof(sc->sc_temp)); + len = sizeof(sc->sc_temp); + } + if (len == 0) { + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, len); + + DPRINTFN(6, "data = %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, + (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, + (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, + (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); + + /* + * 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_FLAG_T_AXIS so use it as an + * identifier. + * + * + * 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. + * + * We probably should switch to some more official quirk. + */ + if (sc->sc_iid) { + if (sc->sc_flags & UMS_FLAG_T_AXIS) { + if (*buf == 0x02) { + goto tr_setup; + } + } else { + if (*buf != sc->sc_iid) { + goto tr_setup; + } + } + + len--; + buf++; + + } else { + if (sc->sc_flags & UMS_FLAG_SBU) { + if ((*buf == 0x14) || (*buf == 0x15)) { + goto tr_setup; + } + } + } + + dw = (sc->sc_flags & UMS_FLAG_W_AXIS) ? + hid_get_data(buf, len, &sc->sc_loc_w) : 0; + + dx = (sc->sc_flags & UMS_FLAG_X_AXIS) ? + hid_get_data(buf, len, &sc->sc_loc_x) : 0; + + dy = (sc->sc_flags & UMS_FLAG_Y_AXIS) ? + -hid_get_data(buf, len, &sc->sc_loc_y) : 0; + + dz = (sc->sc_flags & UMS_FLAG_Z_AXIS) ? + -hid_get_data(buf, len, &sc->sc_loc_z) : 0; + + if (sc->sc_flags & UMS_FLAG_REVZ) { + dz = -dz; + } + dt = (sc->sc_flags & UMS_FLAG_T_AXIS) ? + -hid_get_data(buf, len, &sc->sc_loc_t): 0; + + for (i = 0; i < sc->sc_buttons; i++) { + if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) { + buttons |= (1 << UMS_BUT(i)); + } + } + + if (dx || dy || dz || dt || dw || + (buttons != sc->sc_status.button)) { + + DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", + dx, dy, dz, dt, dw, buttons); + + sc->sc_status.button = buttons; + sc->sc_status.dx += dx; + sc->sc_status.dy += dy; + sc->sc_status.dz += dz; + /* + * sc->sc_status.dt += dt; + * no way to export this yet + */ + + /* + * 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->sc_flags & UMS_FLAG_SBU) && + (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && + (dw == 0) && (buttons == 0)) { + + usb2_callout_reset(&sc->sc_callout, hz / 20, + &ums_put_queue_timeout, sc); + } else { + + usb2_callout_stop(&sc->sc_callout); + + ums_put_queue(sc, dx, dy, dz, dt, buttons); + } + } + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UMS_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[1]); + } else { + /* check if we can put more data into the FIFO */ + if (usb2_fifo_put_bytes_max( + sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= UMS_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[1]); + } + return; + } +} + +static const struct usb2_config ums_config[UMS_N_TRANSFER] = { + + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &ums_intr_callback, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ums_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static int +ums_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + void *d_ptr; + int32_t error = 0; + uint16_t d_len; + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->iface == NULL) { + return (ENXIO); + } + id = usb2_get_interface_descriptor(uaa->iface); + + if ((id == NULL) || + (id->bInterfaceClass != UICLASS_HID)) { + return (ENXIO); + } + error = usb2_req_get_hid_desc + (uaa->device, &Giant, + &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); + + if (error) { + return (ENXIO); + } + if (hid_is_collection(d_ptr, d_len, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) { + error = 0; + } else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) && + (id->bInterfaceProtocol == UIPROTO_MOUSE)) { + error = 0; + } else { + error = ENXIO; + } + + free(d_ptr, M_TEMP); + return (error); +} + +static int +ums_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ums_softc *sc = device_get_softc(dev); + void *d_ptr = NULL; + int unit = device_get_unit(dev); + int32_t isize; + uint32_t flags; + int32_t err; + uint16_t d_len; + uint8_t i; + + DPRINTFN(11, "sc=%p\n", sc); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_callout, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + /* + * Force the report (non-boot) protocol. + * + * Mice without boot protocol support may choose not to implement + * Set_Protocol at all; Ignore any error. + */ + err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1); + + err = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, + UMS_N_TRANSFER, sc, &sc->sc_mtx); + + if (err) { + DPRINTF("error=%s\n", usb2_errstr(err)); + goto detach; + } + err = usb2_req_get_hid_desc + (uaa->device, &Giant, &d_ptr, + &d_len, M_TEMP, uaa->info.bIfaceIndex); + + if (err) { + device_printf(dev, "error reading report description\n"); + goto detach; + } + if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, &sc->sc_loc_x, &flags)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_X_AXIS; + } + } + if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, &sc->sc_loc_y, &flags)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_Y_AXIS; + } + } + /* Try the wheel first as the Z activator since it's tradition. */ + if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags) || + hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags)) { + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_Z_AXIS; + } + /* + * We might have both a wheel and Z direction, if so put + * put the Z on the W coordinate. + */ + if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, &sc->sc_loc_w, &flags)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_W_AXIS; + } + } + } else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, &sc->sc_loc_z, &flags)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_Z_AXIS; + } + } + /* + * The Microsoft Wireless Intellimouse 2.0 reports it's wheel + * using 0x0048, which is 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(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), + hid_input, &sc->sc_loc_t, &flags)) { + + sc->sc_loc_t.pos += 8; + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + sc->sc_flags |= UMS_FLAG_T_AXIS; + } + } + /* figure out the number of buttons */ + + for (i = 0; i < UMS_BUTTON_MAX; i++) { + if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)), + hid_input, &sc->sc_loc_btn[i], NULL)) { + break; + } + } + + sc->sc_buttons = i; + + isize = hid_report_size(d_ptr, d_len, 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 (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) { + sc->sc_flags = (UMS_FLAG_X_AXIS | + UMS_FLAG_Y_AXIS | + UMS_FLAG_Z_AXIS | + UMS_FLAG_SBU); + sc->sc_buttons = 3; + 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->info.idVendor == USB_VENDOR_MICROSOFT) && + (uaa->info.idProduct == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3)) { + sc->sc_flags = (UMS_FLAG_X_AXIS | + UMS_FLAG_Y_AXIS | + UMS_FLAG_Z_AXIS); + sc->sc_buttons = 3; + 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; + } + if (usb2_test_quirk(uaa, UQ_MS_REVZ)) { + /* Some wheels need the Z axis reversed. */ + sc->sc_flags |= UMS_FLAG_REVZ; + } + if (isize > sc->sc_xfer[0]->max_frame_size) { + DPRINTF("WARNING: report size, %d bytes, is larger " + "than interrupt size, %d bytes!\n", + isize, sc->sc_xfer[0]->max_frame_size); + } + /* announce information about the mouse */ + + device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n", + (sc->sc_buttons), + (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", + (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", + (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", + (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", + (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : ""); + + free(d_ptr, M_TEMP); + d_ptr = NULL; + +#if USB_DEBUG + DPRINTF("sc=%p\n", sc); + DPRINTF("X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size); + DPRINTF("Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size); + DPRINTF("Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size); + DPRINTF("T\t%d/%d\n", sc->sc_loc_t.pos, sc->sc_loc_t.size); + DPRINTF("W\t%d/%d\n", sc->sc_loc_w.pos, sc->sc_loc_w.size); + + for (i = 0; i < sc->sc_buttons; i++) { + DPRINTF("B%d\t%d/%d\n", + i + 1, sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size); + } + DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); +#endif + + if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + else + sc->sc_hw.buttons = sc->sc_buttons; + + sc->sc_hw.iftype = MOUSE_IF_USB; + sc->sc_hw.type = MOUSE_MOUSE; + sc->sc_hw.model = MOUSE_MODEL_GENERIC; + sc->sc_hw.hwid = 0; + + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.rate = -1; + sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; + sc->sc_mode.accelfactor = 0; + sc->sc_mode.level = 0; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + + sc->sc_status.flags = 0; + sc->sc_status.button = 0; + sc->sc_status.obutton = 0; + sc->sc_status.dx = 0; + sc->sc_status.dy = 0; + sc->sc_status.dz = 0; + + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &ums_fifo_methods, &sc->sc_fifo, + unit, 0 - 1, uaa->info.bIfaceIndex); + if (err) { + goto detach; + } + return (0); + +detach: + if (d_ptr) { + free(d_ptr, M_TEMP); + } + ums_detach(dev); + return (ENOMEM); +} + +static int +ums_detach(device_t self) +{ + struct ums_softc *sc = device_get_softc(self); + + DPRINTF("sc=%p\n", sc); + + usb2_fifo_detach(&sc->sc_fifo); + + usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); + + usb2_callout_drain(&sc->sc_callout); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +ums_start_read(struct usb2_fifo *fifo) +{ + struct ums_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ums_stop_read(struct usb2_fifo *fifo) +{ + struct ums_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_callout_stop(&sc->sc_callout); + return; +} + + +#if ((MOUSE_SYS_PACKETSIZE != 8) || \ + (MOUSE_MSC_PACKETSIZE != 5)) +#error "Software assumptions are not met. Please update code." +#endif + +static void +ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, + int32_t dz, int32_t dt, int32_t buttons) +{ + uint8_t buf[8]; + + if (1) { + + 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; + + buf[0] = sc->sc_mode.syncmask[1]; + buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; + buf[1] = dx >> 1; + buf[2] = dy >> 1; + buf[3] = dx - (dx >> 1); + buf[4] = dy - (dy >> 1); + + if (sc->sc_mode.level == 1) { + buf[5] = dz >> 1; + buf[6] = dz - (dz >> 1); + buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); + } + usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, + sc->sc_mode.packetsize, 1); + + } else { + DPRINTF("Buffer full, discarded packet\n"); + } + + return; +} + +static void +ums_reset_buf(struct ums_softc *sc) +{ + /* reset read queue */ + usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); + return; +} + +static int +ums_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ums_softc *sc = fifo->priv_sc0; + + DPRINTFN(2, "\n"); + + if (fflags & FREAD) { + + /* reset status */ + + sc->sc_status.flags = 0; + sc->sc_status.button = 0; + sc->sc_status.obutton = 0; + sc->sc_status.dx = 0; + sc->sc_status.dy = 0; + sc->sc_status.dz = 0; + /* sc->sc_status.dt = 0; */ + + if (usb2_fifo_alloc_buffer(fifo, + UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + return (0); +} + +static void +ums_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & FREAD) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +static int +ums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, + int fflags, struct thread *td) +{ + struct ums_softc *sc = fifo->priv_sc0; + mousemode_t mode; + int error = 0; + + DPRINTFN(2, "\n"); + + mtx_lock(&sc->sc_mtx); + + switch (cmd) { + case MOUSE_GETHWINFO: + *(mousehw_t *)addr = sc->sc_hw; + break; + + case MOUSE_GETMODE: + *(mousemode_t *)addr = sc->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)) { + error = EINVAL; + goto done; + } else { + sc->sc_mode.level = mode.level; + } + + if (sc->sc_mode.level == 0) { + if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + else + sc->sc_hw.buttons = sc->sc_buttons; + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + else + sc->sc_hw.buttons = sc->sc_buttons; + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + ums_reset_buf(sc); + break; + + case MOUSE_GETLEVEL: + *(int *)addr = sc->sc_mode.level; + break; + + case MOUSE_SETLEVEL: + if (*(int *)addr < 0 || *(int *)addr > 1) { + error = EINVAL; + goto done; + } + sc->sc_mode.level = *(int *)addr; + + if (sc->sc_mode.level == 0) { + if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + else + sc->sc_hw.buttons = sc->sc_buttons; + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + else + sc->sc_hw.buttons = sc->sc_buttons; + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + ums_reset_buf(sc); + break; + + case MOUSE_GETSTATUS:{ + mousestatus_t *status = (mousestatus_t *)addr; + + *status = sc->sc_status; + sc->sc_status.obutton = sc->sc_status.button; + sc->sc_status.button = 0; + sc->sc_status.dx = 0; + sc->sc_status.dy = 0; + sc->sc_status.dz = 0; + /* sc->sc_status.dt = 0; */ + + 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; + } + +done: + mtx_unlock(&sc->sc_mtx); + return (error); +} + +static devclass_t ums_devclass; + +static device_method_t ums_methods[] = { + DEVMETHOD(device_probe, ums_probe), + DEVMETHOD(device_attach, ums_attach), + DEVMETHOD(device_detach, ums_detach), + {0, 0} +}; + +static driver_t ums_driver = { + .name = "ums", + .methods = ums_methods, + .size = sizeof(struct ums_softc), +}; + +DRIVER_MODULE(ums, ushub, ums_driver, ums_devclass, NULL, 0); +MODULE_DEPEND(ums, usb2_input, 1, 1, 1); +MODULE_DEPEND(ums, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/input/usb2_input.c b/sys/dev/usb2/input/usb2_input.c new file mode 100644 index 000000000000..56f9ff229f15 --- /dev/null +++ b/sys/dev/usb2/input/usb2_input.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_input, 1); +MODULE_DEPEND(usb2_input, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/input/usb2_input.h b/sys/dev/usb2/input/usb2_input.h new file mode 100644 index 000000000000..0b5185374dc5 --- /dev/null +++ b/sys/dev/usb2/input/usb2_input.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_INPUT_H_ +#define _USB2_INPUT_H_ + +#endif /* _USB2_INPUT_H_ */ diff --git a/sys/dev/usb2/input/usb2_rdesc.h b/sys/dev/usb2/input/usb2_rdesc.h new file mode 100644 index 000000000000..9f4363dcfb68 --- /dev/null +++ b/sys/dev/usb2/input/usb2_rdesc.h @@ -0,0 +1,276 @@ +/*- + * Copyright (c) 2000 Nick Hibma + * All rights reserved. + * + * Copyright (c) 2005 Ed Schouten + * 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$ + * + * This file contains replacements for broken HID report descriptors. + */ + +#define 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 */\ + +#define 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 */\ + +/* + * The descriptor has no output report format, thus preventing you from + * controlling the LEDs and the built-in rumblers. + */ +#define 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 */\ + diff --git a/sys/dev/usb2/misc/udbp2.c b/sys/dev/usb2/misc/udbp2.c new file mode 100644 index 000000000000..d43386879734 --- /dev/null +++ b/sys/dev/usb2/misc/udbp2.c @@ -0,0 +1,861 @@ +/*- + * 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 +__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 + * streaming, and <5k to avoid overflowing the system with small TDs). + */ + + +/* probe/attach/detach: + * Connect the driver to the hardware and netgraph + * + * 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 +#include +#include +#include + +#define USB_DEBUG_VAR udbp_debug + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int udbp_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); +SYSCTL_INT(_hw_usb2_udbp, OID_AUTO, debug, CTLFLAG_RW, + &udbp_debug, 0, "udbp debug level"); +#endif + +#define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in + * msecs */ +#define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one + * transfer */ +#define UDBP_T_WR 0 +#define UDBP_T_RD 1 +#define UDBP_T_WR_CS 2 +#define UDBP_T_RD_CS 3 +#define UDBP_T_MAX 4 +#define UDBP_Q_MAXLEN 50 + +struct udbp_softc { + + struct mtx sc_mtx; + struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ + struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ + + struct usb2_xfer *sc_xfer[UDBP_T_MAX]; + node_p sc_node; /* back pointer to node */ + hook_p sc_hook; /* pointer to the hook */ + struct mbuf *sc_bulk_in_buffer; + + uint32_t sc_packets_in; /* packets in from downstream */ + uint32_t sc_packets_out; /* packets out towards downstream */ + + uint8_t sc_flags; +#define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ +#define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ + + uint8_t sc_name[16]; +}; + +/* prototypes */ + +static int udbp_modload(module_t mod, int event, void *data); + +static device_probe_t udbp_probe; +static device_attach_t udbp_attach; +static device_detach_t udbp_detach; + +static usb2_callback_t udbp_bulk_read_callback; +static usb2_callback_t udbp_bulk_read_clear_stall_callback; +static usb2_callback_t udbp_bulk_write_callback; +static usb2_callback_t udbp_bulk_write_clear_stall_callback; + +static void udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2); + +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, +}; + +/* USB config */ +static const struct usb2_config udbp_config[UDBP_T_MAX] = { + + [UDBP_T_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UDBP_BUFFERSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &udbp_bulk_write_callback, + .mh.timeout = UDBP_TIMEOUT, + }, + + [UDBP_T_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UDBP_BUFFERSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &udbp_bulk_read_callback, + }, + + [UDBP_T_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &udbp_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [UDBP_T_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &udbp_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t udbp_devclass; + +static device_method_t udbp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, udbp_probe), + DEVMETHOD(device_attach, udbp_attach), + DEVMETHOD(device_detach, udbp_detach), + {0, 0} +}; + +static driver_t udbp_driver = { + .name = "udbp", + .methods = udbp_methods, + .size = sizeof(struct udbp_softc), +}; + +DRIVER_MODULE(udbp, ushub, udbp_driver, udbp_devclass, udbp_modload, 0); +MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +MODULE_DEPEND(udbp, usb2_misc, 1, 1, 1); +MODULE_DEPEND(udbp, usb2_core, 1, 1, 1); + +static int +udbp_modload(module_t mod, int event, void *data) +{ + int error; + + switch (event) { + case MOD_LOAD: + error = ng_newtype(&ng_udbp_typestruct); + if (error != 0) { + printf("%s: Could not register " + "Netgraph node type, error=%d\n", + NG_UDBP_NODE_TYPE, error); + } + break; + + case MOD_UNLOAD: + error = ng_rmtype(&ng_udbp_typestruct); + break; + + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +static int +udbp_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* + * 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->info.idVendor == USB_VENDOR_NETCHIP) && + (uaa->info.idProduct == USB_PRODUCT_NETCHIP_TURBOCONNECT))) + return (0); + + if (((uaa->info.idVendor == USB_VENDOR_PROLIFIC) && + ((uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2301) || + (uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2302)))) + return (0); + + if ((uaa->info.idVendor == USB_VENDOR_ANCHOR) && + (uaa->info.idProduct == USB_PRODUCT_ANCHOR_EZLINK)) + return (0); + + if ((uaa->info.idVendor == USB_VENDOR_GENESYS) && + (uaa->info.idProduct == USB_PRODUCT_GENESYS_GL620USB)) + return (0); + + return (ENXIO); +} + +static int +udbp_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct udbp_softc *sc = device_get_softc(dev); + int error; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); + + error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, + sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); + if (error) { + DPRINTF("error=%s\n", usb2_errstr(error)); + goto detach; + } + NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); + + NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); + + /* create Netgraph node */ + + if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { + printf("%s: Could not create Netgraph node\n", + sc->sc_name); + sc->sc_node = NULL; + goto detach; + } + /* name node */ + + if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { + printf("%s: Could not name node\n", + sc->sc_name); + NG_NODE_UNREF(sc->sc_node); + sc->sc_node = NULL; + goto detach; + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + + /* the device is now operational */ + + return (0); /* success */ + +detach: + udbp_detach(dev); + return (ENOMEM); /* failure */ +} + +static int +udbp_detach(device_t dev) +{ + struct udbp_softc *sc = device_get_softc(dev); + + /* destroy Netgraph node */ + + if (sc->sc_node != NULL) { + NG_NODE_SET_PRIVATE(sc->sc_node, NULL); + ng_rmnode_self(sc->sc_node); + sc->sc_node = NULL; + } + /* free USB transfers, if any */ + + usb2_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); + + mtx_destroy(&sc->sc_mtx); + + /* destroy queues */ + + NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); + NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); + + /* extra check */ + + if (sc->sc_bulk_in_buffer) { + m_freem(sc->sc_bulk_in_buffer); + sc->sc_bulk_in_buffer = NULL; + } + return (0); /* success */ +} + +static void +udbp_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct udbp_softc *sc = xfer->priv_sc; + struct mbuf *m; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* allocate new mbuf */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) { + goto tr_setup; + } + MCLGET(m, M_DONTWAIT); + + if (!(m->m_flags & M_EXT)) { + m_freem(m); + goto tr_setup; + } + m->m_pkthdr.len = m->m_len = xfer->actlen; + + usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen); + + sc->sc_bulk_in_buffer = m; + + DPRINTF("received package %d " + "bytes\n", xfer->actlen); + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_bulk_in_buffer) { + ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); + return; + } + if (sc->sc_flags & UDBP_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); + return; + } + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UDBP_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); + } + return; + + } +} + +static void +udbp_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct udbp_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UDBP_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(node); + struct mbuf *m; + int error; + + if (sc == NULL) { + return; + } + mtx_lock(&sc->sc_mtx); + + m = sc->sc_bulk_in_buffer; + + if (m) { + + sc->sc_bulk_in_buffer = NULL; + + if ((sc->sc_hook == NULL) || + NG_HOOK_NOT_VALID(sc->sc_hook)) { + DPRINTF("No upstream hook\n"); + goto done; + } + sc->sc_packets_in++; + + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + + m = NULL; + } +done: + if (m) { + m_freem(m); + } + /* start USB bulk-in transfer, if not already started */ + + usb2_transfer_start(sc->sc_xfer[UDBP_T_RD]); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +udbp_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct udbp_softc *sc = xfer->priv_sc; + struct mbuf *m; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + sc->sc_packets_out++; + + case USB_ST_SETUP: + if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); + return; + } + /* get next mbuf, if any */ + + NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); + if (m == NULL) { + NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); + if (m == NULL) { + DPRINTF("Data queue is empty\n"); + return; + } + } + if (m->m_pkthdr.len > MCLBYTES) { + DPRINTF("truncating large packet " + "from %d to %d bytes\n", m->m_pkthdr.len, + MCLBYTES); + m->m_pkthdr.len = MCLBYTES; + } + usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); + + xfer->frlengths[0] = m->m_pkthdr.len; + + m_freem(m); + + DPRINTF("packet out: %d bytes\n", + xfer->frlengths[0]); + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= UDBP_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); + } + return; + + } +} + +static void +udbp_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct udbp_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/*********************************************************************** + * 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) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(node); + int32_t error = 0; + + if (strcmp(name, NG_UDBP_HOOK_NAME)) { + return (EINVAL); + } + mtx_lock(&sc->sc_mtx); + + if (sc->sc_hook != NULL) { + error = EISCONN; + } else { + sc->sc_hook = hook; + NG_HOOK_SET_PRIVATE(hook, NULL); + } + + mtx_unlock(&sc->sc_mtx); + + return (error); +} + +/* + * 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) +{ + struct udbp_softc *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; + mtx_lock(&sc->sc_mtx); + stats->packets_in = sc->sc_packets_in; + stats->packets_out = sc->sc_packets_out; + mtx_unlock(&sc->sc_mtx); + break; + } + case NGM_UDBP_SET_FLAG: + if (msg->header.arglen != sizeof(uint32_t)) { + error = EINVAL; + break; + } + DPRINTF("flags = 0x%08x\n", + *((uint32_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) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct ng_bt_mbufq *queue_ptr; + struct mbuf *m; + struct ng_tag_prio *ptag; + int error; + + if (sc == NULL) { + NG_FREE_ITEM(item); + return (EHOSTDOWN); + } + NGI_GET_M(item, m); + NG_FREE_ITEM(item); + + /* + * Now queue the data for when it can be sent + */ + ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL); + + if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) + queue_ptr = &sc->sc_xmitq_hipri; + else + queue_ptr = &sc->sc_xmitq; + + mtx_lock(&sc->sc_mtx); + + if (NG_BT_MBUFQ_FULL(queue_ptr)) { + NG_BT_MBUFQ_DROP(queue_ptr); + NG_FREE_M(m); + error = ENOBUFS; + } else { + NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); + /* + * start bulk-out transfer, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[UDBP_T_WR]); + error = 0; + } + + mtx_unlock(&sc->sc_mtx); + + 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) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(node); + + /* Let old node go */ + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); /* forget it ever existed */ + + if (sc == NULL) { + goto done; + } + /* Create Netgraph node */ + if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { + printf("%s: Could not create Netgraph node\n", + sc->sc_name); + sc->sc_node = NULL; + goto done; + } + /* Name node */ + if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { + printf("%s: Could not name Netgraph node\n", + sc->sc_name); + NG_NODE_UNREF(sc->sc_node); + sc->sc_node = NULL; + goto done; + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + +done: + if (sc) { + mtx_unlock(&sc->sc_mtx); + } + return (0); +} + +/* + * 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) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + /* probably not at splnet, force outward queueing */ + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + + mtx_lock(&sc->sc_mtx); + + sc->sc_flags |= (UDBP_FLAG_READ_STALL | + UDBP_FLAG_WRITE_STALL); + + /* start bulk-in transfer */ + usb2_transfer_start(sc->sc_xfer[UDBP_T_RD]); + + /* start bulk-out transfer */ + usb2_transfer_start(sc->sc_xfer[UDBP_T_WR]); + + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +/* + * Dook disconnection + * + * For this type, removal of the last link destroys the node + */ +static int +ng_udbp_disconnect(hook_p hook) +{ + struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error = 0; + + if (sc != NULL) { + + mtx_lock(&sc->sc_mtx); + + if (hook != sc->sc_hook) { + error = EINVAL; + } else { + + /* stop bulk-in transfer */ + usb2_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); + usb2_transfer_stop(sc->sc_xfer[UDBP_T_RD]); + + /* stop bulk-out transfer */ + usb2_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); + usb2_transfer_stop(sc->sc_xfer[UDBP_T_WR]); + + /* cleanup queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); + NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); + + if (sc->sc_bulk_in_buffer) { + m_freem(sc->sc_bulk_in_buffer); + sc->sc_bulk_in_buffer = NULL; + } + sc->sc_hook = NULL; + } + + mtx_unlock(&sc->sc_mtx); + } + 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 (error); +} diff --git a/sys/dev/usb2/misc/udbp2.h b/sys/dev/usb2/misc/udbp2.h new file mode 100644 index 000000000000..e6fd85326152 --- /dev/null +++ b/sys/dev/usb2/misc/udbp2.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 { + uint32_t packets_in; /* packets in from downstream */ + uint32_t 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/dev/usb2/misc/ufm2.c b/sys/dev/usb2/misc/ufm2.c new file mode 100644 index 000000000000..ba24ca518186 --- /dev/null +++ b/sys/dev/usb2/misc/ufm2.c @@ -0,0 +1,336 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UFM_CMD0 0x00 +#define UFM_CMD_SET_FREQ 0x01 +#define UFM_CMD2 0x02 + +struct ufm_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + + struct usb2_device *sc_udev; + + uint32_t sc_unit; + uint32_t sc_freq; + + uint8_t sc_name[16]; +}; + +/* prototypes */ + +static device_probe_t ufm_probe; +static device_attach_t ufm_attach; +static device_detach_t ufm_detach; + +static usb2_fifo_ioctl_t ufm_ioctl; +static usb2_fifo_open_t ufm_open; + +static struct usb2_fifo_methods ufm_fifo_methods = { + .f_ioctl = &ufm_ioctl, + .f_open = &ufm_open, + .basename[0] = "ufm", +}; + +static int ufm_do_req(struct ufm_softc *sc, uint8_t request, uint16_t value, uint16_t index, uint8_t *retbuf); +static int ufm_set_freq(struct ufm_softc *sc, void *addr); +static int ufm_get_freq(struct ufm_softc *sc, void *addr); +static int ufm_start(struct ufm_softc *sc, void *addr); +static int ufm_stop(struct ufm_softc *sc, void *addr); +static int ufm_get_stat(struct ufm_softc *sc, void *addr); + +static devclass_t ufm_devclass; + +static device_method_t ufm_methods[] = { + DEVMETHOD(device_probe, ufm_probe), + DEVMETHOD(device_attach, ufm_attach), + DEVMETHOD(device_detach, ufm_detach), + {0, 0} +}; + +static driver_t ufm_driver = { + .name = "ufm", + .methods = ufm_methods, + .size = sizeof(struct ufm_softc), +}; + +MODULE_DEPEND(ufm, usb2_misc, 1, 1, 1); +DRIVER_MODULE(ufm, ushub, ufm_driver, ufm_devclass, NULL, 0); +MODULE_DEPEND(ufm, usb2_core, 1, 1, 1); + +static int +ufm_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if ((uaa->info.idVendor == USB_VENDOR_CYPRESS) && + (uaa->info.idProduct == USB_PRODUCT_CYPRESS_FMRADIO)) { + return (0); + } + return (ENXIO); +} + +static int +ufm_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ufm_softc *sc = device_get_softc(dev); + int error; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_unit = device_get_unit(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + mtx_init(&sc->sc_mtx, "ufm lock", NULL, MTX_DEF | MTX_RECURSE); + + device_set_usb2_desc(dev); + + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &ufm_fifo_methods, &sc->sc_fifo, + device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + return (0); /* success */ + +detach: + ufm_detach(dev); + return (ENXIO); +} + +static int +ufm_detach(device_t dev) +{ + struct ufm_softc *sc = device_get_softc(dev); + + usb2_fifo_detach(&sc->sc_fifo); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static int +ufm_open(struct usb2_fifo *dev, int fflags, struct thread *td) +{ + if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) { + return (EACCES); + } + return (0); +} + +static int +ufm_do_req(struct ufm_softc *sc, uint8_t request, + uint16_t value, uint16_t index, uint8_t *retbuf) +{ + int error; + + struct usb2_device_request req; + uint8_t buf[1]; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = request; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, 1); + + error = usb2_do_request(sc->sc_udev, NULL, &req, buf); + + if (retbuf) { + *retbuf = buf[0]; + } + if (error) { + return (ENXIO); + } + return (0); +} + +static int +ufm_set_freq(struct ufm_softc *sc, void *addr) +{ + int freq = *(int *)addr; + + /* + * 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. + */ + mtx_lock(&sc->sc_mtx); + sc->sc_freq = freq; + mtx_unlock(&sc->sc_mtx); + + freq = (freq + 10700001) / 12500; + + /* This appears to set the frequency */ + if (ufm_do_req(sc, UFM_CMD_SET_FREQ, + freq >> 8, freq, NULL) != 0) { + return (EIO); + } + /* Not sure what this does */ + if (ufm_do_req(sc, UFM_CMD0, + 0x96, 0xb7, NULL) != 0) { + return (EIO); + } + return (0); +} + +static int +ufm_get_freq(struct ufm_softc *sc, void *addr) +{ + int *valp = (int *)addr; + + mtx_lock(&sc->sc_mtx); + *valp = sc->sc_freq; + mtx_unlock(&sc->sc_mtx); + return (0); +} + +static int +ufm_start(struct ufm_softc *sc, void *addr) +{ + uint8_t ret; + + if (ufm_do_req(sc, UFM_CMD0, + 0x00, 0xc7, &ret)) { + return (EIO); + } + if (ufm_do_req(sc, UFM_CMD2, + 0x01, 0x00, &ret)) { + return (EIO); + } + if (ret & 0x1) { + return (EIO); + } + return (0); +} + +static int +ufm_stop(struct ufm_softc *sc, void *addr) +{ + if (ufm_do_req(sc, UFM_CMD0, + 0x16, 0x1C, NULL)) { + return (EIO); + } + if (ufm_do_req(sc, UFM_CMD2, + 0x00, 0x00, NULL)) { + return (EIO); + } + return (0); +} + +static int +ufm_get_stat(struct ufm_softc *sc, void *addr) +{ + uint8_t ret; + + /* + * Note, there's a 240ms settle time before the status + * will be valid, so sleep that amount. + */ + + mtx_lock(&sc->sc_mtx); + usb2_pause_mtx(&sc->sc_mtx, USB_MS_HZ / 4); + mtx_unlock(&sc->sc_mtx); + + if (ufm_do_req(sc, UFM_CMD0, + 0x00, 0x24, &ret)) { + return (EIO); + } + *(int *)addr = ret; + + return (0); +} + +static int +ufm_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, + int fflags, struct thread *td) +{ + struct ufm_softc *sc = fifo->priv_sc0; + int error = 0; + + 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: + error = ENOTTY; + break; + } + return (error); +} diff --git a/sys/dev/usb2/misc/usb2_misc.c b/sys/dev/usb2/misc/usb2_misc.c new file mode 100644 index 000000000000..74eb6c52435d --- /dev/null +++ b/sys/dev/usb2/misc/usb2_misc.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_misc, 1); +MODULE_DEPEND(usb2_misc, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/misc/usb2_misc.h b/sys/dev/usb2/misc/usb2_misc.h new file mode 100644 index 000000000000..dabf9e340748 --- /dev/null +++ b/sys/dev/usb2/misc/usb2_misc.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_MISC_H_ +#define _USB2_MISC_H_ + +#endif /* _USB2_MISC_H_ */ diff --git a/sys/dev/usb2/ndis/if_ndis_usb2.c b/sys/dev/usb2/ndis/if_ndis_usb2.c new file mode 100644 index 000000000000..3e2aaa2a6c63 --- /dev/null +++ b/sys/dev/usb2/ndis/if_ndis_usb2.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +MODULE_DEPEND(ndis, usb2_ndis, 1, 1, 1); +MODULE_DEPEND(ndis, usb2_core, 1, 1, 1); +MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); +MODULE_DEPEND(ndis, if_ndis, 1, 1, 1); + +static device_probe_t ndisusb2_probe; +static device_attach_t ndisusb2_attach; +static struct resource_list *ndis_get_resource_list(device_t, device_t); + +extern device_attach_t ndis_attach; +extern device_shutdown_t ndis_shutdown; +extern device_detach_t ndis_detach; +extern device_suspend_t ndis_suspend; +extern device_resume_t ndis_resume; +extern int ndisdrv_modevent(module_t, int, void *); + +extern unsigned char drv_data[]; + +static device_method_t ndis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ndisusb2_probe), + DEVMETHOD(device_attach, ndisusb2_attach), + DEVMETHOD(device_detach, ndis_detach), + DEVMETHOD(device_shutdown, ndis_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_get_resource_list, ndis_get_resource_list), + + {0, 0} +}; + +static driver_t ndis_driver = { + "ndis", + ndis_methods, + sizeof(struct ndis_softc) +}; + +static devclass_t ndis_devclass; + +DRIVER_MODULE(ndis, ushub, ndis_driver, ndis_devclass, ndisdrv_modevent, 0); + +static int +ndisusb2_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (windrv_lookup(0, "USB Bus") == NULL) { + return (ENXIO); + } + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + return (ENXIO); +} + +static int +ndisusb2_attach(device_t dev) +{ + struct ndis_softc *sc = device_get_softc(dev); + driver_object *drv; + + sc->ndis_dev = dev; + + /* Create PDO for this device instance */ + + drv = windrv_lookup(0, "USB Bus"); + windrv_create_pdo(drv, dev); + + if (ndis_attach(dev) != 0) { + return (ENXIO); + } + return (0); /* success */ +} + +static struct resource_list * +ndis_get_resource_list(device_t dev, device_t child) +{ + struct ndis_softc *sc = device_get_softc(dev); + + return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); +} diff --git a/sys/dev/usb2/ndis/usb2_ndis.c b/sys/dev/usb2/ndis/usb2_ndis.c new file mode 100644 index 000000000000..1776e0d1cfc6 --- /dev/null +++ b/sys/dev/usb2/ndis/usb2_ndis.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_ndis, 1); +MODULE_DEPEND(usb2_ndis, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/ndis/usb2_ndis.h b/sys/dev/usb2/ndis/usb2_ndis.h new file mode 100644 index 000000000000..7f187e1fc6e4 --- /dev/null +++ b/sys/dev/usb2/ndis/usb2_ndis.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_NDIS_H_ +#define _USB2_NDIS_H_ + +#endif /* _USB2_NDIS_H_ */ diff --git a/sys/dev/usb2/quirk/usb2_quirk.c b/sys/dev/usb2/quirk/usb2_quirk.c new file mode 100644 index 000000000000..dbaaa6a59234 --- /dev/null +++ b/sys/dev/usb2/quirk/usb2_quirk.c @@ -0,0 +1,372 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. All rights reserved. + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include + +#include + +MODULE_DEPEND(usb2_quirk, usb2_core, 1, 1, 1); +MODULE_VERSION(usb2_quirk, 1); + +/* + * The following macro adds one or more quirks for a USB device: + */ +#define USB_QUIRK_ENTRY(v,p,l,h,...) \ + .vid = (v), .pid = (p), .lo_rev = (l), .hi_rev = (h), .quirks = { __VA_ARGS__ } + +#define USB_DEV_QUIRKS_MAX 128 +#define USB_SUB_QUIRKS_MAX 8 + +struct usb2_quirk_entry { + uint16_t vid; + uint16_t pid; + uint16_t lo_rev; + uint16_t hi_rev; + uint16_t quirks[USB_SUB_QUIRKS_MAX]; +}; + +static struct mtx usb2_quirk_mtx; + +static struct usb2_quirk_entry usb2_quirks[USB_DEV_QUIRKS_MAX] = { + {USB_QUIRK_ENTRY(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_LCM, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 0x094, 0x094, UQ_SWAP_UNICODE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 0x0a2, UQ_BAD_ADC, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 0x0a2, UQ_AU_NO_XU, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, 0x103, UQ_BAD_ADC, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, 0x000, UQ_BAD_AUDIO, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, 0x110, UQ_SPUR_BUT_UP, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, 0x001, UQ_SPUR_BUT_UP, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, 0x110, UQ_POWER_CLAIM, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, 0x009, UQ_AU_NO_FRAC, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN53B, 0x0000, 0xFFFF, UQ_NO_STRINGS, UQ_NONE)}, + + /* + * XXX The following quirks should have a more specific revision + * number: + */ + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_895C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_880C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_815C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_810C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_830C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_1220C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_XEROX, USB_PRODUCT_XEROX_WCM15, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)}, + /* Devices which should be ignored by uhid */ + {USB_QUIRK_ENTRY(USB_VENDOR_APC, USB_PRODUCT_APC_UPS, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_1500CAVRLCD, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD2X20, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)}, + /* Devices which should be ignored by both ukbd and uhid */ + {USB_QUIRK_ENTRY(USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY1A, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY1B, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_TENX, USB_PRODUCT_TENX_UAUDIO0, 0x0101, 0x0101, UQ_AUDIO_SWAP_LR, UQ_NONE)}, + /* MS keyboards do weird things */ + {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, UQ_MS_LEADING_BYTE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK2, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, UQ_MS_LEADING_BYTE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE, 0x0000, 0xFFFF, UQ_MS_LEADING_BYTE, UQ_NONE)}, + {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY24X, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)}, +}; + +USB_MAKE_DEBUG_TABLE(USB_QUIRK); + +/*------------------------------------------------------------------------* + * usb2_quirkstr + * + * This function converts an USB quirk code into a string. + *------------------------------------------------------------------------*/ +static const char * +usb2_quirkstr(uint16_t quirk) +{ + return ((quirk < USB_QUIRK_MAX) ? + USB_QUIRK[quirk] : "USB_QUIRK_UNKNOWN"); +} + +/*------------------------------------------------------------------------* + * usb2_test_quirk_by_info + * + * Returns: + * 0: Quirk not found + * Else: Quirk found + *------------------------------------------------------------------------*/ +static uint8_t +usb2_test_quirk_by_info(const struct usb2_lookup_info *info, uint16_t quirk) +{ + uint16_t x; + uint16_t y; + + if (quirk == UQ_NONE) { + return (0); + } + mtx_lock(&usb2_quirk_mtx); + + for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { + /* see if quirk information does not match */ + if ((usb2_quirks[x].vid != info->idVendor) || + (usb2_quirks[x].pid != info->idProduct) || + (usb2_quirks[x].lo_rev > info->bcdDevice) || + (usb2_quirks[x].hi_rev < info->bcdDevice)) { + continue; + } + /* lookup quirk */ + for (y = 0; y != USB_SUB_QUIRKS_MAX; y++) { + if (usb2_quirks[x].quirks[y] == quirk) { + mtx_unlock(&usb2_quirk_mtx); + DPRINTF("Found quirk '%s'.\n", usb2_quirkstr(quirk)); + return (1); + } + } + /* no quirk found */ + break; + } + mtx_unlock(&usb2_quirk_mtx); + return (0); +} + +static struct usb2_quirk_entry * +usb2_quirk_get_entry(uint16_t vid, uint16_t pid, + uint16_t lo_rev, uint16_t hi_rev, uint8_t do_alloc) +{ + uint16_t x; + + mtx_assert(&usb2_quirk_mtx, MA_OWNED); + + if ((vid | pid | lo_rev | hi_rev) == 0) { + /* all zero - special case */ + return (usb2_quirks + USB_DEV_QUIRKS_MAX - 1); + } + /* search for an existing entry */ + for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { + /* see if quirk information does not match */ + if ((usb2_quirks[x].vid != vid) || + (usb2_quirks[x].pid != pid) || + (usb2_quirks[x].lo_rev != lo_rev) || + (usb2_quirks[x].hi_rev != hi_rev)) { + continue; + } + return (usb2_quirks + x); + } + + if (do_alloc == 0) { + /* no match */ + return (NULL); + } + /* search for a free entry */ + for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { + /* see if quirk information does not match */ + if ((usb2_quirks[x].vid | + usb2_quirks[x].pid | + usb2_quirks[x].lo_rev | + usb2_quirks[x].hi_rev) != 0) { + continue; + } + usb2_quirks[x].vid = vid; + usb2_quirks[x].pid = pid; + usb2_quirks[x].lo_rev = lo_rev; + usb2_quirks[x].hi_rev = hi_rev; + + return (usb2_quirks + x); + } + + /* no entry found */ + return (NULL); +} + +/*------------------------------------------------------------------------* + * usb2_quirk_ioctl - handle quirk IOCTLs + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static int +usb2_quirk_ioctl(unsigned long cmd, caddr_t data, + int fflag, struct thread *td) +{ + struct usb2_gen_quirk *pgq; + struct usb2_quirk_entry *pqe; + uint32_t x; + uint32_t y; + int err; + + switch (cmd) { + case USB_DEV_QUIRK_GET: + pgq = (void *)data; + x = pgq->index % USB_SUB_QUIRKS_MAX; + y = pgq->index / USB_SUB_QUIRKS_MAX; + if (y >= USB_DEV_QUIRKS_MAX) { + return (EINVAL); + } + mtx_lock(&usb2_quirk_mtx); + /* copy out data */ + pgq->vid = usb2_quirks[y].vid; + pgq->pid = usb2_quirks[y].pid; + pgq->bcdDeviceLow = usb2_quirks[y].lo_rev; + pgq->bcdDeviceHigh = usb2_quirks[y].hi_rev; + strlcpy(pgq->quirkname, + usb2_quirkstr(usb2_quirks[y].quirks[x]), + sizeof(pgq->quirkname)); + mtx_unlock(&usb2_quirk_mtx); + return (0); /* success */ + + case USB_QUIRK_NAME_GET: + pgq = (void *)data; + x = pgq->index; + if (x >= USB_QUIRK_MAX) { + return (EINVAL); + } + strlcpy(pgq->quirkname, + usb2_quirkstr(x), sizeof(pgq->quirkname)); + return (0); /* success */ + + case USB_DEV_QUIRK_ADD: + pgq = (void *)data; + + /* check privileges */ + err = priv_check(curthread, PRIV_DRIVER); + if (err) { + return (err); + } + /* convert quirk string into numerical */ + for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { + if (strcmp(pgq->quirkname, usb2_quirkstr(y)) == 0) { + break; + } + } + if (y == USB_DEV_QUIRKS_MAX) { + return (EINVAL); + } + if (y == UQ_NONE) { + return (EINVAL); + } + mtx_lock(&usb2_quirk_mtx); + pqe = usb2_quirk_get_entry(pgq->vid, pgq->pid, + pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 1); + for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { + if (pqe->quirks[x] == UQ_NONE) { + pqe->quirks[x] = y; + break; + } + } + mtx_unlock(&usb2_quirk_mtx); + if (x == USB_SUB_QUIRKS_MAX) { + return (ENOMEM); + } + return (0); /* success */ + + case USB_DEV_QUIRK_REMOVE: + pgq = (void *)data; + /* check privileges */ + err = priv_check(curthread, PRIV_DRIVER); + if (err) { + return (err); + } + /* convert quirk string into numerical */ + for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { + if (strcmp(pgq->quirkname, usb2_quirkstr(y)) == 0) { + break; + } + } + if (y == USB_DEV_QUIRKS_MAX) { + return (EINVAL); + } + if (y == UQ_NONE) { + return (EINVAL); + } + mtx_lock(&usb2_quirk_mtx); + pqe = usb2_quirk_get_entry(pgq->vid, pgq->pid, + pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 0); + for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { + if (pqe->quirks[x] == y) { + pqe->quirks[x] = UQ_NONE; + break; + } + } + if (x == USB_SUB_QUIRKS_MAX) { + mtx_unlock(&usb2_quirk_mtx); + return (ENOMEM); + } + for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { + if (pqe->quirks[x] != UQ_NONE) { + break; + } + } + if (x == USB_SUB_QUIRKS_MAX) { + /* all quirk entries are unused - release */ + memset(pqe, 0, sizeof(pqe)); + } + mtx_unlock(&usb2_quirk_mtx); + return (0); /* success */ + + default: + break; + } + return (ENOIOCTL); +} + +static void +usb2_quirk_init(void *arg) +{ + /* initialize mutex */ + mtx_init(&usb2_quirk_mtx, "USB quirk", NULL, MTX_DEF); + + /* register our function */ + usb2_test_quirk_p = &usb2_test_quirk_by_info; + usb2_quirk_ioctl_p = &usb2_quirk_ioctl; + return; +} + +static void +usb2_quirk_uninit(void *arg) +{ + usb2_quirk_unload(arg); + + /* destroy mutex */ + mtx_destroy(&usb2_quirk_mtx); + return; +} + +SYSINIT(usb2_quirk_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_quirk_init, NULL); +SYSUNINIT(usb2_quirk_uninit, SI_SUB_LOCK, SI_ORDER_ANY, usb2_quirk_uninit, NULL); diff --git a/sys/dev/usb2/quirk/usb2_quirk.h b/sys/dev/usb2/quirk/usb2_quirk.h new file mode 100644 index 000000000000..9b57a2e6c0c3 --- /dev/null +++ b/sys/dev/usb2/quirk/usb2_quirk.h @@ -0,0 +1,83 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_QUIRK_H_ +#define _USB2_QUIRK_H_ + +/* NOTE: UQ_NONE is not a valid quirk */ + +#define USB_QUIRK(m,n) \ + m(n, UQ_NONE) \ + /* left and right sound channels are swapped */ \ + m(n, UQ_AUDIO_SWAP_LR) \ + /* input is async despite claim of adaptive */ \ + m(n, UQ_AU_INP_ASYNC) \ + /* don't adjust for fractional samples */ \ + m(n, UQ_AU_NO_FRAC) \ + /* audio device has broken extension unit */ \ + m(n, UQ_AU_NO_XU) \ + /* bad audio spec version number */ \ + m(n, UQ_BAD_ADC) \ + /* device claims audio class, but isn't */ \ + m(n, UQ_BAD_AUDIO) \ + /* printer has broken bidir mode */ \ + m(n, UQ_BROKEN_BIDIR) \ + /* device is bus powered, despite claim */ \ + m(n, UQ_BUS_POWERED) \ + /* device should be ignored by hid class */ \ + m(n, UQ_HID_IGNORE) \ + /* device should be ignored by kbd class */ \ + m(n, UQ_KBD_IGNORE) \ + /* doesn't identify properly */ \ + m(n, UQ_MS_BAD_CLASS) \ + /* mouse sends an unknown leading byte */ \ + m(n, UQ_MS_LEADING_BYTE) \ + /* mouse has Z-axis reversed */ \ + m(n, UQ_MS_REVZ) \ + /* string descriptors are broken */ \ + m(n, UQ_NO_STRINGS) \ + /* device needs clear endpoint stall */ \ + m(n, UQ_OPEN_CLEARSTALL) \ + /* hub lies about power status */ \ + m(n, UQ_POWER_CLAIM) \ + /* spurious mouse button up events */ \ + m(n, UQ_SPUR_BUT_UP) \ + /* has some Unicode strings swapped */ \ + m(n, UQ_SWAP_UNICODE) \ + /* select configuration index 1 by default */ \ + m(n, UQ_CFG_INDEX_1) \ + /* select configuration index 2 by default */ \ + m(n, UQ_CFG_INDEX_2) \ + /* select configuration index 3 by default */ \ + m(n, UQ_CFG_INDEX_3) \ + /* select configuration index 4 by default */ \ + m(n, UQ_CFG_INDEX_4) \ + /* select configuration index 0 by default */ \ + m(n, UQ_CFG_INDEX_0) + +USB_MAKE_ENUM(USB_QUIRK); + +#endif /* _USB2_QUIRK_H_ */ diff --git a/sys/dev/usb2/serial/uark2.c b/sys/dev/usb2/serial/uark2.c new file mode 100644 index 000000000000..5503140c4379 --- /dev/null +++ b/sys/dev/usb2/serial/uark2.c @@ -0,0 +1,482 @@ +/* $OpenBSD: uark.c,v 1.1 2006/08/14 08:30:22 jsg Exp $ */ + +/* + * Copyright (c) 2006 Jonathan Gray + * + * 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$ + */ + +/* + * NOTE: all function names beginning like "uark_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UARK_BUF_SIZE 1024 /* bytes */ + +#define UARK_N_TRANSFER 4 /* units */ + +#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 usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UARK_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_flags; +#define UARK_FLAG_BULK_READ_STALL 0x01 +#define UARK_FLAG_BULK_WRITE_STALL 0x02 + uint8_t sc_msr; + uint8_t sc_lsr; +}; + +/* prototypes */ + +static device_probe_t uark_probe; +static device_attach_t uark_attach; +static device_detach_t uark_detach; + +static usb2_callback_t uark_bulk_write_callback; +static usb2_callback_t uark_bulk_write_clear_stall_callback; +static usb2_callback_t uark_bulk_read_callback; +static usb2_callback_t uark_bulk_read_clear_stall_callback; + +static void uark_start_read(struct usb2_com_softc *ucom); +static void uark_stop_read(struct usb2_com_softc *ucom); +static void uark_start_write(struct usb2_com_softc *ucom); +static void uark_stop_write(struct usb2_com_softc *ucom); + +static int uark_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void uark_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void uark_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void uark_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value); + +static const struct usb2_config + uark_xfer_config[UARK_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UARK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uark_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UARK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uark_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uark_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uark_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback uark_callback = { + .usb2_com_cfg_get_status = &uark_cfg_get_status, + .usb2_com_cfg_set_break = &uark_cfg_set_break, + .usb2_com_cfg_param = &uark_cfg_param, + .usb2_com_pre_param = &uark_pre_param, + .usb2_com_start_read = &uark_start_read, + .usb2_com_stop_read = &uark_stop_read, + .usb2_com_start_write = &uark_start_write, + .usb2_com_stop_write = &uark_stop_write, +}; + +static device_method_t uark_methods[] = { + /* Device methods */ + DEVMETHOD(device_probe, uark_probe), + DEVMETHOD(device_attach, uark_attach), + DEVMETHOD(device_detach, uark_detach), + {0, 0} +}; + +static devclass_t uark_devclass; + +static driver_t uark_driver = { + .name = "uark", + .methods = uark_methods, + .size = sizeof(struct uark_softc), +}; + +DRIVER_MODULE(uark, ushub, uark_driver, uark_devclass, NULL, 0); +MODULE_DEPEND(uark, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uark, usb2_core, 1, 1, 1); +MODULE_DEPEND(uark, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static const struct usb2_device_id uark_devs[] = { + {USB_VPI(USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, 0)}, +}; + +static int +uark_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != 0) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UARK_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uark_devs, sizeof(uark_devs), uaa)); +} + +static int +uark_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uark_softc *sc = device_get_softc(dev); + int32_t error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + iface_index = UARK_IFACE_INDEX; + error = usb2_transfer_setup + (uaa->device, &iface_index, sc->sc_xfer, + uark_xfer_config, UARK_N_TRANSFER, sc, &Giant); + + if (error) { + device_printf(dev, "allocating control USB " + "transfers failed!\n"); + goto detach; + } + /* clear stall at first run */ + sc->sc_flags |= (UARK_FLAG_BULK_WRITE_STALL | + UARK_FLAG_BULK_READ_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uark_callback, &Giant); + if (error) { + DPRINTF("usb2_com_attach failed\n"); + goto detach; + } + return (0); /* success */ + +detach: + uark_detach(dev); + return (ENXIO); /* failure */ +} + +static int +uark_detach(device_t dev) +{ + struct uark_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UARK_N_TRANSFER); + + return (0); +} + +static void +uark_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct uark_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flags & UARK_FLAG_BULK_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UARK_BUF_SIZE, &actlen)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UARK_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uark_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uark_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UARK_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uark_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct uark_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, + xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flags & UARK_FLAG_BULK_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UARK_FLAG_BULK_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uark_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uark_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UARK_FLAG_BULK_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uark_start_read(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uark_stop_read(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uark_start_write(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uark_stop_write(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static int +uark_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + if ((t->c_ospeed < 300) || (t->c_ospeed > 115200)) + return (EINVAL); + return (0); +} + +static void +uark_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uark_softc *sc = ucom->sc_parent; + uint32_t speed = t->c_ospeed; + uint16_t data; + + /* + * NOTE: When reverse computing the baud rate from the "data" all + * allowed baud rates are within 3% of the initial baud rate. + */ + data = (UARK_BAUD_REF + (speed / 2)) / speed; + + uark_cfg_write(sc, 3, 0x83); + uark_cfg_write(sc, 0, data & 0xFF); + uark_cfg_write(sc, 1, data >> 8); + uark_cfg_write(sc, 3, 0x03); + + if (t->c_cflag & CSTOPB) + data = UARK_STOP_BITS_2; + else + data = UARK_STOP_BITS_1; + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) + data |= UARK_PARITY_ODD; + else + data |= UARK_PARITY_EVEN; + } else + data |= UARK_PARITY_NONE; + + switch (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; + default: + case CS8: + data |= UARK_SET_DATA_BITS(8); + break; + } + uark_cfg_write(sc, 3, 0x00); + uark_cfg_write(sc, 3, data); + return; +} + +static void +uark_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uark_softc *sc = ucom->sc_parent; + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static void +uark_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uark_softc *sc = ucom->sc_parent; + + DPRINTF("onoff=%d\n", onoff); + + uark_cfg_write(sc, 4, onoff ? 0x01 : 0x00); + return; +} + +static void +uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + return; + } + req.bmRequestType = UARK_WRITE; + req.bRequest = UARK_REQUEST; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, 0); + + err = usb2_do_request_flags + (sc->sc_udev, &Giant, &req, NULL, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + return; +} diff --git a/sys/dev/usb2/serial/ubsa2.c b/sys/dev/usb2/serial/ubsa2.c new file mode 100644 index 000000000000..140e0908fb64 --- /dev/null +++ b/sys/dev/usb2/serial/ubsa2.c @@ -0,0 +1,755 @@ +/*- + * Copyright (c) 2002, Alexander Kabaev . + * 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 +__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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR ubsa_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int ubsa_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa"); +SYSCTL_INT(_hw_usb2_ubsa, OID_AUTO, debug, CTLFLAG_RW, + &ubsa_debug, 0, "ubsa debug level"); +#endif + +#define UBSA_N_TRANSFER 6 /* units */ +#define UBSA_BSIZE 1024 /* bytes */ + +#define UBSA_CONFIG_INDEX 1 +#define UBSA_IFACE_INDEX 0 + +#define UBSA_REG_BAUDRATE 0x00 +#define UBSA_REG_STOP_BITS 0x01 +#define UBSA_REG_DATA_BITS 0x02 +#define UBSA_REG_PARITY 0x03 +#define UBSA_REG_DTR 0x0A +#define UBSA_REG_RTS 0x0B +#define UBSA_REG_BREAK 0x0C +#define UBSA_REG_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 usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UBSA_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_flag; +#define UBSA_FLAG_WRITE_STALL 0x0001 +#define UBSA_FLAG_READ_STALL 0x0002 +#define UBSA_FLAG_INTR_STALL 0x0004 + + uint8_t sc_iface_no; /* interface number */ + uint8_t sc_iface_index; /* interface index */ + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* UBSA status register */ +}; + +static device_probe_t ubsa_probe; +static device_attach_t ubsa_attach; +static device_detach_t ubsa_detach; + +static usb2_callback_t ubsa_write_callback; +static usb2_callback_t ubsa_write_clear_stall_callback; +static usb2_callback_t ubsa_read_callback; +static usb2_callback_t ubsa_read_clear_stall_callback; +static usb2_callback_t ubsa_intr_callback; +static usb2_callback_t ubsa_intr_clear_stall_callback; + +static void ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value); +static void ubsa_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void ubsa_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void ubsa_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static int ubsa_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void ubsa_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void ubsa_start_read(struct usb2_com_softc *ucom); +static void ubsa_stop_read(struct usb2_com_softc *ucom); +static void ubsa_start_write(struct usb2_com_softc *ucom); +static void ubsa_stop_write(struct usb2_com_softc *ucom); +static void ubsa_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); + +static const struct usb2_config ubsa_config[UBSA_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UBSA_BSIZE, /* bytes */ + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &ubsa_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UBSA_BSIZE, /* bytes */ + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ubsa_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubsa_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubsa_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &ubsa_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubsa_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback ubsa_callback = { + .usb2_com_cfg_get_status = &ubsa_cfg_get_status, + .usb2_com_cfg_set_dtr = &ubsa_cfg_set_dtr, + .usb2_com_cfg_set_rts = &ubsa_cfg_set_rts, + .usb2_com_cfg_set_break = &ubsa_cfg_set_break, + .usb2_com_cfg_param = &ubsa_cfg_param, + .usb2_com_pre_param = &ubsa_pre_param, + .usb2_com_start_read = &ubsa_start_read, + .usb2_com_stop_read = &ubsa_stop_read, + .usb2_com_start_write = &ubsa_start_write, + .usb2_com_stop_write = &ubsa_stop_write, +}; + +static const struct usb2_device_id ubsa_devs[] = { + /* AnyData ADU-500A */ + {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A, 0)}, + /* AnyData ADU-E100A/H */ + {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X, 0)}, + /* Axesstel MV100H */ + {USB_VPI(USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM, 0)}, + /* BELKIN F5U103 */ + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103, 0)}, + /* BELKIN F5U120 */ + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120, 0)}, + /* GoHubs GO-COM232 */ + {USB_VPI(USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM, 0)}, + /* GoHubs GO-COM232 */ + {USB_VPI(USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232, 0)}, + /* Peracom */ + {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0)}, + /* Novatel Wireless Merlin cards */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, 0)}, + /* Dell version of the above */ + {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, 0)}, + /* Novatel Wireless Merlin v740 */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, 0)}, + /* Option Vodafone MC3G */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, 0)}, + /* Option GlobeTrotter 3G */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, 0)}, + /* Option GlobeTrotter 3G+ */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, 0)}, + /* Option GlobeTrotter Max 3.6 */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, 0)}, + /* Option GlobeTrotter 3G QUAD */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, 0)}, + /* Sierra Wireless LENOVO UMTS card */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, 0)}, + /* Qualcomm, Inc. ZTE CDMA */ + {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, 0)}, +}; + +static device_method_t ubsa_methods[] = { + DEVMETHOD(device_probe, ubsa_probe), + DEVMETHOD(device_attach, ubsa_attach), + DEVMETHOD(device_detach, ubsa_detach), + {0, 0} +}; + +static devclass_t ubsa_devclass; + +static driver_t ubsa_driver = { + .name = "ubsa", + .methods = ubsa_methods, + .size = sizeof(struct ubsa_softc), +}; + +DRIVER_MODULE(ubsa, ushub, ubsa_driver, ubsa_devclass, NULL, 0); +MODULE_DEPEND(ubsa, usb2_serial, 1, 1, 1); +MODULE_DEPEND(ubsa, usb2_core, 1, 1, 1); +MODULE_DEPEND(ubsa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +ubsa_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UBSA_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UBSA_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(ubsa_devs, sizeof(ubsa_devs), uaa)); +} + +static int +ubsa_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubsa_softc *sc = device_get_softc(dev); + int error; + + DPRINTF("sc=%p\n", sc); + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = UBSA_IFACE_INDEX; + + error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index, + sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &Giant); + + if (error) { + DPRINTF("could not allocate all pipes\n"); + goto detach; + } + /* clear stall at first run */ + sc->sc_flag |= (UBSA_FLAG_WRITE_STALL | + UBSA_FLAG_READ_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &ubsa_callback, &Giant); + if (error) { + DPRINTF("usb2_com_attach failed\n"); + goto detach; + } + return (0); + +detach: + ubsa_detach(dev); + return (ENXIO); +} + +static int +ubsa_detach(device_t dev) +{ + struct ubsa_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER); + + return (0); +} + +static void +ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + return; + } + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = index; + USETW(req.wValue, value); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usb2_do_request_flags + (sc->sc_udev, &Giant, &req, NULL, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + return; +} + +static void +ubsa_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + ubsa_cfg_request(sc, UBSA_REG_DTR, onoff ? 1 : 0); + return; +} + +static void +ubsa_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + ubsa_cfg_request(sc, UBSA_REG_RTS, onoff ? 1 : 0); + return; +} + +static void +ubsa_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + ubsa_cfg_request(sc, UBSA_REG_BREAK, onoff ? 1 : 0); + return; +} + +static int +ubsa_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + DPRINTF("sc = %p\n", sc); + + switch (t->c_ospeed) { + case B0: + case B300: + case B600: + case B1200: + case B2400: + case B4800: + case B9600: + case B19200: + case B38400: + case B57600: + case B115200: + case B230400: + break; + default: + return (EINVAL); + } + return (0); +} + +static void +ubsa_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct ubsa_softc *sc = ucom->sc_parent; + uint16_t value = 0; + + DPRINTF("sc = %p\n", sc); + + switch (t->c_ospeed) { + case B0: + ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, 0); + ubsa_cfg_set_dtr(&sc->sc_ucom, 0); + ubsa_cfg_set_rts(&sc->sc_ucom, 0); + 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 / t->c_ospeed; + ubsa_cfg_request(sc, UBSA_REG_BAUDRATE, value); + break; + default: + return; + } + + if (t->c_cflag & PARENB) + value = (t->c_cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; + else + value = UBSA_PARITY_NONE; + + ubsa_cfg_request(sc, UBSA_REG_PARITY, value); + + switch (t->c_cflag & CSIZE) { + case CS5: + value = 0; + break; + case CS6: + value = 1; + break; + case CS7: + value = 2; + break; + default: + case CS8: + value = 3; + break; + } + + ubsa_cfg_request(sc, UBSA_REG_DATA_BITS, value); + + value = (t->c_cflag & CSTOPB) ? 1 : 0; + + ubsa_cfg_request(sc, UBSA_REG_STOP_BITS, value); + + value = 0; + if (t->c_cflag & CRTSCTS) + value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; + + if (t->c_iflag & (IXON | IXOFF)) + value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; + + ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, value); + return; +} + +static void +ubsa_start_read(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer[4]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +ubsa_stop_read(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + /* stop interrupt endpoint */ + usb2_transfer_stop(sc->sc_xfer[5]); + usb2_transfer_stop(sc->sc_xfer[4]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +ubsa_start_write(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ubsa_stop_write(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +ubsa_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static void +ubsa_write_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UBSA_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UBSA_BSIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UBSA_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +ubsa_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UBSA_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubsa_read_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UBSA_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UBSA_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +ubsa_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UBSA_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubsa_intr_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + uint8_t buf[4]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen >= sizeof(buf)) { + + usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf)); + + /* + * incidentally, Belkin adapter status bits match + * UART 16550 bits + */ + sc->sc_lsr = buf[2]; + sc->sc_msr = buf[3]; + + DPRINTF("lsr = 0x%02x, msr = 0x%02x\n", + sc->sc_lsr, sc->sc_msr); + + usb2_com_status_change(&sc->sc_ucom); + } else { + DPRINTF("ignoring short packet, %d bytes\n", + xfer->actlen); + } + + case USB_ST_SETUP: + if (sc->sc_flag & UBSA_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UBSA_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + + } +} + +static void +ubsa_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubsa_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UBSA_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} diff --git a/sys/dev/usb2/serial/ubser2.c b/sys/dev/usb2/serial/ubser2.c new file mode 100644 index 000000000000..7b9c88dda304 --- /dev/null +++ b/sys/dev/usb2/serial/ubser2.c @@ -0,0 +1,604 @@ +/*- + * Copyright (c) 2004 Bernd Walter + * + * $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 . + * 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 +__FBSDID("$FreeBSD$"); + +/* + * BWCT serial adapter driver + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR ubser_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UBSER_UNIT_MAX 32 + +/* Vendor Interface Requests */ +#define VENDOR_GET_NUMSER 0x01 +#define VENDOR_SET_BREAK 0x02 +#define VENDOR_CLEAR_BREAK 0x03 + +#if USB_DEBUG +static int ubser_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser"); +SYSCTL_INT(_hw_usb2_ubser, OID_AUTO, debug, CTLFLAG_RW, + &ubser_debug, 0, "ubser debug level"); +#endif + +#define UBSER_TR_DT_WRITE 0 +#define UBSER_TR_DT_READ 1 +#define UBSER_TR_CS_WRITE 2 +#define UBSER_TR_CS_READ 3 +#define UBSER_TR_MAX 4 + +struct ubser_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom[UBSER_UNIT_MAX]; + + struct usb2_xfer *sc_xfer[UBSER_TR_MAX]; + struct usb2_device *sc_udev; + + uint16_t sc_tx_size; + + uint8_t sc_numser; + uint8_t sc_flags; +#define UBSER_FLAG_READ_STALL 0x01 +#define UBSER_FLAG_WRITE_STALL 0x02 + + uint8_t sc_iface_no; + uint8_t sc_iface_index; + uint8_t sc_curr_tx_unit; + uint8_t sc_name[16]; +}; + +/* prototypes */ + +static device_probe_t ubser_probe; +static device_attach_t ubser_attach; +static device_detach_t ubser_detach; + +static usb2_callback_t ubser_write_clear_stall_callback; +static usb2_callback_t ubser_write_callback; +static usb2_callback_t ubser_read_clear_stall_callback; +static usb2_callback_t ubser_read_callback; + +static int ubser_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void ubser_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void ubser_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void ubser_start_read(struct usb2_com_softc *ucom); +static void ubser_stop_read(struct usb2_com_softc *ucom); +static void ubser_start_write(struct usb2_com_softc *ucom); +static void ubser_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config ubser_config[UBSER_TR_MAX] = { + + [UBSER_TR_DT_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &ubser_write_callback, + }, + + [UBSER_TR_DT_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ubser_read_callback, + }, + + [UBSER_TR_CS_WRITE] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ubser_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [UBSER_TR_CS_READ] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ubser_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback ubser_callback = { + .usb2_com_cfg_set_break = &ubser_cfg_set_break, + .usb2_com_cfg_get_status = &ubser_cfg_get_status, + .usb2_com_pre_param = &ubser_pre_param, + .usb2_com_start_read = &ubser_start_read, + .usb2_com_stop_read = &ubser_stop_read, + .usb2_com_start_write = &ubser_start_write, + .usb2_com_stop_write = &ubser_stop_write, +}; + +static device_method_t ubser_methods[] = { + DEVMETHOD(device_probe, ubser_probe), + DEVMETHOD(device_attach, ubser_attach), + DEVMETHOD(device_detach, ubser_detach), + {0, 0} +}; + +static devclass_t ubser_devclass; + +static driver_t ubser_driver = { + .name = "ubser", + .methods = ubser_methods, + .size = sizeof(struct ubser_softc), +}; + +DRIVER_MODULE(ubser, ushub, ubser_driver, ubser_devclass, NULL, 0); +MODULE_DEPEND(ubser, usb2_serial, 1, 1, 1); +MODULE_DEPEND(ubser, usb2_core, 1, 1, 1); +MODULE_DEPEND(ubser, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +ubser_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* check if this is a BWCT vendor specific ubser interface */ + if ((strcmp(uaa->device->manufacturer, "BWCT") == 0) && + (uaa->info.bInterfaceClass == 0xff) && + (uaa->info.bInterfaceSubClass == 0x00)) + return (0); + + return (ENXIO); +} + +static int +ubser_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubser_softc *sc = device_get_softc(dev); + struct usb2_device_request req; + uint8_t n; + int error; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = uaa->info.bIfaceIndex; + sc->sc_udev = uaa->device; + + /* get number of serials */ + req.bmRequestType = UT_READ_VENDOR_INTERFACE; + req.bRequest = VENDOR_GET_NUMSER; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + error = usb2_do_request_flags + (uaa->device, &Giant, &req, &sc->sc_numser, + 0, NULL, USB_DEFAULT_TIMEOUT); + + if (error || (sc->sc_numser == 0)) { + device_printf(dev, "failed to get number " + "of serial ports: %s\n", + usb2_errstr(error)); + goto detach; + } + if (sc->sc_numser > UBSER_UNIT_MAX) + sc->sc_numser = UBSER_UNIT_MAX; + + device_printf(dev, "found %i serials\n", sc->sc_numser); + + error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index, + sc->sc_xfer, ubser_config, UBSER_TR_MAX, sc, &Giant); + if (error) { + goto detach; + } + sc->sc_tx_size = sc->sc_xfer[UBSER_TR_DT_WRITE]->max_data_length; + + if (sc->sc_tx_size == 0) { + DPRINTFN(0, "invalid tx_size!\n"); + goto detach; + } + /* initialize port numbers */ + + for (n = 0; n < sc->sc_numser; n++) { + sc->sc_ucom[n].sc_portno = n; + } + + error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, + sc->sc_numser, sc, &ubser_callback, &Giant); + if (error) { + goto detach; + } + mtx_lock(&Giant); + + sc->sc_flags |= (UBSER_FLAG_READ_STALL | + UBSER_FLAG_WRITE_STALL); + + usb2_transfer_start(sc->sc_xfer[UBSER_TR_DT_READ]); + + mtx_unlock(&Giant); + + return (0); /* success */ + +detach: + ubser_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ubser_detach(device_t dev) +{ + struct ubser_softc *sc = device_get_softc(dev); + uint8_t n; + + DPRINTF("\n"); + + usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser); + + /* + * need to stop all transfers atomically, hence when clear stall + * completes, it might start other transfers ! + */ + mtx_lock(&Giant); + for (n = 0; n < UBSER_TR_MAX; n++) { + usb2_transfer_stop(sc->sc_xfer[n]); + } + mtx_unlock(&Giant); + + usb2_transfer_unsetup(sc->sc_xfer, UBSER_TR_MAX); + + return (0); +} + +static int +ubser_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + DPRINTF("\n"); + + /* + * The firmware on our devices can only do 8n1@9600bps + * without handshake. + * We refuse to accept other configurations. + */ + + /* ensure 9600bps */ + switch (t->c_ospeed) { + case 9600: + break; + default: + return (EINVAL); + } + + /* 2 stop bits not possible */ + if (t->c_cflag & CSTOPB) + return (EINVAL); + + /* XXX parity handling not possible with current firmware */ + if (t->c_cflag & PARENB) + return (EINVAL); + + /* we can only do 8 data bits */ + switch (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. + */ + + return (0); +} + +static __inline void +ubser_inc_tx_unit(struct ubser_softc *sc) +{ + sc->sc_curr_tx_unit++; + if (sc->sc_curr_tx_unit >= sc->sc_numser) { + sc->sc_curr_tx_unit = 0; + } + return; +} + +static void +ubser_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubser_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[UBSER_TR_DT_WRITE]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBSER_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubser_write_callback(struct usb2_xfer *xfer) +{ + struct ubser_softc *sc = xfer->priv_sc; + uint8_t buf[1]; + uint8_t first_unit = sc->sc_curr_tx_unit; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flags & UBSER_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[UBSER_TR_CS_WRITE]); + return; + } + do { + if (usb2_com_get_data(sc->sc_ucom + sc->sc_curr_tx_unit, + xfer->frbuffers, 1, sc->sc_tx_size - 1, + &actlen)) { + + buf[0] = sc->sc_curr_tx_unit; + + usb2_copy_in(xfer->frbuffers, 0, buf, 1); + + xfer->frlengths[0] = actlen + 1; + usb2_start_hardware(xfer); + + ubser_inc_tx_unit(sc); /* round robin */ + + break; + } + ubser_inc_tx_unit(sc); + + } while (sc->sc_curr_tx_unit != first_unit); + + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UBSER_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[UBSER_TR_CS_WRITE]); + } + return; + + } +} + +static void +ubser_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ubser_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[UBSER_TR_DT_READ]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBSER_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ubser_read_callback(struct usb2_xfer *xfer) +{ + struct ubser_softc *sc = xfer->priv_sc; + uint8_t buf[1]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen < 1) { + DPRINTF("invalid actlen=0!\n"); + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 1); + + if (buf[0] >= sc->sc_numser) { + DPRINTF("invalid serial number!\n"); + goto tr_setup; + } + usb2_com_put_data(sc->sc_ucom + buf[0], + xfer->frbuffers, 1, xfer->actlen - 1); + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UBSER_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[UBSER_TR_CS_READ]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UBSER_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[UBSER_TR_CS_READ]); + } + return; + + } +} + +static void +ubser_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ubser_softc *sc = ucom->sc_parent; + uint8_t x = ucom->sc_portno; + struct usb2_device_request req; + usb2_error_t err; + + if (onoff) { + + req.bmRequestType = UT_READ_VENDOR_INTERFACE; + req.bRequest = VENDOR_SET_BREAK; + req.wValue[0] = x; + req.wValue[1] = 0; + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usb2_do_request_flags + (sc->sc_udev, &Giant, &req, NULL, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "send break failed, error=%s\n", + usb2_errstr(err)); + } + } + return; +} + +static void +ubser_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + /* fake status bits */ + *lsr = 0; + *msr = SER_DCD; + return; +} + +static void +ubser_start_read(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UBSER_TR_DT_READ]); + return; +} + +static void +ubser_stop_read(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UBSER_TR_CS_READ]); + usb2_transfer_stop(sc->sc_xfer[UBSER_TR_DT_READ]); + return; +} + +static void +ubser_start_write(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UBSER_TR_DT_WRITE]); + return; +} + +static void +ubser_stop_write(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UBSER_TR_CS_WRITE]); + usb2_transfer_stop(sc->sc_xfer[UBSER_TR_DT_WRITE]); + return; +} diff --git a/sys/dev/usb2/serial/uchcom2.c b/sys/dev/usb2/serial/uchcom2.c new file mode 100644 index 000000000000..049c924dcf55 --- /dev/null +++ b/sys/dev/usb2/serial/uchcom2.c @@ -0,0 +1,1038 @@ +/* $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 +__FBSDID("$FreeBSD$"); + +/* + * driver for WinChipHead CH341/340, the worst USB-serial chip in the world. + */ + +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uchcom_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int uchcom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom"); +SYSCTL_INT(_hw_usb2_uchcom, OID_AUTO, debug, CTLFLAG_RW, + &uchcom_debug, 0, "uchcom debug level"); +#endif + +#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 UCHCOM_BULK_BUF_SIZE 1024 /* bytes */ +#define UCHCOM_N_TRANSFER 6 /* units */ + +struct uchcom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_dtr; /* local copy */ + uint8_t sc_rts; /* local copy */ + uint8_t sc_version; + uint8_t sc_msr; + uint8_t sc_lsr; /* local status register */ + uint8_t sc_flag; +#define UCHCOM_FLAG_INTR_STALL 0x01 +#define UCHCOM_FLAG_READ_STALL 0x02 +#define UCHCOM_FLAG_WRITE_STALL 0x04 +}; + +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 usb2_device_id uchcom_devs[] = { + {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, +}; + +/* protypes */ + +static int uchcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td); +static int uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void uchcom_cfg_set_break(struct usb2_com_softc *sc, uint8_t onoff); +static void uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void uchcom_start_read(struct usb2_com_softc *ucom); +static void uchcom_start_write(struct usb2_com_softc *ucom); +static void uchcom_stop_read(struct usb2_com_softc *ucom); +static void uchcom_stop_write(struct usb2_com_softc *ucom); + +static void uchcom_update_version(struct uchcom_softc *sc); +static void uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur); +static void uchcom_update_status(struct uchcom_softc *sc); +static void uchcom_set_dtrrts(struct uchcom_softc *sc); +static int uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate); +static void uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate); +static void uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag); +static void uchcom_clear_chip(struct uchcom_softc *sc); +static void uchcom_reset_chip(struct uchcom_softc *sc); + +static device_probe_t uchcom_probe; +static device_attach_t uchcom_attach; +static device_detach_t uchcom_detach; + +static usb2_callback_t uchcom_intr_callback; +static usb2_callback_t uchcom_intr_clear_stall_callback; +static usb2_callback_t uchcom_write_callback; +static usb2_callback_t uchcom_write_clear_stall_callback; +static usb2_callback_t uchcom_read_callback; +static usb2_callback_t uchcom_read_clear_stall_callback; + +static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UCHCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uchcom_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UCHCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uchcom_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uchcom_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uchcom_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &uchcom_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uchcom_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +struct usb2_com_callback uchcom_callback = { + .usb2_com_cfg_get_status = &uchcom_cfg_get_status, + .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr, + .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts, + .usb2_com_cfg_set_break = &uchcom_cfg_set_break, + .usb2_com_cfg_param = &uchcom_cfg_param, + .usb2_com_pre_param = &uchcom_pre_param, + .usb2_com_ioctl = &uchcom_ioctl, + .usb2_com_start_read = &uchcom_start_read, + .usb2_com_stop_read = &uchcom_stop_read, + .usb2_com_start_write = &uchcom_start_write, + .usb2_com_stop_write = &uchcom_stop_write, +}; + +/* ---------------------------------------------------------------------- + * driver entry points + */ + +static int +uchcom_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); +} + +static int +uchcom_attach(device_t dev) +{ + struct uchcom_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + int error; + uint8_t iface_index; + + DPRINTFN(11, "\n"); + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + switch (uaa->info.bcdDevice) { + case UCHCOM_REV_CH340: + device_printf(dev, "CH340 detected\n"); + break; + default: + device_printf(dev, "CH341 detected\n"); + break; + } + + iface_index = UCHCOM_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, uchcom_config_data, + UCHCOM_N_TRANSFER, sc, &Giant); + + if (error) { + DPRINTF("one or more missing USB endpoints, " + "error=%s\n", usb2_errstr(error)); + goto detach; + } + /* + * Do the initialization during attach so that the system does not + * sleep during open: + */ + uchcom_update_version(sc); + uchcom_clear_chip(sc); + uchcom_reset_chip(sc); + uchcom_update_status(sc); + + sc->sc_dtr = 1; + sc->sc_rts = 1; + + /* clear stall at first run */ + sc->sc_flag |= (UCHCOM_FLAG_READ_STALL | + UCHCOM_FLAG_WRITE_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uchcom_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + uchcom_detach(dev); + return (ENXIO); +} + +static int +uchcom_detach(device_t dev) +{ + struct uchcom_softc *sc = device_get_softc(dev); + + DPRINTFN(11, "\n"); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); + + return (0); +} + +/* ---------------------------------------------------------------------- + * low level i/o + */ + +static void +uchcom_do_request(struct uchcom_softc *sc, + struct usb2_device_request *req, void *data) +{ + uint16_t length; + uint16_t actlen; + usb2_error_t err; + + length = UGETW(req->wLength); + actlen = 0; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto done; + } + err = usb2_do_request_flags(sc->sc_udev, &Giant, req, + data, USB_SHORT_XFER_OK, &actlen, 1000); + + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +done: + if (length != actlen) { + if (req->bmRequestType & UT_READ) { + bzero(USB_ADD_BYTES(data, actlen), length - actlen); + } + } + return; +} + +static void +uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, + uint16_t value, uint16_t index) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = reqno; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, 0); + + uchcom_do_request(sc, &req, NULL); + return; +} + +static void +uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, + uint16_t value, uint16_t index, void *buf, uint16_t buflen) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = reqno; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, buflen); + + uchcom_do_request(sc, &req, buf); + return; +} + +static void +uchcom_write_reg(struct uchcom_softc *sc, + uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) +{ + DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", + (unsigned)reg1, (unsigned)val1, + (unsigned)reg2, (unsigned)val2); + uchcom_ctrl_write( + sc, UCHCOM_REQ_WRITE_REG, + reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); + return; +} + +static void +uchcom_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]; + + uchcom_ctrl_read( + sc, UCHCOM_REQ_READ_REG, + reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); + + DPRINTF("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; +} + +static void +uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) +{ + uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; + + uchcom_ctrl_read( + sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); + + if (rver) + *rver = buf[0]; + + return; +} + +static void +uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) +{ + uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); + return; +} + +static void +uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) +{ + uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); + return; +} + +static void +uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) +{ + uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); + return; +} + + +/* ---------------------------------------------------------------------- + * middle layer + */ + +static void +uchcom_update_version(struct uchcom_softc *sc) +{ + uchcom_get_version(sc, &sc->sc_version); + return; +} + +static void +uchcom_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 void +uchcom_update_status(struct uchcom_softc *sc) +{ + uint8_t cur; + + uchcom_get_status(sc, &cur); + uchcom_convert_status(sc, cur); + return; +} + + +static void +uchcom_set_dtrrts(struct uchcom_softc *sc) +{ + uint8_t val = 0; + + if (sc->sc_dtr) + val |= UCHCOM_DTR_MASK; + if (sc->sc_rts) + val |= UCHCOM_RTS_MASK; + + if (sc->sc_version < UCHCOM_VER_20) + uchcom_set_dtrrts_10(sc, ~val); + else + uchcom_set_dtrrts_20(sc, ~val); + + return; +} + +static void +uchcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uchcom_softc *sc = ucom->sc_parent; + uint8_t brk1; + uint8_t brk2; + + uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); + 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; + } + uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); + + return; +} + +static int +uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) +{ + const struct uchcom_divider_record *rp; + uint32_t div; + uint32_t rem; + uint32_t mod; + uint8_t i; + + /* find record */ + for (i = 0; i != NUM_DIVIDERS; i++) { + if (dividers[i].dvr_high >= rate && + dividers[i].dvr_low <= rate) { + rp = ÷rs[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 void +uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) +{ + struct uchcom_divider dv; + + if (uchcom_calc_divider_settings(&dv, rate)) + return; + + uchcom_write_reg(sc, + UCHCOM_REG_BPS_PRE, dv.dv_prescaler, + UCHCOM_REG_BPS_DIV, dv.dv_div); + uchcom_write_reg(sc, + UCHCOM_REG_BPS_MOD, dv.dv_mod, + UCHCOM_REG_BPS_PAD, 0); + return; +} + +static void +uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) +{ + uint8_t lcr1 = 0; + uint8_t lcr2 = 0; + + uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); + + 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. + */ + + if (cflag & PARENB) { + lcr1 |= UCHCOM_LCR1_PARENB; + if (cflag & PARODD) + lcr2 |= UCHCOM_LCR2_PARODD; + else + lcr2 |= UCHCOM_LCR2_PAREVEN; + } + uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2); + + return; +} + +static void +uchcom_clear_chip(struct uchcom_softc *sc) +{ + DPRINTF("\n"); + uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); + return; +} + +static void +uchcom_reset_chip(struct uchcom_softc *sc) +{ + uint16_t val; + uint16_t idx; + uint8_t lcr1; + uint8_t lcr2; + uint8_t pre; + uint8_t div; + uint8_t mod; + + uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); + uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div); + uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL); + + val = 0; + idx = 0; + 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("reset v=0x%04X, i=0x%04X\n", val, idx); + + uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx); + + return; +} + +/* ---------------------------------------------------------------------- + * methods for ucom + */ +static void +uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static int +uchcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, + struct thread *td) +{ + return (ENOTTY); +} + +static void +uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + sc->sc_dtr = onoff; + uchcom_set_dtrrts(sc); + return; +} + +static void +uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + sc->sc_rts = onoff; + uchcom_set_dtrrts(sc); + return; +} + +static int +uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uchcom_divider dv; + + switch (t->c_cflag & CSIZE) { + case CS5: + case CS6: + case CS7: + return (EIO); + default: + break; + } + + if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { + return (EIO); + } + return (0); /* success */ +} + +static void +uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + uchcom_set_line_control(sc, t->c_cflag); + uchcom_set_dte_rate(sc, t->c_ospeed); + return; +} + +static void +uchcom_start_read(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer[4]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uchcom_stop_read(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + /* stop interrupt endpoint */ + usb2_transfer_stop(sc->sc_xfer[4]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uchcom_start_write(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uchcom_stop_write(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +/* ---------------------------------------------------------------------- + * callback when the modem status is changed. + */ +static void +uchcom_intr_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + uint8_t buf[UCHCOM_INTR_LEAST]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("actlen = %u\n", xfer->actlen); + + if (xfer->actlen >= UCHCOM_INTR_LEAST) { + usb2_copy_out(xfer->frbuffers, 0, buf, + UCHCOM_INTR_LEAST); + + DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", + (unsigned)buf[0], (unsigned)buf[1], + (unsigned)buf[2], (unsigned)buf[3]); + + uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); + usb2_com_status_change(&sc->sc_ucom); + } + case USB_ST_SETUP: + if (sc->sc_flag & UCHCOM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UCHCOM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + break; + } + return; +} + +static void +uchcom_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UCHCOM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uchcom_write_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UCHCOM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UCHCOM_BULK_BUF_SIZE, &actlen)) { + + DPRINTF("actlen = %d\n", actlen); + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UCHCOM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uchcom_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UCHCOM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uchcom_read_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UCHCOM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UCHCOM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uchcom_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uchcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UCHCOM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static device_method_t uchcom_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uchcom_probe), + DEVMETHOD(device_attach, uchcom_attach), + DEVMETHOD(device_detach, uchcom_detach), + + {0, 0} +}; + +static driver_t uchcom_driver = { + "ucom", + uchcom_methods, + sizeof(struct uchcom_softc) +}; + +static devclass_t uchcom_devclass; + +DRIVER_MODULE(uchcom, ushub, uchcom_driver, uchcom_devclass, NULL, 0); +MODULE_DEPEND(uchcom, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uchcom, usb2_core, 1, 1, 1); +MODULE_DEPEND(uchcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); diff --git a/sys/dev/usb2/serial/ucycom2.c b/sys/dev/usb2/serial/ucycom2.c new file mode 100644 index 000000000000..2cbbe47240b6 --- /dev/null +++ b/sys/dev/usb2/serial/ucycom2.c @@ -0,0 +1,607 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * 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. + */ + +/* + * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to + * RS232 bridges. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ + +#define UCYCOM_ENDPT_MAX 3 /* units */ +#define UCYCOM_IFACE_INDEX 0 + +struct ucycom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[UCYCOM_ENDPT_MAX]; + + uint32_t sc_model; +#define MODEL_CY7C63743 0x63743 +#define MODEL_CY7C64013 0x64013 + + uint16_t sc_flen; /* feature report length */ + uint16_t sc_ilen; /* input report length */ + uint16_t sc_olen; /* output report length */ + + uint8_t sc_fid; /* feature report id */ + uint8_t sc_iid; /* input report id */ + uint8_t sc_oid; /* output report id */ + 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_flags; +#define UCYCOM_FLAG_INTR_STALL 0x01 + uint8_t sc_name[16]; + uint8_t sc_iface_no; + uint8_t sc_temp_cfg[32]; +}; + +/* prototypes */ + +static device_probe_t ucycom_probe; +static device_attach_t ucycom_attach; +static device_detach_t ucycom_detach; + +static usb2_callback_t ucycom_ctrl_write_callback; +static usb2_callback_t ucycom_intr_read_clear_stall_callback; +static usb2_callback_t ucycom_intr_read_callback; + +static void ucycom_cfg_open(struct usb2_com_softc *ucom); +static void ucycom_start_read(struct usb2_com_softc *ucom); +static void ucycom_stop_read(struct usb2_com_softc *ucom); +static void ucycom_start_write(struct usb2_com_softc *ucom); +static void ucycom_stop_write(struct usb2_com_softc *ucom); +static void ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg); +static int ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t); + +static const struct usb2_config ucycom_config[UCYCOM_ENDPT_MAX] = { + + [0] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN), + .mh.flags = {}, + .mh.callback = &ucycom_ctrl_write_callback, + .mh.timeout = 1000, /* 1 second */ + }, + + [1] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = UCYCOM_MAX_IOLEN, + .mh.callback = &ucycom_intr_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ucycom_intr_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback ucycom_callback = { + .usb2_com_cfg_param = &ucycom_cfg_param, + .usb2_com_cfg_open = &ucycom_cfg_open, + .usb2_com_pre_param = &ucycom_pre_param, + .usb2_com_start_read = &ucycom_start_read, + .usb2_com_stop_read = &ucycom_stop_read, + .usb2_com_start_write = &ucycom_start_write, + .usb2_com_stop_write = &ucycom_stop_write, +}; + +static device_method_t ucycom_methods[] = { + DEVMETHOD(device_probe, ucycom_probe), + DEVMETHOD(device_attach, ucycom_attach), + DEVMETHOD(device_detach, ucycom_detach), + {0, 0} +}; + +static devclass_t ucycom_devclass; + +static driver_t ucycom_driver = { + .name = "ucycom", + .methods = ucycom_methods, + .size = sizeof(struct ucycom_softc), +}; + +DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0); +MODULE_DEPEND(ucycom, usb2_serial, 1, 1, 1); +MODULE_DEPEND(ucycom, usb2_core, 1, 1, 1); +MODULE_DEPEND(ucycom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +/* + * Supported devices + */ +static const struct usb2_device_id ucycom_devs[] = { + {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, +}; + +#define UCYCOM_DEFAULT_RATE 4800 +#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ + +static int +ucycom_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != 0) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); +} + +static int +ucycom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ucycom_softc *sc = device_get_softc(dev); + void *urd_ptr = NULL; + int32_t error; + uint16_t urd_len; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + DPRINTF("\n"); + + /* get chip model */ + sc->sc_model = USB_GET_DRIVER_INFO(uaa); + if (sc->sc_model == 0) { + device_printf(dev, "unsupported device\n"); + goto detach; + } + device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); + + /* get report descriptor */ + + error = usb2_req_get_hid_desc + (uaa->device, &Giant, + &urd_ptr, &urd_len, M_USBDEV, + UCYCOM_IFACE_INDEX); + + if (error) { + device_printf(dev, "failed to get report " + "descriptor: %s\n", + usb2_errstr(error)); + goto detach; + } + /* get report sizes */ + + sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid); + sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid); + sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid); + + if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || + (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || + (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { + device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", + sc->sc_ilen, sc->sc_olen, sc->sc_flen, + UCYCOM_MAX_IOLEN); + goto detach; + } + sc->sc_iface_no = uaa->info.bIfaceNum; + + iface_index = UCYCOM_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, ucycom_config, UCYCOM_ENDPT_MAX, + sc, &Giant); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &ucycom_callback, &Giant); + + if (error) { + goto detach; + } + if (urd_ptr) { + free(urd_ptr, M_USBDEV); + } + return (0); /* success */ + +detach: + if (urd_ptr) { + free(urd_ptr, M_USBDEV); + } + ucycom_detach(dev); + return (ENXIO); +} + +static int +ucycom_detach(device_t dev) +{ + struct ucycom_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_ENDPT_MAX); + + return (0); +} + +static void +ucycom_cfg_open(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + /* set default configuration */ + ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); + return; +} + +static void +ucycom_start_read(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +ucycom_stop_read(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +ucycom_start_write(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ucycom_stop_write(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +ucycom_ctrl_write_callback(struct usb2_xfer *xfer) +{ + struct ucycom_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + uint8_t data[2]; + uint8_t offset; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + case USB_ST_SETUP: + + switch (sc->sc_model) { + case MODEL_CY7C63743: + offset = 1; + break; + case MODEL_CY7C64013: + offset = 2; + break; + default: + offset = 0; + break; + } + + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset, + sc->sc_olen - offset, &actlen)) { + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, sc->sc_olen); + + switch (sc->sc_model) { + case MODEL_CY7C63743: + data[0] = actlen; + break; + case MODEL_CY7C64013: + data[0] = 0; + data[1] = actlen; + break; + default: + break; + } + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_copy_in(xfer->frbuffers + 1, 0, data, offset); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = sc->sc_olen; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + return; + } + DPRINTF("error=%s\n", + usb2_errstr(xfer->error)); + goto tr_transferred; + } +} + +static void +ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) +{ + struct usb2_device_request req; + uint16_t len; + usb2_error_t err; + + len = sc->sc_flen; + if (len > sizeof(sc->sc_temp_cfg)) { + len = sizeof(sc->sc_temp_cfg); + } + sc->sc_cfg = cfg; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_SET_REPORT; + USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, len); + + sc->sc_temp_cfg[0] = (baud & 0xff); + sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; + sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; + sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; + sc->sc_temp_cfg[4] = cfg; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + return; + } + err = usb2_do_request_flags + (sc->sc_udev, &Giant, &req, sc->sc_temp_cfg, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + return; +} + +static int +ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + switch (t->c_ospeed) { + 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); + } + return (0); +} + +static void +ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct ucycom_softc *sc = ucom->sc_parent; + uint8_t cfg; + + DPRINTF("\n"); + + if (t->c_cflag & CIGNORE) { + cfg = sc->sc_cfg; + } else { + cfg = 0; + switch (t->c_cflag & CSIZE) { + default: + case CS8: + ++cfg; + case CS7: + ++cfg; + case CS6: + ++cfg; + case CS5: + break; + } + + 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; + } + + ucycom_cfg_write(sc, t->c_ospeed, cfg); + return; +} + +static void +ucycom_intr_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ucycom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UCYCOM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ucycom_intr_read_callback(struct usb2_xfer *xfer) +{ + struct ucycom_softc *sc = xfer->priv_sc; + uint8_t buf[2]; + uint32_t offset; + uint32_t len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + switch (sc->sc_model) { + case MODEL_CY7C63743: + if (xfer->actlen < 1) { + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 1); + + sc->sc_ist = buf[0] & ~0x07; + len = buf[0] & 0x07; + + (xfer->actlen)--; + + offset = 1; + + break; + + case MODEL_CY7C64013: + if (xfer->actlen < 2) { + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 2); + + sc->sc_ist = buf[0] & ~0x07; + len = buf[1]; + + (xfer->actlen) -= 2; + + offset = 2; + + break; + + default: + DPRINTFN(0, "unsupported model number!\n"); + goto tr_setup; + } + + if (len > xfer->actlen) { + len = xfer->actlen; + } + if (len) { + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, + offset, len); + } + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UCYCOM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + } else { + xfer->frlengths[0] = sc->sc_ilen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UCYCOM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} diff --git a/sys/dev/usb2/serial/ufoma2.c b/sys/dev/usb2/serial/ufoma2.c new file mode 100644 index 000000000000..5c04d0c9a26d --- /dev/null +++ b/sys/dev/usb2/serial/ufoma2.c @@ -0,0 +1,1198 @@ +/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2005, Takanori Watanabe + * Copyright (c) 2003, 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. + */ + +/*- + * 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: + * - Implement a Call Device for modems without multiplexed commands. + */ + +/* + * NOTE: all function names beginning like "ufoma_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct ufoma_mobile_acm_descriptor { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bType; + uint8_t bMode[1]; +} __packed usb2_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_ACKNOWLEDGE 0x31 + +#define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */ +#define UFOMA_CMD_BUF_SIZE 64 /* bytes */ + +#define UFOMA_BULK_BUF_SIZE 1024 /* bytes */ + +#define UFOMA_CTRL_ENDPT_MAX 4 /* units */ +#define UFOMA_BULK_ENDPT_MAX 4 /* units */ + +struct ufoma_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + struct cv sc_cv; + + struct usb2_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX]; + struct usb2_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX]; + uint8_t *sc_modetable; + device_t sc_dev; + struct usb2_device *sc_udev; + + uint32_t sc_unit; + + uint16_t sc_line; + + uint8_t sc_num_msg; + uint8_t sc_is_pseudo; + uint8_t sc_ctrl_iface_no; + uint8_t sc_ctrl_iface_index; + uint8_t sc_data_iface_no; + uint8_t sc_data_iface_index; + uint8_t sc_cm_cap; + uint8_t sc_acm_cap; + uint8_t sc_lsr; + uint8_t sc_msr; + uint8_t sc_modetoactivate; + uint8_t sc_currentmode; + uint8_t sc_flags; +#define UFOMA_FLAG_INTR_STALL 0x01 +#define UFOMA_FLAG_BULK_WRITE_STALL 0x02 +#define UFOMA_FLAG_BULK_READ_STALL 0x04 + + uint8_t sc_name[16]; +}; + +/* prototypes */ + +static device_probe_t ufoma_probe; +static device_attach_t ufoma_attach; +static device_detach_t ufoma_detach; + +static usb2_callback_t ufoma_ctrl_read_callback; +static usb2_callback_t ufoma_ctrl_write_callback; +static usb2_callback_t ufoma_intr_clear_stall_callback; +static usb2_callback_t ufoma_intr_callback; +static usb2_callback_t ufoma_bulk_write_callback; +static usb2_callback_t ufoma_bulk_write_clear_stall_callback; +static usb2_callback_t ufoma_bulk_read_callback; +static usb2_callback_t ufoma_bulk_read_clear_stall_callback; + +static void ufoma_cfg_do_request(struct ufoma_softc *sc, struct usb2_device_request *req, void *data); +static void *ufoma_get_intconf(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id, uint8_t type, uint8_t subtype); +static void ufoma_cfg_link_state(struct ufoma_softc *sc); +static void ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state); +static void ufoma_cfg_open(struct usb2_com_softc *ucom); +static void ufoma_cfg_close(struct usb2_com_softc *ucom); +static void ufoma_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void ufoma_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static int ufoma_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void ufoma_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static int ufoma_modem_setup(device_t dev, struct ufoma_softc *sc, struct usb2_attach_arg *uaa); +static void ufoma_start_read(struct usb2_com_softc *ucom); +static void ufoma_stop_read(struct usb2_com_softc *ucom); +static void ufoma_start_write(struct usb2_com_softc *ucom); +static void ufoma_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config + ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { + + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = sizeof(struct usb2_cdc_notification), + .mh.callback = &ufoma_intr_callback, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ufoma_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + UFOMA_CMD_BUF_SIZE), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &ufoma_ctrl_read_callback, + .mh.timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + 1), + .mh.flags = {}, + .mh.callback = &ufoma_ctrl_write_callback, + .mh.timeout = 1000, /* 1 second */ + }, +}; + +static const struct usb2_config + ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UFOMA_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &ufoma_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UFOMA_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ufoma_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ufoma_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ufoma_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback ufoma_callback = { + .usb2_com_cfg_get_status = &ufoma_cfg_get_status, + .usb2_com_cfg_set_dtr = &ufoma_cfg_set_dtr, + .usb2_com_cfg_set_rts = &ufoma_cfg_set_rts, + .usb2_com_cfg_set_break = &ufoma_cfg_set_break, + .usb2_com_cfg_param = &ufoma_cfg_param, + .usb2_com_cfg_open = &ufoma_cfg_open, + .usb2_com_cfg_close = &ufoma_cfg_close, + .usb2_com_pre_param = &ufoma_pre_param, + .usb2_com_start_read = &ufoma_start_read, + .usb2_com_stop_read = &ufoma_stop_read, + .usb2_com_start_write = &ufoma_start_write, + .usb2_com_stop_write = &ufoma_stop_write, +}; + +static device_method_t ufoma_methods[] = { + /* Device methods */ + DEVMETHOD(device_probe, ufoma_probe), + DEVMETHOD(device_attach, ufoma_attach), + DEVMETHOD(device_detach, ufoma_detach), + {0, 0} +}; + +static devclass_t ufoma_devclass; + +static driver_t ufoma_driver = { + .name = "ufoma", + .methods = ufoma_methods, + .size = sizeof(struct ufoma_softc), +}; + +DRIVER_MODULE(ufoma, ushub, ufoma_driver, ufoma_devclass, NULL, 0); +MODULE_DEPEND(ufoma, usb2_serial, 1, 1, 1); +MODULE_DEPEND(ufoma, usb2_core, 1, 1, 1); +MODULE_DEPEND(ufoma, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +ufoma_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + struct usb2_config_descriptor *cd; + usb2_mcpc_acm_descriptor *mad; + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + id = usb2_get_interface_descriptor(uaa->iface); + cd = usb2_get_config_descriptor(uaa->device); + + if ((id == NULL) || + (cd == NULL) || + (id->bInterfaceClass != UICLASS_CDC) || + (id->bInterfaceSubClass != UISUBCLASS_MCPC)) { + return (ENXIO); + } + mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); + if (mad == NULL) { + return (ENXIO); + } +#ifndef UFOMA_HANDSFREE + if ((mad->bType == UMCPC_ACM_TYPE_AB5) || + (mad->bType == UMCPC_ACM_TYPE_AB6)) { + return (ENXIO); + } +#endif + return (0); +} + +static int +ufoma_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ufoma_softc *sc = device_get_softc(dev); + struct usb2_config_descriptor *cd; + struct usb2_interface_descriptor *id; + usb2_mcpc_acm_descriptor *mad; + uint8_t elements; + int32_t error; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + usb2_cv_init(&sc->sc_cv, "CWAIT"); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + DPRINTF("\n"); + + /* setup control transfers */ + + cd = usb2_get_config_descriptor(uaa->device); + id = usb2_get_interface_descriptor(uaa->iface); + sc->sc_ctrl_iface_no = id->bInterfaceNumber; + sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex; + + error = usb2_transfer_setup(uaa->device, + &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer, + ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &Giant); + + if (error) { + device_printf(dev, "allocating control USB " + "transfers failed!\n"); + goto detach; + } + mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); + if (mad == NULL) { + goto detach; + } + if (mad->bFunctionLength < sizeof(*mad)) { + device_printf(dev, "invalid MAD descriptor\n"); + goto detach; + } + if ((mad->bType == UMCPC_ACM_TYPE_AB5) || + (mad->bType == UMCPC_ACM_TYPE_AB6)) { + sc->sc_is_pseudo = 1; + } else { + sc->sc_is_pseudo = 0; + if (ufoma_modem_setup(dev, sc, uaa)) { + goto detach; + } + } + + elements = (mad->bFunctionLength - sizeof(*mad) + 1); + + /* initialize mode variables */ + + sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK); + + if (sc->sc_modetable == NULL) { + goto detach; + } + 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]; + + /* clear stall at first run */ + sc->sc_flags |= (UFOMA_FLAG_BULK_WRITE_STALL | + UFOMA_FLAG_BULK_READ_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &ufoma_callback, &Giant); + if (error) { + DPRINTF("usb2_com_attach failed\n"); + goto detach; + } + return (0); /* success */ + +detach: + ufoma_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ufoma_detach(device_t dev) +{ + struct ufoma_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); + + usb2_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); + + if (sc->sc_modetable) { + free(sc->sc_modetable, M_USBDEV); + } + usb2_cv_destroy(&sc->sc_cv); + + return (0); +} + +static void +ufoma_cfg_do_request(struct ufoma_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &Giant, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void * +ufoma_get_intconf(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id, + uint8_t type, uint8_t subtype) +{ + struct usb2_descriptor *desc = (void *)id; + + while ((desc = usb2_desc_foreach(cd, desc))) { + + if (desc->bDescriptorType == UDESC_INTERFACE) { + return (NULL); + } + if ((desc->bDescriptorType == type) && + (desc->bDescriptorSubtype == subtype)) { + break; + } + } + return (desc); +} + +static void +ufoma_cfg_link_state(struct ufoma_softc *sc) +{ + struct usb2_device_request req; + int32_t error; + + req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; + req.bRequest = UMCPC_SET_LINK; + USETW(req.wValue, UMCPC_CM_MOBILE_ACM); + USETW(req.wIndex, sc->sc_ctrl_iface_no); + USETW(req.wLength, sc->sc_modetable[0]); + + ufoma_cfg_do_request(sc, &req, sc->sc_modetable); + + error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz); + + if (error) { + DPRINTF("NO response\n"); + } + return; +} + +static void +ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state) +{ + struct usb2_device_request req; + int32_t error; + + req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; + req.bRequest = UMCPC_ACTIVATE_MODE; + USETW(req.wValue, state); + USETW(req.wIndex, sc->sc_ctrl_iface_no); + USETW(req.wLength, 0); + + ufoma_cfg_do_request(sc, &req, NULL); + + error = usb2_cv_timedwait(&sc->sc_cv, &Giant, + (UFOMA_MAX_TIMEOUT * hz)); + if (error) { + DPRINTF("No response\n"); + } + return; +} + +static void +ufoma_ctrl_read_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + if (xfer->aframes != xfer->nframes) { + goto tr_setup; + } + if (xfer->frlengths[1] > 0) { + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers + 1, + 0, xfer->frlengths[1]); + } + case USB_ST_SETUP: +tr_setup: + if (sc->sc_num_msg) { + sc->sc_num_msg--; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; + USETW(req.wIndex, sc->sc_ctrl_iface_no); + USETW(req.wValue, 0); + USETW(req.wLength, UFOMA_CMD_BUF_SIZE); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = UFOMA_CMD_BUF_SIZE; + xfer->nframes = 2; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + DPRINTF("error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error == USB_ERR_CANCELLED) { + return; + } else { + goto tr_setup; + } + + goto tr_transferred; + } +} + +static void +ufoma_ctrl_write_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + case USB_ST_SETUP: +tr_setup: + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, + 0, 1, &actlen)) { + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; + USETW(req.wIndex, sc->sc_ctrl_iface_no); + USETW(req.wValue, 0); + USETW(req.wLength, 1); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = 1; + xfer->nframes = 2; + + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + DPRINTF("error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error == USB_ERR_CANCELLED) { + return; + } else { + goto tr_setup; + } + + goto tr_transferred; + } +} + +static void +ufoma_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_ctrl_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UFOMA_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ufoma_intr_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_cdc_notification pkt; + uint16_t wLen; + uint16_t temp; + uint8_t mstatus; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen < 8) { + DPRINTF("too short message\n"); + goto tr_setup; + } + if (xfer->actlen > sizeof(pkt)) { + DPRINTF("truncating message\n"); + xfer->actlen = sizeof(pkt); + } + usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen); + + xfer->actlen -= 8; + + wLen = UGETW(pkt.wLength); + if (xfer->actlen > wLen) { + xfer->actlen = wLen; + } + if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) && + (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) { + temp = UGETW(pkt.wValue); + sc->sc_currentmode = (temp >> 8); + if (!(temp & 0xff)) { + DPRINTF("Mode change failed!\n"); + } + usb2_cv_signal(&sc->sc_cv); + } + if (pkt.bmRequestType != UCDC_NOTIFICATION) { + goto tr_setup; + } + switch (pkt.bNotification) { + case UCDC_N_RESPONSE_AVAILABLE: + if (!(sc->sc_is_pseudo)) { + DPRINTF("Wrong serial state!\n"); + break; + } + if (sc->sc_num_msg != 0xFF) { + sc->sc_num_msg++; + } + usb2_transfer_start(sc->sc_ctrl_xfer[3]); + break; + + case UCDC_N_SERIAL_STATE: + if (sc->sc_is_pseudo) { + DPRINTF("Wrong serial state!\n"); + break; + } + /* + * Set the serial state in ucom driver based on + * the bits from the notify message + */ + if (xfer->actlen < 2) { + DPRINTF("invalid notification " + "length, %d bytes!\n", xfer->actlen); + break; + } + DPRINTF("notify bytes = 0x%02x, 0x%02x\n", + pkt.data[0], pkt.data[1]); + + /* currently, lsr is always zero. */ + sc->sc_lsr = 0; + sc->sc_msr = 0; + + mstatus = pkt.data[0]; + + if (mstatus & UCDC_N_SERIAL_RI) { + sc->sc_msr |= SER_RI; + } + if (mstatus & UCDC_N_SERIAL_DSR) { + sc->sc_msr |= SER_DSR; + } + if (mstatus & UCDC_N_SERIAL_DCD) { + sc->sc_msr |= SER_DCD; + } + usb2_com_status_change(&sc->sc_ucom); + break; + + default: + break; + } + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UFOMA_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_ctrl_xfer[1]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= UFOMA_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_ctrl_xfer[1]); + } + return; + + } +} + +static void +ufoma_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flags & UFOMA_FLAG_BULK_WRITE_STALL) { + usb2_transfer_start(sc->sc_bulk_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UFOMA_BULK_BUF_SIZE, &actlen)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UFOMA_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(sc->sc_bulk_xfer[2]); + } + return; + + } +} + +static void +ufoma_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_bulk_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UFOMA_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ufoma_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, + xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flags & UFOMA_FLAG_BULK_READ_STALL) { + usb2_transfer_start(sc->sc_bulk_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UFOMA_FLAG_BULK_READ_STALL; + usb2_transfer_start(sc->sc_bulk_xfer[3]); + } + return; + + } +} + +static void +ufoma_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ufoma_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_bulk_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UFOMA_FLAG_BULK_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ufoma_cfg_open(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + /* empty input queue */ + + if (sc->sc_num_msg != 0xFF) { + sc->sc_num_msg++; + } + if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) { + ufoma_cfg_link_state(sc); + } + if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) { + ufoma_cfg_activate_state(sc, sc->sc_modetoactivate); + } + return; +} + +static void +ufoma_cfg_close(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED); + return; +} + +static void +ufoma_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ufoma_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t wValue; + + if (sc->sc_is_pseudo) { + return; + } + if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) { + return; + } + wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SEND_BREAK; + USETW(req.wValue, wValue); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + ufoma_cfg_do_request(sc, &req, 0); + return; +} + +static void +ufoma_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static void +ufoma_cfg_set_line_state(struct ufoma_softc *sc) +{ + struct usb2_device_request req; + + /* Don't send line state emulation request for OBEX port */ + if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) { + return; + } + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + ufoma_cfg_do_request(sc, &req, 0); + return; +} + +static void +ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_is_pseudo) { + return; + } + if (onoff) + sc->sc_line |= UCDC_LINE_DTR; + else + sc->sc_line &= ~UCDC_LINE_DTR; + + ufoma_cfg_set_line_state(sc); + return; +} + +static void +ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_is_pseudo) { + return; + } + if (onoff) + sc->sc_line |= UCDC_LINE_RTS; + else + sc->sc_line &= ~UCDC_LINE_RTS; + + ufoma_cfg_set_line_state(sc); + return; +} + +static int +ufoma_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + return (0); /* we accept anything */ +} + +static void +ufoma_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct ufoma_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + struct usb2_cdc_line_state ls; + + if (sc->sc_is_pseudo || + (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { + return; + } + DPRINTF("\n"); + + bzero(&ls, sizeof(ls)); + + USETDW(ls.dwDTERate, t->c_ospeed); + + if (t->c_cflag & CSTOPB) { + ls.bCharFormat = UCDC_STOP_BIT_2; + } else { + ls.bCharFormat = UCDC_STOP_BIT_1; + } + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + ls.bParityType = UCDC_PARITY_ODD; + } else { + ls.bParityType = UCDC_PARITY_EVEN; + } + } else { + ls.bParityType = UCDC_PARITY_NONE; + } + + switch (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; + } + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_LINE_CODING; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, UCDC_LINE_STATE_LENGTH); + + ufoma_cfg_do_request(sc, &req, &ls); + return; +} + +static int +ufoma_modem_setup(device_t dev, struct ufoma_softc *sc, + struct usb2_attach_arg *uaa) +{ + struct usb2_config_descriptor *cd; + struct usb2_cdc_acm_descriptor *acm; + struct usb2_cdc_cm_descriptor *cmd; + struct usb2_interface_descriptor *id; + struct usb2_interface *iface; + uint8_t i; + int32_t error; + + cd = usb2_get_config_descriptor(uaa->device); + id = usb2_get_interface_descriptor(uaa->iface); + + cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); + + if ((cmd == NULL) || + (cmd->bLength < sizeof(*cmd))) { + return (EINVAL); + } + sc->sc_cm_cap = cmd->bmCapabilities; + sc->sc_data_iface_no = cmd->bDataInterface; + + acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); + + if ((acm == NULL) || + (acm->bLength < sizeof(*acm))) { + return (EINVAL); + } + sc->sc_acm_cap = acm->bmCapabilities; + + device_printf(dev, "data interface %d, has %sCM over data, " + "has %sbreak\n", + 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 "); + + /* get the data interface too */ + + for (i = 0;; i++) { + + iface = usb2_get_iface(uaa->device, i); + + if (iface) { + + id = usb2_get_interface_descriptor(iface); + + if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { + sc->sc_data_iface_index = i; + usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + break; + } + } else { + device_printf(dev, "no data interface!\n"); + return (EINVAL); + } + } + + error = usb2_transfer_setup(uaa->device, + &sc->sc_data_iface_index, sc->sc_bulk_xfer, + ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &Giant); + + if (error) { + device_printf(dev, "allocating BULK USB " + "transfers failed!\n"); + return (EINVAL); + } + return (0); +} + +static void +ufoma_start_read(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + /* start interrupt transfer */ + usb2_transfer_start(sc->sc_ctrl_xfer[0]); + + /* start data transfer */ + if (sc->sc_is_pseudo) { + usb2_transfer_start(sc->sc_ctrl_xfer[2]); + } else { + usb2_transfer_start(sc->sc_bulk_xfer[1]); + } + return; +} + +static void +ufoma_stop_read(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + /* stop interrupt transfer */ + usb2_transfer_stop(sc->sc_ctrl_xfer[1]); + usb2_transfer_stop(sc->sc_ctrl_xfer[0]); + + /* stop data transfer */ + if (sc->sc_is_pseudo) { + usb2_transfer_stop(sc->sc_ctrl_xfer[2]); + } else { + usb2_transfer_stop(sc->sc_bulk_xfer[3]); + usb2_transfer_stop(sc->sc_bulk_xfer[1]); + } + return; +} + +static void +ufoma_start_write(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_is_pseudo) { + usb2_transfer_start(sc->sc_ctrl_xfer[3]); + } else { + usb2_transfer_start(sc->sc_bulk_xfer[0]); + } + return; +} + +static void +ufoma_stop_write(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_is_pseudo) { + usb2_transfer_stop(sc->sc_ctrl_xfer[3]); + } else { + usb2_transfer_stop(sc->sc_bulk_xfer[2]); + usb2_transfer_stop(sc->sc_bulk_xfer[0]); + } + return; +} diff --git a/sys/dev/usb2/serial/uftdi2.c b/sys/dev/usb2/serial/uftdi2.c new file mode 100644 index 000000000000..d620441c7e02 --- /dev/null +++ b/sys/dev/usb2/serial/uftdi2.c @@ -0,0 +1,868 @@ +/* $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 +__FBSDID("$FreeBSD$"); + +/* + * NOTE: all function names beginning like "uftdi_cfg_" can only + * be called from within the config thread function ! + */ + +/* + * FTDI FT8U100AX serial adapter driver + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uftdi_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if USB_DEBUG +static int uftdi_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); +SYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW, + &uftdi_debug, 0, "Debug level"); +#endif + +#define UFTDI_CONFIG_INDEX 0 +#define UFTDI_IFACE_INDEX 0 +#define UFTDI_ENDPT_MAX 4 + +#define UFTDI_IBUFSIZE 64 /* bytes, maximum number of bytes per + * frame */ +#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to + * do size encoding */ + +struct uftdi_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[UFTDI_ENDPT_MAX]; + device_t sc_dev; + + uint32_t sc_unit; + enum uftdi_type sc_type; + + uint16_t sc_last_lcr; + + uint8_t sc_iface_index; + uint8_t sc_hdrlen; + + uint8_t sc_msr; + uint8_t sc_lsr; + + uint8_t sc_flag; +#define UFTDI_FLAG_WRITE_STALL 0x01 +#define UFTDI_FLAG_READ_STALL 0x02 + + uint8_t sc_name[16]; +}; + +struct uftdi_param_config { + uint16_t rate; + uint16_t lcr; + uint8_t v_start; + uint8_t v_stop; + uint8_t v_flow; +}; + +/* prototypes */ + +static device_probe_t uftdi_probe; +static device_attach_t uftdi_attach; +static device_detach_t uftdi_detach; + +static usb2_callback_t uftdi_write_callback; +static usb2_callback_t uftdi_write_clear_stall_callback; +static usb2_callback_t uftdi_read_callback; +static usb2_callback_t uftdi_read_clear_stall_callback; + +static void uftdi_cfg_do_request(struct uftdi_softc *sc, struct usb2_device_request *req, void *data); +static void uftdi_cfg_open(struct usb2_com_softc *ucom); +static void uftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void uftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void uftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static int uftdi_set_parm_soft(struct termios *t, struct uftdi_param_config *cfg, uint8_t type); +static int uftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void uftdi_start_read(struct usb2_com_softc *ucom); +static void uftdi_stop_read(struct usb2_com_softc *ucom); +static void uftdi_start_write(struct usb2_com_softc *ucom); +static void uftdi_stop_write(struct usb2_com_softc *ucom); +static uint8_t uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate); + +static const struct usb2_config uftdi_config[UFTDI_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UFTDI_OBUFSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uftdi_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UFTDI_IBUFSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uftdi_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uftdi_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &uftdi_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback uftdi_callback = { + .usb2_com_cfg_get_status = &uftdi_cfg_get_status, + .usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr, + .usb2_com_cfg_set_rts = &uftdi_cfg_set_rts, + .usb2_com_cfg_set_break = &uftdi_cfg_set_break, + .usb2_com_cfg_param = &uftdi_cfg_param, + .usb2_com_cfg_open = &uftdi_cfg_open, + .usb2_com_pre_param = &uftdi_pre_param, + .usb2_com_start_read = &uftdi_start_read, + .usb2_com_stop_read = &uftdi_stop_read, + .usb2_com_start_write = &uftdi_start_write, + .usb2_com_stop_write = &uftdi_stop_write, +}; + +static device_method_t uftdi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uftdi_probe), + DEVMETHOD(device_attach, uftdi_attach), + DEVMETHOD(device_detach, uftdi_detach), + + {0, 0} +}; + +static devclass_t uftdi_devclass; + +static driver_t uftdi_driver = { + .name = "uftdi", + .methods = uftdi_methods, + .size = sizeof(struct uftdi_softc), +}; + +DRIVER_MODULE(uftdi, ushub, uftdi_driver, uftdi_devclass, NULL, 0); +MODULE_DEPEND(uftdi, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uftdi, usb2_core, 1, 1, 1); +MODULE_DEPEND(uftdi, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static struct usb2_device_id uftdi_devs[] = { + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)}, +}; + +static int +uftdi_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) { + return (ENXIO); + } + /* attach to all present interfaces */ + + return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa)); +} + +static int +uftdi_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uftdi_softc *sc = device_get_softc(dev); + int error; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + DPRINTF("\n"); + + sc->sc_iface_index = uaa->info.bIfaceIndex; + sc->sc_type = USB_GET_DRIVER_INFO(uaa); + + switch (sc->sc_type) { + case UFTDI_TYPE_SIO: + sc->sc_hdrlen = 1; + break; + case UFTDI_TYPE_8U232AM: + default: + sc->sc_hdrlen = 0; + break; + } + + error = usb2_transfer_setup(uaa->device, + &sc->sc_iface_index, sc->sc_xfer, uftdi_config, + UFTDI_ENDPT_MAX, sc, &Giant); + + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; + + /* clear stall at first run */ + + sc->sc_flag |= (UFTDI_FLAG_WRITE_STALL | + UFTDI_FLAG_READ_STALL); + + /* set a valid "lcr" value */ + + sc->sc_last_lcr = + (FTDI_SIO_SET_DATA_STOP_BITS_2 | + FTDI_SIO_SET_DATA_PARITY_NONE | + FTDI_SIO_SET_DATA_BITS(8)); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uftdi_callback, &Giant); + if (error) { + goto detach; + } + return (0); /* success */ + +detach: + uftdi_detach(dev); + return (ENXIO); +} + +static int +uftdi_detach(device_t dev) +{ + struct uftdi_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UFTDI_ENDPT_MAX); + + return (0); +} + +static void +uftdi_cfg_do_request(struct uftdi_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &Giant, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +uftdi_cfg_open(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + uint16_t wIndex = ucom->sc_portno; + struct usb2_device_request req; + + DPRINTF(""); + + /* 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, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + /* 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, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + /* + * NOTE: with the new UCOM layer there will always be a + * "uftdi_cfg_param()" call after "open()", so there is no need for + * "open()" to configure anything + */ + return; +} + +static void +uftdi_write_callback(struct usb2_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + uint32_t actlen; + uint8_t buf[1]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UFTDI_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, + sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen, + &actlen)) { + + if (sc->sc_hdrlen > 0) { + buf[0] = + FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno); + usb2_copy_in(xfer->frbuffers, 0, buf, 1); + } + xfer->frlengths[0] = actlen + sc->sc_hdrlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UFTDI_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uftdi_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uftdi_read_callback(struct usb2_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + uint8_t buf[2]; + uint8_t msr; + uint8_t lsr; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < 2) { + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, 2); + + msr = FTDI_GET_MSR(buf); + lsr = FTDI_GET_LSR(buf); + + if ((sc->sc_msr != msr) || + ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { + DPRINTF("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; + + usb2_com_status_change(&sc->sc_ucom); + } + xfer->actlen -= 2; + + if (xfer->actlen > 0) { + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2, + xfer->actlen); + } + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flag & UFTDI_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UFTDI_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uftdi_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + uint16_t wIndex = ucom->sc_portno; + uint16_t wValue; + struct usb2_device_request req; + + wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_MODEM_CTRL; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + uint16_t wIndex = ucom->sc_portno; + uint16_t wValue; + struct usb2_device_request req; + + wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_MODEM_CTRL; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + uint16_t wIndex = ucom->sc_portno; + uint16_t wValue; + struct usb2_device_request req; + + if (onoff) { + sc->sc_last_lcr |= FTDI_SIO_SET_BREAK; + } else { + sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK; + } + + wValue = sc->sc_last_lcr; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_DATA; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + return; +} + +static int +uftdi_set_parm_soft(struct termios *t, + struct uftdi_param_config *cfg, uint8_t type) +{ + bzero(cfg, sizeof(*cfg)); + + switch (type) { + case UFTDI_TYPE_SIO: + switch (t->c_ospeed) { + case 300: + cfg->rate = ftdi_sio_b300; + break; + case 600: + cfg->rate = ftdi_sio_b600; + break; + case 1200: + cfg->rate = ftdi_sio_b1200; + break; + case 2400: + cfg->rate = ftdi_sio_b2400; + break; + case 4800: + cfg->rate = ftdi_sio_b4800; + break; + case 9600: + cfg->rate = ftdi_sio_b9600; + break; + case 19200: + cfg->rate = ftdi_sio_b19200; + break; + case 38400: + cfg->rate = ftdi_sio_b38400; + break; + case 57600: + cfg->rate = ftdi_sio_b57600; + break; + case 115200: + cfg->rate = ftdi_sio_b115200; + break; + default: + return (EINVAL); + } + break; + + case UFTDI_TYPE_8U232AM: + if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) { + return (EINVAL); + } + break; + } + + if (t->c_cflag & CSTOPB) + cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2; + else + cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1; + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD; + } else { + cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN; + } + } else { + cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE; + } + + switch (t->c_cflag & CSIZE) { + case CS5: + cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5); + break; + + case CS6: + cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6); + break; + + case CS7: + cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7); + break; + + case CS8: + cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8); + break; + } + + if (t->c_cflag & CRTSCTS) { + cfg->v_flow = FTDI_SIO_RTS_CTS_HS; + } else if (t->c_iflag & (IXON | IXOFF)) { + cfg->v_flow = FTDI_SIO_XON_XOFF_HS; + cfg->v_start = t->c_cc[VSTART]; + cfg->v_stop = t->c_cc[VSTOP]; + } else { + cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL; + } + + return (0); +} + +static int +uftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uftdi_softc *sc = ucom->sc_parent; + struct uftdi_param_config cfg; + + DPRINTF("\n"); + + return (uftdi_set_parm_soft(t, &cfg, sc->sc_type)); +} + +static void +uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uftdi_softc *sc = ucom->sc_parent; + uint16_t wIndex = ucom->sc_portno; + struct uftdi_param_config cfg; + struct usb2_device_request req; + + if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) { + /* should not happen */ + return; + } + sc->sc_last_lcr = cfg.lcr; + + DPRINTF("\n"); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_BAUD_RATE; + USETW(req.wValue, cfg.rate); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_DATA; + USETW(req.wValue, cfg.lcr); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_FLOW_CTRL; + USETW2(req.wValue, cfg.v_stop, cfg.v_start); + USETW2(req.wIndex, cfg.v_flow, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + return; +} + +static void +uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + DPRINTF("msr=0x%02x lsr=0x%02x\n", + sc->sc_msr, sc->sc_lsr); + + *msr = sc->sc_msr; + *lsr = sc->sc_lsr; + return; +} + +static void +uftdi_start_read(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uftdi_stop_read(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uftdi_start_write(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uftdi_stop_write(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +/*------------------------------------------------------------------------* + * uftdi_8u232am_getrate + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate) +{ + /* Table of the nearest even powers-of-2 for values 0..15. */ + static const uint8_t roundoff[16] = { + 0, 2, 2, 4, 4, 4, 8, 8, + 8, 8, 8, 8, 16, 16, 16, 16, + }; + uint32_t d; + uint32_t freq; + uint16_t result; + + if ((speed < 178) || (speed > ((3000000 * 100) / 97))) + return (1); /* prevent numerical overflow */ + + /* 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 < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) || + (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 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); +} diff --git a/sys/dev/usb2/serial/uftdi2_reg.h b/sys/dev/usb2/serial/uftdi2_reg.h new file mode 100644 index 000000000000..0074bc5d701b --- /dev/null +++ b/sys/dev/usb2/serial/uftdi2_reg.h @@ -0,0 +1,340 @@ +/* $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/dev/usb2/serial/ugensa2.c b/sys/dev/usb2/serial/ugensa2.c new file mode 100644 index 000000000000..e3ae41906f9a --- /dev/null +++ b/sys/dev/usb2/serial/ugensa2.c @@ -0,0 +1,462 @@ +/* $FreeBSD$ */ +/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */ + +/* + * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland C. Dowdeswell . + * + * 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. + */ + +/* + * NOTE: all function names beginning like "ugensa_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UGENSA_BUF_SIZE 2048 /* bytes */ +#define UGENSA_N_TRANSFER 4 /* units */ +#define UGENSA_CONFIG_INDEX 0 +#define UGENSA_IFACE_INDEX 0 +#define UGENSA_IFACE_MAX 8 /* exclusivly */ + +struct ugensa_sub_softc { + struct usb2_com_softc *sc_usb2_com_ptr; + struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER]; + + uint8_t sc_flags; +#define UGENSA_FLAG_BULK_READ_STALL 0x01 +#define UGENSA_FLAG_BULK_WRITE_STALL 0x02 +}; + +struct ugensa_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom[UGENSA_IFACE_MAX]; + struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX]; + + struct mtx sc_mtx; + uint8_t sc_niface; +}; + +/* prototypes */ + +static device_probe_t ugensa_probe; +static device_attach_t ugensa_attach; +static device_detach_t ugensa_detach; + +static usb2_callback_t ugensa_bulk_write_callback; +static usb2_callback_t ugensa_bulk_write_clear_stall_callback; +static usb2_callback_t ugensa_bulk_read_callback; +static usb2_callback_t ugensa_bulk_read_clear_stall_callback; + +static void ugensa_start_read(struct usb2_com_softc *ucom); +static void ugensa_stop_read(struct usb2_com_softc *ucom); +static void ugensa_start_write(struct usb2_com_softc *ucom); +static void ugensa_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config + ugensa_xfer_config[UGENSA_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UGENSA_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &ugensa_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UGENSA_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ugensa_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ugensa_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &ugensa_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback ugensa_callback = { + .usb2_com_start_read = &ugensa_start_read, + .usb2_com_stop_read = &ugensa_stop_read, + .usb2_com_start_write = &ugensa_start_write, + .usb2_com_stop_write = &ugensa_stop_write, +}; + +static device_method_t ugensa_methods[] = { + /* Device methods */ + DEVMETHOD(device_probe, ugensa_probe), + DEVMETHOD(device_attach, ugensa_attach), + DEVMETHOD(device_detach, ugensa_detach), + {0, 0} +}; + +static devclass_t ugensa_devclass; + +static driver_t ugensa_driver = { + .name = "ugensa", + .methods = ugensa_methods, + .size = sizeof(struct ugensa_softc), +}; + +DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0); +MODULE_DEPEND(ugensa, usb2_serial, 1, 1, 1); +MODULE_DEPEND(ugensa, usb2_core, 1, 1, 1); +MODULE_DEPEND(ugensa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static const struct usb2_device_id ugensa_devs[] = { + {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)}, + {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)}, + {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)}, + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270, 0)}, + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, 0)}, + {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U950D, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, 0)}, + {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, 0)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, 0)}, +}; + +static int +ugensa_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != 0) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); +} + +static int +ugensa_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ugensa_softc *sc = device_get_softc(dev); + struct ugensa_sub_softc *ssc; + struct usb2_interface *iface; + int32_t error; + uint8_t iface_index; + int x, cnt; + + if (sc == NULL) + return (ENOMEM); + + device_set_usb2_desc(dev); + mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); + + /* Figure out how many interfaces this device has got */ + for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) { + if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) || + (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) { + /* we have reached the end */ + break; + } + } + + if (cnt == 0) { + device_printf(dev, "No interfaces!\n"); + goto detach; + } + for (x = 0; x < cnt; x++) { + iface = usb2_get_iface(uaa->device, x); + if (iface->idesc->bInterfaceClass != UICLASS_VENDOR) + /* Not a serial port, most likely a SD reader */ + continue; + + ssc = sc->sc_sub + sc->sc_niface; + ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface; + + iface_index = (UGENSA_IFACE_INDEX + x); + error = usb2_transfer_setup(uaa->device, + &iface_index, ssc->sc_xfer, ugensa_xfer_config, + UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); + + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + /* clear stall at first run */ + ssc->sc_flags |= (UGENSA_FLAG_BULK_WRITE_STALL | + UGENSA_FLAG_BULK_READ_STALL); + + /* initialize port number */ + ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface; + sc->sc_niface++; + if (x != uaa->info.bIfaceIndex) + usb2_set_parent_iface(uaa->device, x, + uaa->info.bIfaceIndex); + } + device_printf(dev, "Found %d interfaces.\n", sc->sc_niface); + + error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc, + &ugensa_callback, &sc->sc_mtx); + if (error) { + DPRINTF("attach failed\n"); + goto detach; + } + return (0); /* success */ + +detach: + ugensa_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ugensa_detach(device_t dev) +{ + struct ugensa_softc *sc = device_get_softc(dev); + uint8_t x; + + usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface); + + for (x = 0; x < sc->sc_niface; x++) { + usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); + } + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +ugensa_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct ugensa_sub_softc *ssc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (ssc->sc_flags & UGENSA_FLAG_BULK_WRITE_STALL) { + usb2_transfer_start(ssc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, + UGENSA_BUF_SIZE, &actlen)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + ssc->sc_flags |= UGENSA_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(ssc->sc_xfer[2]); + } + return; + + } +} + +static void +ugensa_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ugensa_sub_softc *ssc = xfer->priv_sc; + struct usb2_xfer *xfer_other = ssc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + ssc->sc_flags &= ~UGENSA_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ugensa_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct ugensa_sub_softc *ssc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, + xfer->actlen); + + case USB_ST_SETUP: + if (ssc->sc_flags & UGENSA_FLAG_BULK_READ_STALL) { + usb2_transfer_start(ssc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + ssc->sc_flags |= UGENSA_FLAG_BULK_READ_STALL; + usb2_transfer_start(ssc->sc_xfer[3]); + } + return; + + } +} + +static void +ugensa_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ugensa_sub_softc *ssc = xfer->priv_sc; + struct usb2_xfer *xfer_other = ssc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + ssc->sc_flags &= ~UGENSA_FLAG_BULK_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ugensa_start_read(struct usb2_com_softc *ucom) +{ + struct ugensa_softc *sc = ucom->sc_parent; + struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; + + usb2_transfer_start(ssc->sc_xfer[1]); + return; +} + +static void +ugensa_stop_read(struct usb2_com_softc *ucom) +{ + struct ugensa_softc *sc = ucom->sc_parent; + struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; + + usb2_transfer_stop(ssc->sc_xfer[3]); + usb2_transfer_stop(ssc->sc_xfer[1]); + return; +} + +static void +ugensa_start_write(struct usb2_com_softc *ucom) +{ + struct ugensa_softc *sc = ucom->sc_parent; + struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; + + usb2_transfer_start(ssc->sc_xfer[0]); + return; +} + +static void +ugensa_stop_write(struct usb2_com_softc *ucom) +{ + struct ugensa_softc *sc = ucom->sc_parent; + struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; + + usb2_transfer_stop(ssc->sc_xfer[2]); + usb2_transfer_stop(ssc->sc_xfer[0]); + return; +} diff --git a/sys/dev/usb2/serial/uipaq2.c b/sys/dev/usb2/serial/uipaq2.c new file mode 100644 index 000000000000..91503d88665d --- /dev/null +++ b/sys/dev/usb2/serial/uipaq2.c @@ -0,0 +1,1408 @@ +/* $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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UIPAQ_CONFIG_INDEX 0 /* config number 1 */ +#define UIPAQ_IFACE_INDEX 0 + +#define UIPAQ_BUF_SIZE 1024 +#define UIPAQ_N_DATA_TRANSFER 4 + +struct uipaq_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer_data[UIPAQ_N_DATA_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_line; + + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* modem status register */ + uint8_t sc_flag; +#define UIPAQ_FLAG_READ_STALL 0x01 +#define UIPAQ_FLAG_WRITE_STALL 0x02 +#define UIPAQ_FLAG_INTR_STALL 0x04 +}; + +static device_probe_t uipaq_probe; +static device_attach_t uipaq_attach; +static device_detach_t uipaq_detach; + +static usb2_callback_t uipaq_write_callback; +static usb2_callback_t uipaq_read_callback; +static usb2_callback_t uipaq_write_clear_stall_callback; +static usb2_callback_t uipaq_read_clear_stall_callback; + +static void uipaq_start_read(struct usb2_com_softc *ucom); +static void uipaq_stop_read(struct usb2_com_softc *ucom); +static void uipaq_start_write(struct usb2_com_softc *ucom); +static void uipaq_stop_write(struct usb2_com_softc *ucom); +static void uipaq_cfg_do_request(struct uipaq_softc *sc, struct usb2_device_request *req, void *data); +static void uipaq_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void uipaq_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void uipaq_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); + +static const struct usb2_config uipaq_config_data[UIPAQ_N_DATA_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UIPAQ_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uipaq_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UIPAQ_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uipaq_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uipaq_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uipaq_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback uipaq_callback = { + .usb2_com_cfg_set_dtr = &uipaq_cfg_set_dtr, + .usb2_com_cfg_set_rts = &uipaq_cfg_set_rts, + .usb2_com_cfg_set_break = &uipaq_cfg_set_break, + .usb2_com_start_read = &uipaq_start_read, + .usb2_com_stop_read = &uipaq_stop_read, + .usb2_com_start_write = &uipaq_start_write, + .usb2_com_stop_write = &uipaq_stop_write, +}; + +/* + * 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 usb2_device_id uipaq_devs[] = { + /* Socket USB Sync */ + {USB_VPI(0x0104, 0x00be, 0)}, + /* USB Sync 0301 */ + {USB_VPI(0x04ad, 0x0301, 0)}, + /* USB Sync 0302 */ + {USB_VPI(0x04ad, 0x0302, 0)}, + /* USB Sync 0303 */ + {USB_VPI(0x04ad, 0x0303, 0)}, + /* GPS Pocket PC USB Sync */ + {USB_VPI(0x04ad, 0x0306, 0)}, + /* HHP PDT */ + {USB_VPI(0x0536, 0x01a0, 0)}, + /* Intermec Mobile Computer */ + {USB_VPI(0x067e, 0x1001, 0)}, + /* Linkup Systems USB Sync */ + {USB_VPI(0x094b, 0x0001, 0)}, + /* BCOM USB Sync 0065 */ + {USB_VPI(0x0960, 0x0065, 0)}, + /* BCOM USB Sync 0066 */ + {USB_VPI(0x0960, 0x0066, 0)}, + /* BCOM USB Sync 0067 */ + {USB_VPI(0x0960, 0x0067, 0)}, + /* Portatec USB Sync */ + {USB_VPI(0x0961, 0x0010, 0)}, + /* Trimble GeoExplorer */ + {USB_VPI(0x099e, 0x0052, 0)}, + /* TDS Data Collector */ + {USB_VPI(0x099e, 0x4000, 0)}, + /* Motorola iDEN Smartphone */ + {USB_VPI(0x0c44, 0x03a2, 0)}, + /* Cesscom Luxian Series */ + {USB_VPI(0x0c8e, 0x6000, 0)}, + /* Motorola PowerPad Pocket PCDevice */ + {USB_VPI(0x0cad, 0x9001, 0)}, + /* Freedom Scientific USB Sync */ + {USB_VPI(0x0f4e, 0x0200, 0)}, + /* Cyberbank USB Sync */ + {USB_VPI(0x0f98, 0x0201, 0)}, + /* Wistron USB Sync */ + {USB_VPI(0x0fb8, 0x3001, 0)}, + /* Wistron USB Sync */ + {USB_VPI(0x0fb8, 0x3002, 0)}, + /* Wistron USB Sync */ + {USB_VPI(0x0fb8, 0x3003, 0)}, + /* Wistron USB Sync */ + {USB_VPI(0x0fb8, 0x4001, 0)}, + /* E-TEN USB Sync */ + {USB_VPI(0x1066, 0x00ce, 0)}, + /* E-TEN P3XX Pocket PC */ + {USB_VPI(0x1066, 0x0300, 0)}, + /* E-TEN P5XX Pocket PC */ + {USB_VPI(0x1066, 0x0500, 0)}, + /* E-TEN P6XX Pocket PC */ + {USB_VPI(0x1066, 0x0600, 0)}, + /* E-TEN P7XX Pocket PC */ + {USB_VPI(0x1066, 0x0700, 0)}, + /* Psion Teklogix Sync 753x */ + {USB_VPI(0x1114, 0x0001, 0)}, + /* Psion Teklogix Sync netBookPro */ + {USB_VPI(0x1114, 0x0004, 0)}, + /* Psion Teklogix Sync 7525 */ + {USB_VPI(0x1114, 0x0006, 0)}, + /* VES USB Sync */ + {USB_VPI(0x1182, 0x1388, 0)}, + /* Rugged Pocket PC 2003 */ + {USB_VPI(0x11d9, 0x1002, 0)}, + /* Rugged Pocket PC 2003 */ + {USB_VPI(0x11d9, 0x1003, 0)}, + /* USB Sync 03 */ + {USB_VPI(0x1231, 0xce01, 0)}, + /* USB Sync 03 */ + {USB_VPI(0x1231, 0xce02, 0)}, + /* Mio DigiWalker PPC StrongARM */ + {USB_VPI(0x3340, 0x011c, 0)}, + /* Mio DigiWalker 338 */ + {USB_VPI(0x3340, 0x0326, 0)}, + /* Mio DigiWalker 338 */ + {USB_VPI(0x3340, 0x0426, 0)}, + /* Mio DigiWalker USB Sync */ + {USB_VPI(0x3340, 0x043a, 0)}, + /* MiTAC USB Sync 528 */ + {USB_VPI(0x3340, 0x051c, 0)}, + /* Mio DigiWalker SmartPhone USB Sync */ + {USB_VPI(0x3340, 0x053a, 0)}, + /* MiTAC USB Sync */ + {USB_VPI(0x3340, 0x071c, 0)}, + /* Generic PPC StrongARM */ + {USB_VPI(0x3340, 0x0b1c, 0)}, + /* Generic PPC USB Sync */ + {USB_VPI(0x3340, 0x0e3a, 0)}, + /* Itautec USB Sync */ + {USB_VPI(0x3340, 0x0f1c, 0)}, + /* Generic SmartPhone USB Sync */ + {USB_VPI(0x3340, 0x0f3a, 0)}, + /* Itautec USB Sync */ + {USB_VPI(0x3340, 0x1326, 0)}, + /* YAKUMO USB Sync */ + {USB_VPI(0x3340, 0x191c, 0)}, + /* Vobis USB Sync */ + {USB_VPI(0x3340, 0x2326, 0)}, + /* MEDION Winodws Moble USB Sync */ + {USB_VPI(0x3340, 0x3326, 0)}, + /* Legend USB Sync */ + {USB_VPI(0x3708, 0x20ce, 0)}, + /* Lenovo USB Sync */ + {USB_VPI(0x3708, 0x21ce, 0)}, + /* Mobile Media Technology USB Sync */ + {USB_VPI(0x4113, 0x0210, 0)}, + /* Mobile Media Technology USB Sync */ + {USB_VPI(0x4113, 0x0211, 0)}, + /* Mobile Media Technology USB Sync */ + {USB_VPI(0x4113, 0x0400, 0)}, + /* Mobile Media Technology USB Sync */ + {USB_VPI(0x4113, 0x0410, 0)}, + /* Smartphone */ + {USB_VPI(0x4505, 0x0010, 0)}, + /* SAGEM Wireless Assistant */ + {USB_VPI(0x5e04, 0xce00, 0)}, + /* c10 Series */ + {USB_VPI(USB_VENDOR_ACER, 0x1631, 0)}, + /* c20 Series */ + {USB_VPI(USB_VENDOR_ACER, 0x1632, 0)}, + /* Acer n10 Handheld USB Sync */ + {USB_VPI(USB_VENDOR_ACER, 0x16e1, 0)}, + /* Acer n20 Handheld USB Sync */ + {USB_VPI(USB_VENDOR_ACER, 0x16e2, 0)}, + /* Acer n30 Handheld USB Sync */ + {USB_VPI(USB_VENDOR_ACER, 0x16e3, 0)}, + /* ASUS USB Sync */ + {USB_VPI(USB_VENDOR_ASUS, 0x4200, 0)}, + /* ASUS USB Sync */ + {USB_VPI(USB_VENDOR_ASUS, 0x4201, 0)}, + /* ASUS USB Sync */ + {USB_VPI(USB_VENDOR_ASUS, 0x4202, 0)}, + /* ASUS USB Sync */ + {USB_VPI(USB_VENDOR_ASUS, 0x9200, 0)}, + /* ASUS USB Sync */ + {USB_VPI(USB_VENDOR_ASUS, 0x9202, 0)}, + /**/ + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535, 0)}, + /* CASIO USB Sync 2001 */ + {USB_VPI(USB_VENDOR_CASIO, 0x2001, 0)}, + /* CASIO USB Sync 2003 */ + {USB_VPI(USB_VENDOR_CASIO, 0x2003, 0)}, + /**/ + {USB_VPI(USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300, 0)}, + /* MyGuide 7000 XL USB Sync */ + {USB_VPI(USB_VENDOR_COMPAL, 0x0531, 0)}, + /* Compaq iPAQ USB Sync */ + {USB_VPI(USB_VENDOR_COMPAQ, 0x0032, 0)}, + /**/ + {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4001, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4002, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4003, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4004, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4005, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4006, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4007, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4008, 0)}, + /* Dell Axim USB Sync */ + {USB_VPI(USB_VENDOR_DELL, 0x4009, 0)}, + /* Fujitsu Siemens Computers USB Sync */ + {USB_VPI(USB_VENDOR_FSC, 0x1001, 0)}, + /* FUJITSU USB Sync */ + {USB_VPI(USB_VENDOR_FUJITSU, 0x1058, 0)}, + /* FUJITSU USB Sync */ + {USB_VPI(USB_VENDOR_FUJITSU, 0x1079, 0)}, + /* Askey USB Sync */ + {USB_VPI(USB_VENDOR_GIGASET, 0x0601, 0)}, + /* Hitachi USB Sync */ + {USB_VPI(USB_VENDOR_HITACHI, 0x0014, 0)}, + /* HP USB Sync 1612 */ + {USB_VPI(USB_VENDOR_HP, 0x1216, 0)}, + /* HP USB Sync 1620 */ + {USB_VPI(USB_VENDOR_HP, 0x2016, 0)}, + /* HP USB Sync 1621 */ + {USB_VPI(USB_VENDOR_HP, 0x2116, 0)}, + /* HP USB Sync 1622 */ + {USB_VPI(USB_VENDOR_HP, 0x2216, 0)}, + /* HP USB Sync 1630 */ + {USB_VPI(USB_VENDOR_HP, 0x3016, 0)}, + /* HP USB Sync 1631 */ + {USB_VPI(USB_VENDOR_HP, 0x3116, 0)}, + /* HP USB Sync 1632 */ + {USB_VPI(USB_VENDOR_HP, 0x3216, 0)}, + /* HP USB Sync 1640 */ + {USB_VPI(USB_VENDOR_HP, 0x4016, 0)}, + /* HP USB Sync 1641 */ + {USB_VPI(USB_VENDOR_HP, 0x4116, 0)}, + /* HP USB Sync 1642 */ + {USB_VPI(USB_VENDOR_HP, 0x4216, 0)}, + /* HP USB Sync 1650 */ + {USB_VPI(USB_VENDOR_HP, 0x5016, 0)}, + /* HP USB Sync 1651 */ + {USB_VPI(USB_VENDOR_HP, 0x5116, 0)}, + /* HP USB Sync 1652 */ + {USB_VPI(USB_VENDOR_HP, 0x5216, 0)}, + /**/ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2215, 0)}, + /**/ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_568J, 0)}, + /* HTC USB Modem */ + {USB_VPI(USB_VENDOR_HTC, 0x00cf, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a01, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a02, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a03, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a04, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a05, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a06, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a07, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a08, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a09, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0a, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0b, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0c, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0d, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0e, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a0f, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a10, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a11, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a12, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a13, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a14, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a15, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a16, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a17, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a18, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a19, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1a, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1b, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1c, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1d, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1e, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a1f, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a20, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a21, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a22, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a23, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a24, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a25, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a26, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a27, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a28, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a29, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2a, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2b, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2c, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2d, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2e, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a2f, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a30, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a31, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a32, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a33, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a34, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a35, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a36, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a37, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a38, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a39, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3a, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3b, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3c, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3d, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3e, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a3f, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a40, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a41, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a42, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a43, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a44, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a45, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a46, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a47, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a48, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a49, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4a, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4b, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4c, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4d, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4e, 0)}, + /* PocketPC USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a4f, 0)}, + /* HTC SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a50, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a52, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a53, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a54, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a55, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a56, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a57, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a58, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a59, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5a, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5b, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5c, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5d, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5e, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a5f, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a60, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a61, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a62, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a63, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a64, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a65, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a66, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a67, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a68, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a69, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6a, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6b, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6c, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6d, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6e, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a6f, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a70, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a71, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a72, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a73, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a74, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a75, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a76, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a77, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a78, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a79, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7a, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7b, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7c, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7d, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7e, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a7f, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a80, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a81, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a82, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a83, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a84, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a85, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a86, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a87, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a88, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a89, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8a, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8b, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8c, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8d, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8e, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a8f, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a90, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a91, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a92, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a93, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a94, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a95, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a96, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a97, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a98, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a99, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9a, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9b, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9c, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9d, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9e, 0)}, + /* SmartPhone USB Sync */ + {USB_VPI(USB_VENDOR_HTC, 0x0a9f, 0)}, + /* "High Tech Computer Corp" */ + {USB_VPI(USB_VENDOR_HTC, 0x0bce, 0)}, + /**/ + {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM, 0)}, + /**/ + {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE, 0)}, + /**/ + {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE, 0)}, + /* JVC USB Sync */ + {USB_VPI(USB_VENDOR_JVC, 0x3011, 0)}, + /* JVC USB Sync */ + {USB_VPI(USB_VENDOR_JVC, 0x3012, 0)}, + /* LGE USB Sync */ + {USB_VPI(USB_VENDOR_LG, 0x9c01, 0)}, + /* Microsoft USB Sync */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x00ce, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0400, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0401, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0402, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0403, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0404, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0405, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0406, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0407, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0408, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0409, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040a, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040b, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040c, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040d, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040e, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x040f, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0410, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0411, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0412, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0413, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0414, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0415, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0416, 0)}, + /* Windows Pocket PC 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0417, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0432, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0433, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0434, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0435, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0436, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0437, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0438, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0439, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043a, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043b, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043c, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043d, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043e, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x043f, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0440, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0441, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0442, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0443, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0444, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0445, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0446, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0447, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0448, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0449, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044a, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044b, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044c, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044d, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044e, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x044f, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0450, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0451, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0452, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0453, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0454, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0455, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0456, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0457, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0458, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0459, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045a, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045b, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045c, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045d, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045e, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x045f, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0460, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0461, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0462, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0463, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0464, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0465, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0466, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0467, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0468, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0469, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046a, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046b, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046c, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046d, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046e, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x046f, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0470, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0471, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0472, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0473, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0474, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0475, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0476, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0477, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0478, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x0479, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x047a, 0)}, + /* Windows Pocket PC 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x047b, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c8, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c9, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ca, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cb, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cc, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cd, 0)}, + /* Windows Smartphone 2002 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ce, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d7, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d8, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d9, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04da, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04db, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dc, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dd, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04de, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04df, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e0, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e1, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e2, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e3, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e4, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e5, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e6, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e7, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e8, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e9, 0)}, + /* Windows Smartphone 2003 */ + {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ea, 0)}, + /* Motorola MPx200 Smartphone */ + {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4204, 0)}, + /* Motorola MPc GSM */ + {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4214, 0)}, + /* Motorola MPx220 Smartphone */ + {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4224, 0)}, + /* Motorola MPc CDMA */ + {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4234, 0)}, + /* Motorola MPx100 Smartphone */ + {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4244, 0)}, + /* NEC USB Sync */ + {USB_VPI(USB_VENDOR_NEC, 0x00d5, 0)}, + /* NEC USB Sync */ + {USB_VPI(USB_VENDOR_NEC, 0x00d6, 0)}, + /* NEC USB Sync */ + {USB_VPI(USB_VENDOR_NEC, 0x00d7, 0)}, + /* NEC USB Sync */ + {USB_VPI(USB_VENDOR_NEC, 0x8024, 0)}, + /* NEC USB Sync */ + {USB_VPI(USB_VENDOR_NEC, 0x8025, 0)}, + /* Panasonic USB Sync */ + {USB_VPI(USB_VENDOR_PANASONIC, 0x2500, 0)}, + /* Samsung NEXiO USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f00, 0)}, + /* Samsung NEXiO USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f01, 0)}, + /* Samsung NEXiO USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f02, 0)}, + /* Samsung NEXiO USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f03, 0)}, + /* Samsung NEXiO USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f04, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6611, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6613, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6615, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6617, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6619, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x661b, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x662e, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6630, 0)}, + /* Samsung MITs USB Sync */ + {USB_VPI(USB_VENDOR_SAMSUNG, 0x6632, 0)}, + /* SHARP WS003SH USB Modem */ + {USB_VPI(USB_VENDOR_SHARP, 0x9102, 0)}, + /* SHARP WS004SH USB Modem */ + {USB_VPI(USB_VENDOR_SHARP, 0x9121, 0)}, + /* SHARP S01SH USB Modem */ + {USB_VPI(USB_VENDOR_SHARP, 0x9151, 0)}, +/**/ + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES, 0)}, + /* Symbol USB Sync */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2000, 0)}, + /* Symbol USB Sync 0x2001 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2001, 0)}, + /* Symbol USB Sync 0x2002 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2002, 0)}, + /* Symbol USB Sync 0x2003 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2003, 0)}, + /* Symbol USB Sync 0x2004 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2004, 0)}, + /* Symbol USB Sync 0x2005 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2005, 0)}, + /* Symbol USB Sync 0x2006 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2006, 0)}, + /* Symbol USB Sync 0x2007 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2007, 0)}, + /* Symbol USB Sync 0x2008 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2008, 0)}, + /* Symbol USB Sync 0x2009 */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x2009, 0)}, + /* Symbol USB Sync 0x200a */ + {USB_VPI(USB_VENDOR_SYMBOL, 0x200a, 0)}, + /* TOSHIBA USB Sync 0700 */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x0700, 0)}, + /* TOSHIBA Pocket PC e310 */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x0705, 0)}, + /* TOSHIBA Pocket PC e330 Series */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x0707, 0)}, + /* TOSHIBA Pocket PC e350Series */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x0708, 0)}, + /* TOSHIBA Pocket PC e750 Series */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x0709, 0)}, + /* TOSHIBA Pocket PC e400 Series */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x070a, 0)}, + /* TOSHIBA Pocket PC e800 Series */ + {USB_VPI(USB_VENDOR_TOSHIBA, 0x070b, 0)}, + /* TOSHIBA Pocket PC e740 */ + {USB_VPI(USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740, 0)}, + /* ViewSonic Color Pocket PC V35 */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x0ed9, 0)}, + /* ViewSonic Color Pocket PC V36 */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1527, 0)}, + /* ViewSonic Color Pocket PC V37 */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1529, 0)}, + /* ViewSonic Color Pocket PC V38 */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152b, 0)}, + /* ViewSonic Pocket PC */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152e, 0)}, + /* ViewSonic Communicator Pocket PC */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1921, 0)}, + /* ViewSonic Smartphone */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1922, 0)}, + /* ViewSonic Pocket PC V30 */ + {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1923, 0)}, +}; + +static device_method_t uipaq_methods[] = { + DEVMETHOD(device_probe, uipaq_probe), + DEVMETHOD(device_attach, uipaq_attach), + DEVMETHOD(device_detach, uipaq_detach), + {0, 0} +}; + +static devclass_t uipaq_devclass; + +static driver_t uipaq_driver = { + .name = "uipaq", + .methods = uipaq_methods, + .size = sizeof(struct uipaq_softc), +}; + +DRIVER_MODULE(uipaq, ushub, uipaq_driver, uipaq_devclass, NULL, 0); +MODULE_DEPEND(uipaq, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uipaq, usb2_core, 1, 1, 1); +MODULE_DEPEND(uipaq, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +uipaq_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UIPAQ_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UIPAQ_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uipaq_devs, sizeof(uipaq_devs), uaa)); +} + +static int +uipaq_attach(device_t dev) +{ + struct usb2_device_request req; + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uipaq_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + uint8_t i; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + + device_set_usb2_desc(dev); + + /* + * Send magic bytes, cribbed from Linux ipaq driver that + * claims to have sniffed them from Win98. Wait for driver to + * become ready on device side? + */ + 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 != 64; i++) { + error = + usb2_do_request_flags(uaa->device, NULL, &req, + NULL, 0, NULL, 100); + if (error == 0) + break; + usb2_pause_mtx(NULL, 100); + } + + iface_index = UIPAQ_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer_data, uipaq_config_data, + UIPAQ_N_DATA_TRANSFER, sc, &Giant); + + if (error) { + goto detach; + } + /* clear stall at first run */ + sc->sc_flag |= (UIPAQ_FLAG_READ_STALL | + UIPAQ_FLAG_WRITE_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uipaq_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + uipaq_detach(dev); + return (ENXIO); +} + +int +uipaq_detach(device_t dev) +{ + struct uipaq_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer_data, UIPAQ_N_DATA_TRANSFER); + + return (0); +} + +static void +uipaq_start_read(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer_data[1]); + return; +} + +static void +uipaq_stop_read(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer_data[3]); + usb2_transfer_stop(sc->sc_xfer_data[1]); + return; +} + +static void +uipaq_start_write(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer_data[0]); + return; +} + +static void +uipaq_stop_write(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer_data[2]); + usb2_transfer_stop(sc->sc_xfer_data[0]); + return; +} + +static void +uipaq_cfg_do_request(struct uipaq_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto error; + } + err = usb2_do_request(sc->sc_udev, &Giant, req, data); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +uipaq_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uipaq_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff=%d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_DTR; + else + sc->sc_line &= ~UCDC_LINE_DTR; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = UIPAQ_IFACE_INDEX; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uipaq_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uipaq_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uipaq_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff=%d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_RTS; + else + sc->sc_line &= ~UCDC_LINE_RTS; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = UIPAQ_IFACE_INDEX; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uipaq_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uipaq_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uipaq_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t temp; + + temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SEND_BREAK; + USETW(req.wValue, temp); + req.wIndex[0] = UIPAQ_IFACE_INDEX; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uipaq_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uipaq_write_callback(struct usb2_xfer *xfer) +{ + struct uipaq_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UIPAQ_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer_data[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UIPAQ_BUF_SIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UIPAQ_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer_data[2]); + } + return; + + } +} + +static void +uipaq_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uipaq_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UIPAQ_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uipaq_read_callback(struct usb2_xfer *xfer) +{ + struct uipaq_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, + xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UIPAQ_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer_data[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UIPAQ_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer_data[3]); + } + return; + } +} + +static void +uipaq_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uipaq_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UIPAQ_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} diff --git a/sys/dev/usb2/serial/ulpt2.c b/sys/dev/usb2/serial/ulpt2.c new file mode 100644 index 000000000000..98910e38bfcc --- /dev/null +++ b/sys/dev/usb2/serial/ulpt2.c @@ -0,0 +1,798 @@ +#include +__FBSDID("$FreeBSD$"); + +/* $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. + */ + +/* + * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF + * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR ulpt_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int ulpt_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt"); +SYSCTL_INT(_hw_usb2_ulpt, OID_AUTO, debug, CTLFLAG_RW, + &ulpt_debug, 0, "Debug level"); +#endif + +#define ULPT_BSIZE (1<<15) /* bytes */ +#define ULPT_IFQ_MAXLEN 2 /* units */ +#define ULPT_N_TRANSFER 5 /* units */ + +#define UR_GET_DEVICE_ID 0x00 +#define UR_GET_PORT_STATUS 0x01 +#define UR_SOFT_RESET 0x02 + +#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 { + struct usb2_fifo_sc sc_fifo; + struct usb2_fifo_sc sc_fifo_noreset; + struct mtx sc_mtx; + struct usb2_callout sc_watchdog; + + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_fifo *sc_fifo_open[2]; + struct usb2_xfer *sc_xfer[ULPT_N_TRANSFER]; + + int sc_fflags; /* current open flags, FREAD and + * FWRITE */ + + uint8_t sc_flags; +#define ULPT_FLAG_READ_STALL 0x04 /* read transfer stalled */ +#define ULPT_FLAG_WRITE_STALL 0x08 /* write transfer stalled */ + + uint8_t sc_iface_no; + uint8_t sc_last_status; + uint8_t sc_zlps; /* number of consequtive zero length + * packets received */ +}; + +/* prototypes */ + +static device_probe_t ulpt_probe; +static device_attach_t ulpt_attach; +static device_detach_t ulpt_detach; + +static usb2_callback_t ulpt_write_callback; +static usb2_callback_t ulpt_write_clear_stall_callback; +static usb2_callback_t ulpt_read_callback; +static usb2_callback_t ulpt_read_clear_stall_callback; +static usb2_callback_t ulpt_status_callback; + +static void ulpt_reset(struct ulpt_softc *sc); +static void ulpt_watchdog(void *arg); + +static usb2_fifo_close_t ulpt_close; +static usb2_fifo_cmd_t ulpt_start_read; +static usb2_fifo_cmd_t ulpt_start_write; +static usb2_fifo_cmd_t ulpt_stop_read; +static usb2_fifo_cmd_t ulpt_stop_write; +static usb2_fifo_ioctl_t ulpt_ioctl; +static usb2_fifo_open_t ulpt_open; +static usb2_fifo_open_t unlpt_open; + +static struct usb2_fifo_methods ulpt_fifo_methods = { + .f_close = &ulpt_close, + .f_ioctl = &ulpt_ioctl, + .f_open = &ulpt_open, + .f_start_read = &ulpt_start_read, + .f_start_write = &ulpt_start_write, + .f_stop_read = &ulpt_stop_read, + .f_stop_write = &ulpt_stop_write, + .basename[0] = "ulpt", +}; + +static struct usb2_fifo_methods unlpt_fifo_methods = { + .f_close = &ulpt_close, + .f_ioctl = &ulpt_ioctl, + .f_open = &unlpt_open, + .f_start_read = &ulpt_start_read, + .f_start_write = &ulpt_start_write, + .f_stop_read = &ulpt_stop_read, + .f_stop_write = &ulpt_stop_write, + .basename[0] = "unlpt", +}; + +static void +ulpt_reset(struct ulpt_softc *sc) +{ + struct usb2_device_request req; + + DPRINTFN(2, "\n"); + + req.bRequest = UR_SOFT_RESET; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_iface_no); + 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. + */ + + mtx_lock(&sc->sc_mtx); + req.bmRequestType = UT_WRITE_CLASS_OTHER; + if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, + &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.0 */ + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, + &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.1 */ + /* ignore error */ + } + } + mtx_unlock(&sc->sc_mtx); + return; +} + +static void +ulpt_write_callback(struct usb2_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_TX]; + uint32_t actlen; + + if (f == NULL) { + /* should not happen */ + DPRINTF("no FIFO\n"); + return; + } + DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: + if (sc->sc_flags & ULPT_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + break; + } + if (usb2_fifo_get_data(f, xfer->frbuffers, + 0, xfer->max_data_length, &actlen, 0)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ULPT_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + break; + } + return; +} + +static void +ulpt_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ULPT_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ulpt_read_callback(struct usb2_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_RX]; + + if (f == NULL) { + /* should not happen */ + DPRINTF("no FIFO\n"); + return; + } + DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen == 0) { + + if (sc->sc_zlps == 4) { + /* enable BULK throttle */ + xfer->interval = 500; /* ms */ + } else { + sc->sc_zlps++; + } + } else { + /* disable BULK throttle */ + + xfer->interval = 0; + sc->sc_zlps = 0; + } + + usb2_fifo_put_data(f, xfer->frbuffers, + 0, xfer->actlen, 1); + + case USB_ST_SETUP: + if (sc->sc_flags & ULPT_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[4]); + break; + } + if (usb2_fifo_put_bytes_max(f) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + /* disable BULK throttle */ + xfer->interval = 0; + sc->sc_zlps = 0; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ULPT_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[4]); + } + break; + } + return; +} + +static void +ulpt_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ULPT_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ulpt_status_callback(struct usb2_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + uint8_t cur_status; + uint8_t new_status; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usb2_copy_out(xfer->frbuffers + 1, 0, &cur_status, 1); + + cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; + new_status = cur_status & ~sc->sc_last_status; + sc->sc_last_status = cur_status; + + if (new_status & LPS_SELECT) + log(LOG_NOTICE, "%s: offline\n", + device_get_nameunit(sc->sc_dev)); + else if (new_status & LPS_NOPAPER) + log(LOG_NOTICE, "%s: out of paper\n", + device_get_nameunit(sc->sc_dev)); + else if (new_status & LPS_NERR) + log(LOG_NOTICE, "%s: output error\n", + device_get_nameunit(sc->sc_dev)); + break; + + case USB_ST_SETUP: + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_PORT_STATUS; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = 1; + xfer->nframes = 2; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + if (xfer->error != USB_ERR_CANCELLED) { + /* wait for next watchdog timeout */ + } + break; + } + return; +} + +static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = ULPT_BSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1}, + .mh.callback = &ulpt_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = ULPT_BSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1}, + .mh.callback = &ulpt_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request) + 1, + .mh.callback = &ulpt_status_callback, + .mh.timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ulpt_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ulpt_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static void +ulpt_start_read(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +ulpt_stop_read(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[4]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +ulpt_start_write(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ulpt_stop_write(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static int +ulpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + /* we assume that open is a serial process */ + + if (sc->sc_fflags == 0) { + ulpt_reset(sc); + } + return (unlpt_open(fifo, fflags, td)); +} + +static int +unlpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + if (sc->sc_fflags & fflags) { + return (EBUSY); + } + if (fflags & FREAD) { + /* clear stall first */ + mtx_lock(&sc->sc_mtx); + sc->sc_flags |= ULPT_FLAG_READ_STALL; + mtx_unlock(&sc->sc_mtx); + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[1]->max_data_length, + ULPT_IFQ_MAXLEN)) { + return (ENOMEM); + } + /* set which FIFO is opened */ + sc->sc_fifo_open[USB_FIFO_RX] = fifo; + } + if (fflags & FWRITE) { + /* clear stall first */ + mtx_lock(&sc->sc_mtx); + sc->sc_flags |= ULPT_FLAG_WRITE_STALL; + mtx_unlock(&sc->sc_mtx); + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[0]->max_data_length, + ULPT_IFQ_MAXLEN)) { + return (ENOMEM); + } + /* set which FIFO is opened */ + sc->sc_fifo_open[USB_FIFO_TX] = fifo; + } + sc->sc_fflags |= fflags & (FREAD | FWRITE); + return (0); +} + +static void +ulpt_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); + + if (fflags & (FREAD | FWRITE)) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +static int +ulpt_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, + int fflags, struct thread *td) +{ + return (ENODEV); +} + +static int +ulpt_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if ((uaa->info.bInterfaceClass == UICLASS_PRINTER) && + (uaa->info.bInterfaceSubClass == UISUBCLASS_PRINTER) && + ((uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_UNI) || + (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_BI) || + (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_1284))) { + return (0); + } + return (ENXIO); +} + +static int +ulpt_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ulpt_softc *sc = device_get_softc(dev); + struct usb2_interface_descriptor *id; + int unit = device_get_unit(dev); + int error; + uint8_t iface_index = uaa->info.bIfaceIndex; + uint8_t alt_index; + + DPRINTFN(11, "sc=%p\n", sc); + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + /* search through all the descriptors looking for bidir mode */ + + id = usb2_get_interface_descriptor(uaa->iface); + alt_index = 0 - 1; + while (1) { + if (id == NULL) { + break; + } + if ((id->bDescriptorType == UDESC_INTERFACE) && + (id->bLength >= sizeof(*id))) { + if (id->bInterfaceNumber != uaa->info.bIfaceNum) { + break; + } else { + alt_index++; + if ((id->bInterfaceClass == UICLASS_PRINTER) && + (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && + (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { + goto found; + } + } + } + id = (void *)usb2_desc_foreach( + usb2_get_config_descriptor(uaa->device), (void *)id); + } + goto detach; + +found: + + DPRINTF("setting alternate " + "config number: %d\n", alt_index); + + if (alt_index) { + + error = usb2_set_alt_interface_index + (uaa->device, iface_index, alt_index); + + if (error) { + DPRINTF("could not set alternate " + "config, error=%s\n", usb2_errstr(error)); + goto detach; + } + } + sc->sc_iface_no = id->bInterfaceNumber; + + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER, + sc, &sc->sc_mtx); + if (error) { + DPRINTF("error=%s\n", usb2_errstr(error)); + goto detach; + } + device_printf(sc->sc_dev, "using bi-directional mode\n"); + +#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* + */ + { + struct usb2_config_descriptor *cd = usb2_get_config_descriptor(dev); + struct usb2_device_request 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); + error = usb2_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK, + &alen, USB_DEFAULT_TIMEOUT); + if (error) { + device_printf(sc->sc_dev, "cannot get device id\n"); + } else if (alen <= 2) { + device_printf(sc->sc_dev, "empty device id, no " + "printer connected?\n"); + } 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 + + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &ulpt_fifo_methods, &sc->sc_fifo, + unit, 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &unlpt_fifo_methods, &sc->sc_fifo_noreset, + unit, 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + /* start reading of status */ + + mtx_lock(&sc->sc_mtx); + + ulpt_watchdog(sc); /* will unlock mutex */ + + return (0); + +detach: + ulpt_detach(dev); + return (ENOMEM); +} + +static int +ulpt_detach(device_t dev) +{ + struct ulpt_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + usb2_fifo_detach(&sc->sc_fifo); + usb2_fifo_detach(&sc->sc_fifo_noreset); + + mtx_lock(&sc->sc_mtx); + usb2_callout_stop(&sc->sc_watchdog); + mtx_unlock(&sc->sc_mtx); + + usb2_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +#if 0 +/* XXX This does not belong here. */ + +/* + * Compare two strings until the second ends. + */ + +static uint8_t +ieee1284_compare(const char *a, const char *b) +{ + while (1) { + + if (*b == 0) { + break; + } + if (*a != *b) { + return 1; + } + b++; + a++; + } + return 0; +} + +/* + * 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 (ieee1284_compare(p, "MFG:") == 0 || + ieee1284_compare(p, "MANUFACTURER:") == 0 || + ieee1284_compare(p, "MDL:") == 0 || + ieee1284_compare(p, "MODEL:") == 0) { + q = strchr(p, ';'); + if (q) + printf("%.*s", (int)(q - p + 1), p); + } + } +} + +#endif + +static void +ulpt_watchdog(void *arg) +{ + struct ulpt_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usb2_transfer_start(sc->sc_xfer[2]); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &ulpt_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + return; +} + +static devclass_t ulpt_devclass; + +static device_method_t ulpt_methods[] = { + DEVMETHOD(device_probe, ulpt_probe), + DEVMETHOD(device_attach, ulpt_attach), + DEVMETHOD(device_detach, ulpt_detach), + {0, 0} +}; + +static driver_t ulpt_driver = { + .name = "ulpt", + .methods = ulpt_methods, + .size = sizeof(struct ulpt_softc), +}; + +DRIVER_MODULE(ulpt, ushub, ulpt_driver, ulpt_devclass, NULL, 0); +MODULE_DEPEND(ulpt, usb2_core, 1, 1, 1); +MODULE_DEPEND(ulpt, usb2_serial, 1, 1, 1); diff --git a/sys/dev/usb2/serial/umct2.c b/sys/dev/usb2/serial/umct2.c new file mode 100644 index 000000000000..3d9c3164eb82 --- /dev/null +++ b/sys/dev/usb2/serial/umct2.c @@ -0,0 +1,689 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * 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. + * + */ + +/* + * Driver for the MCT (Magic Control Technology) USB-RS232 Converter. + * Based on the superb documentation from the linux mct_u232 driver by + * Wolfgang Grandeggar . + * 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 . Merging the two together + * might be useful, though the subtle differences might lead to lots of + * #ifdef's. + */ + +/* + * NOTE: all function names beginning like "umct_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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 + +#define UMCT_ENDPT_MAX 6 /* units */ + +struct umct_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[UMCT_ENDPT_MAX]; + + uint32_t sc_unit; + + uint16_t sc_obufsize; + + uint8_t sc_lsr; + uint8_t sc_msr; + uint8_t sc_lcr; + uint8_t sc_mcr; + + uint8_t sc_name[16]; + uint8_t sc_flags; +#define UMCT_FLAG_READ_STALL 0x01 +#define UMCT_FLAG_WRITE_STALL 0x02 +#define UMCT_FLAG_INTR_STALL 0x04 + uint8_t sc_iface_no; +}; + +/* prototypes */ + +static device_probe_t umct_probe; +static device_attach_t umct_attach; +static device_detach_t umct_detach; + +static usb2_callback_t umct_intr_clear_stall_callback; +static usb2_callback_t umct_intr_callback; +static usb2_callback_t umct_write_callback; +static usb2_callback_t umct_write_clear_stall_callback; +static usb2_callback_t umct_read_callback; +static usb2_callback_t umct_read_clear_stall_callback; + +static void umct_cfg_do_request(struct umct_softc *sc, uint8_t request, uint16_t len, uint32_t value); +static void umct_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void umct_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void umct_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void umct_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static uint8_t umct_calc_baud(uint32_t baud); +static int umct_pre_param(struct usb2_com_softc *ucom, struct termios *ti); +static void umct_cfg_param(struct usb2_com_softc *ucom, struct termios *ti); +static void umct_start_read(struct usb2_com_softc *ucom); +static void umct_stop_read(struct usb2_com_softc *ucom); +static void umct_start_write(struct usb2_com_softc *ucom); +static void umct_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config umct_config[UMCT_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &umct_write_callback, + }, + + [1] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umct_read_callback, + .ep_index = 0, /* first interrupt endpoint */ + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umct_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umct_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umct_intr_callback, + .ep_index = 1, /* second interrupt endpoint */ + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umct_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback umct_callback = { + .usb2_com_cfg_get_status = &umct_cfg_get_status, + .usb2_com_cfg_set_dtr = &umct_cfg_set_dtr, + .usb2_com_cfg_set_rts = &umct_cfg_set_rts, + .usb2_com_cfg_set_break = &umct_cfg_set_break, + .usb2_com_cfg_param = &umct_cfg_param, + .usb2_com_pre_param = &umct_pre_param, + .usb2_com_start_read = &umct_start_read, + .usb2_com_stop_read = &umct_stop_read, + .usb2_com_start_write = &umct_start_write, + .usb2_com_stop_write = &umct_stop_write, +}; + +static const struct usb2_device_id umct_devs[] = { + {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0)}, + {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0)}, + {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, 0)}, +}; + +static device_method_t umct_methods[] = { + DEVMETHOD(device_probe, umct_probe), + DEVMETHOD(device_attach, umct_attach), + DEVMETHOD(device_detach, umct_detach), + {0, 0} +}; + +static devclass_t umct_devclass; + +static driver_t umct_driver = { + .name = "umct", + .methods = umct_methods, + .size = sizeof(struct umct_softc), +}; + +DRIVER_MODULE(umct, ushub, umct_driver, umct_devclass, NULL, 0); +MODULE_DEPEND(umct, usb2_serial, 1, 1, 1); +MODULE_DEPEND(umct, usb2_core, 1, 1, 1); +MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +umct_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UMCT_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UMCT_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa)); +} + +static int +umct_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umct_softc *sc = device_get_softc(dev); + int32_t error; + uint16_t maxp; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_unit = device_get_unit(dev); + + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + sc->sc_iface_no = uaa->info.bIfaceNum; + + iface_index = UMCT_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, umct_config, UMCT_ENDPT_MAX, sc, &Giant); + + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + /* + * 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. + */ + maxp = UGETW(sc->sc_xfer[1]->pipe->edesc->wMaxPacketSize); + if (maxp == 0x2) { + + /* guessed wrong - switch around endpoints */ + + struct usb2_xfer *temp = sc->sc_xfer[4]; + + sc->sc_xfer[4] = sc->sc_xfer[1]; + sc->sc_xfer[1] = temp; + + sc->sc_xfer[1]->callback = &umct_read_callback; + sc->sc_xfer[4]->callback = &umct_intr_callback; + } + sc->sc_obufsize = sc->sc_xfer[0]->max_data_length; + + if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) { + if (sc->sc_obufsize > 16) { + sc->sc_obufsize = 16; + } + } + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &umct_callback, &Giant); + if (error) { + goto detach; + } + return (0); /* success */ + +detach: + umct_detach(dev); + return (ENXIO); /* failure */ +} + +static int +umct_detach(device_t dev) +{ + struct umct_softc *sc = device_get_softc(dev); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UMCT_ENDPT_MAX); + + return (0); +} + +static void +umct_cfg_do_request(struct umct_softc *sc, uint8_t request, + uint16_t len, uint32_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t temp[4]; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto done; + } + if (len > 4) { + len = 4; + } + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = request; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, len); + USETDW(temp, value); + + err = usb2_do_request_flags(sc->sc_udev, &Giant, &req, + temp, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +done: + return; +} + +static void +umct_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMCT_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umct_intr_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen < 2) { + DPRINTF("too short message\n"); + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf)); + + sc->sc_msr = buf[0]; + sc->sc_lsr = buf[1]; + + usb2_com_status_change(&sc->sc_ucom); + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UMCT_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* start clear stall */ + sc->sc_flags |= UMCT_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + + } +} + +static void +umct_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct umct_softc *sc = ucom->sc_parent; + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static void +umct_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umct_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_lcr |= 0x40; + else + sc->sc_lcr &= ~0x40; + + umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr); + return; +} + +static void +umct_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umct_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= 0x01; + else + sc->sc_mcr &= ~0x01; + + umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); + return; +} + +static void +umct_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umct_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= 0x02; + else + sc->sc_mcr &= ~0x02; + + umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); + return; +} + +static uint8_t +umct_calc_baud(uint32_t 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_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + return (0); /* we accept anything */ +} + +static void +umct_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct umct_softc *sc = ucom->sc_parent; + uint32_t value; + + value = umct_calc_baud(t->c_ospeed); + umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value); + + value = (sc->sc_lcr & 0x40); + + switch (t->c_cflag & CSIZE) { + case CS5: + value |= 0x0; + break; + case CS6: + value |= 0x1; + break; + case CS7: + value |= 0x2; + break; + default: + case CS8: + value |= 0x3; + break; + } + + value |= (t->c_cflag & CSTOPB) ? 0x4 : 0; + if (t->c_cflag & PARENB) { + value |= 0x8; + value |= (t->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_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value); + return; +} + +static void +umct_start_read(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer[4]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +umct_stop_read(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + /* stop interrupt endpoint */ + usb2_transfer_stop(sc->sc_xfer[5]); + usb2_transfer_stop(sc->sc_xfer[4]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +umct_start_write(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +umct_stop_write(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +umct_write_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flags & UMCT_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + sc->sc_obufsize, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UMCT_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +umct_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMCT_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umct_read_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, + 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flags & UMCT_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flags |= UMCT_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +umct_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umct_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMCT_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} diff --git a/sys/dev/usb2/serial/umodem2.c b/sys/dev/usb2/serial/umodem2.c new file mode 100644 index 000000000000..01d719d7f3ba --- /dev/null +++ b/sys/dev/usb2/serial/umodem2.c @@ -0,0 +1,924 @@ +/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2003, 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. + */ + +/*- + * 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 +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR umodem_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int umodem_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem"); +SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW, + &umodem_debug, 0, "Debug level"); +#endif + +static const struct usb2_device_id umodem_devs[] = { + /* Generic Modem class match */ + {USB_IFACE_CLASS(UICLASS_CDC), + USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), + USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)}, + /* Kyocera AH-K3001V */ + {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)}, + {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)}, +}; + +/* + * As speeds for umodem deivces increase, these numbers will need to + * be increased. They should be good for G3 speeds and below. + * + * TODO: The TTY buffers should be increased! + */ +#define UMODEM_BUF_SIZE 1024 +#define UMODEM_N_DATA_TRANSFER 4 +#define UMODEM_N_INTR_TRANSFER 2 + +#define UMODEM_MODVER 1 /* module version */ + +struct umodem_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer_data[UMODEM_N_DATA_TRANSFER]; + struct usb2_xfer *sc_xfer_intr[UMODEM_N_INTR_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_line; + + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* modem status register */ + uint8_t sc_ctrl_iface_no; + uint8_t sc_ctrl_iface_index; + uint8_t sc_data_iface_no; + uint8_t sc_data_iface_index; + uint8_t sc_cm_over_data; + uint8_t sc_cm_cap; /* CM capabilities */ + uint8_t sc_acm_cap; /* ACM capabilities */ + uint8_t sc_flag; +#define UMODEM_FLAG_READ_STALL 0x01 +#define UMODEM_FLAG_WRITE_STALL 0x02 +#define UMODEM_FLAG_INTR_STALL 0x04 +}; + +static device_probe_t umodem_probe; +static device_attach_t umodem_attach; +static device_detach_t umodem_detach; + +static usb2_callback_t umodem_intr_callback; +static usb2_callback_t umodem_intr_clear_stall_callback; +static usb2_callback_t umodem_write_callback; +static usb2_callback_t umodem_read_callback; +static usb2_callback_t umodem_write_clear_stall_callback; +static usb2_callback_t umodem_read_clear_stall_callback; + +static void umodem_start_read(struct usb2_com_softc *ucom); +static void umodem_stop_read(struct usb2_com_softc *ucom); +static void umodem_start_write(struct usb2_com_softc *ucom); +static void umodem_stop_write(struct usb2_com_softc *ucom); +static void umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm); +static void umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static int umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static int umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td); +static void umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void *umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype); +static usb2_error_t umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no, uint16_t feature, uint16_t state); +static void umodem_cfg_do_request(struct umodem_softc *sc, struct usb2_device_request *req, void *data); + +static const struct usb2_config umodem_config_data[UMODEM_N_DATA_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMODEM_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &umodem_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMODEM_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umodem_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umodem_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umodem_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_config umodem_config_intr[UMODEM_N_INTR_TRANSFER] = { + [0] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umodem_intr_callback, + }, + + [1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umodem_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback umodem_callback = { + .usb2_com_cfg_get_status = &umodem_cfg_get_status, + .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr, + .usb2_com_cfg_set_rts = &umodem_cfg_set_rts, + .usb2_com_cfg_set_break = &umodem_cfg_set_break, + .usb2_com_cfg_param = &umodem_cfg_param, + .usb2_com_pre_param = &umodem_pre_param, + .usb2_com_ioctl = &umodem_ioctl, + .usb2_com_start_read = &umodem_start_read, + .usb2_com_stop_read = &umodem_stop_read, + .usb2_com_start_write = &umodem_start_write, + .usb2_com_stop_write = &umodem_stop_write, +}; + +static device_method_t umodem_methods[] = { + DEVMETHOD(device_probe, umodem_probe), + DEVMETHOD(device_attach, umodem_attach), + DEVMETHOD(device_detach, umodem_detach), + {0, 0} +}; + +static devclass_t umodem_devclass; + +static driver_t umodem_driver = { + .name = "umodem", + .methods = umodem_methods, + .size = sizeof(struct umodem_softc), +}; + +DRIVER_MODULE(umodem, ushub, umodem_driver, umodem_devclass, NULL, 0); +MODULE_DEPEND(umodem, usb2_serial, 1, 1, 1); +MODULE_DEPEND(umodem, usb2_core, 1, 1, 1); +MODULE_DEPEND(umodem, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); +MODULE_VERSION(umodem, UMODEM_MODVER); + +static int +umodem_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + uint8_t cm; + uint8_t acm; + int error; + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa); + if (error) { + return (error); + } + if (uaa->driver_info == NULL) { + /* some modems do not have any capabilities */ + return (error); + } + umodem_get_caps(uaa, &cm, &acm); + if (!(cm & USB_CDC_CM_DOES_CM) || + !(cm & USB_CDC_CM_OVER_DATA) || + !(acm & USB_CDC_ACM_HAS_LINE)) { + error = ENXIO; + } + return (error); +} + +static int +umodem_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umodem_softc *sc = device_get_softc(dev); + struct usb2_cdc_cm_descriptor *cmd; + uint8_t i; + int error; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; + sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex; + sc->sc_udev = uaa->device; + + umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap); + + /* get the data interface number */ + + cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); + + if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { + device_printf(dev, "no CM descriptor!\n"); + goto detach; + } + sc->sc_data_iface_no = cmd->bDataInterface; + + device_printf(dev, "data interface %d, has %sCM over " + "data, has %sbreak\n", + 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 "); + + /* get the data interface too */ + + for (i = 0;; i++) { + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + + iface = usb2_get_iface(uaa->device, i); + + if (iface) { + + id = usb2_get_interface_descriptor(iface); + + if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { + sc->sc_data_iface_index = i; + usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + break; + } + } else { + device_printf(dev, "no data interface!\n"); + goto detach; + } + } + + if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { + if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { + + error = umodem_set_comm_feature + (uaa->device, sc->sc_ctrl_iface_no, + UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); + + /* ignore any errors */ + } + sc->sc_cm_over_data = 1; + } + error = usb2_transfer_setup(uaa->device, + &sc->sc_data_iface_index, sc->sc_xfer_data, + umodem_config_data, UMODEM_N_DATA_TRANSFER, + sc, &Giant); + if (error) { + goto detach; + } + error = usb2_transfer_setup(uaa->device, + &sc->sc_ctrl_iface_index, sc->sc_xfer_intr, + umodem_config_intr, UMODEM_N_INTR_TRANSFER, + sc, &Giant); + + if (error == 0) { + device_printf(dev, "status change " + "notification available\n"); + } + /* clear stall at first run */ + sc->sc_flag |= (UMODEM_FLAG_READ_STALL | + UMODEM_FLAG_WRITE_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &umodem_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + umodem_detach(dev); + return (ENXIO); +} + +static void +umodem_start_read(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + if (sc->sc_xfer_intr[0]) { + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer_intr[0]); + } + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer_data[1]); + return; +} + +static void +umodem_stop_read(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + if (sc->sc_xfer_intr[0]) { + /* stop interrupt endpoint */ + usb2_transfer_stop(sc->sc_xfer_intr[0]); + } + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer_data[3]); + usb2_transfer_stop(sc->sc_xfer_data[1]); + return; +} + +static void +umodem_start_write(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer_data[0]); + return; +} + +static void +umodem_stop_write(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer_data[2]); + usb2_transfer_stop(sc->sc_xfer_data[0]); + return; +} + +static void +umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm) +{ + struct usb2_cdc_cm_descriptor *cmd; + struct usb2_cdc_acm_descriptor *cad; + + *cm = *acm = 0; + + cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); + if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { + DPRINTF("no CM desc\n"); + return; + } + *cm = cmd->bmCapabilities; + + cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); + if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { + DPRINTF("no ACM desc\n"); + return; + } + *acm = cad->bmCapabilities; + + return; +} + +static void +umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct umodem_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static int +umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + return (0); /* we accept anything */ +} + +static void +umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct umodem_softc *sc = ucom->sc_parent; + struct usb2_cdc_line_state ls; + struct usb2_device_request req; + + DPRINTF("sc=%p\n", sc); + + bzero(&ls, sizeof(ls)); + + USETDW(ls.dwDTERate, t->c_ospeed); + + ls.bCharFormat = (t->c_cflag & CSTOPB) ? + UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; + + ls.bParityType = (t->c_cflag & PARENB) ? + ((t->c_cflag & PARODD) ? + UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; + + switch (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; + } + + DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", + UGETDW(ls.dwDTERate), ls.bCharFormat, + ls.bParityType, ls.bDataBits); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_LINE_CODING; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, sizeof(ls)); + + umodem_cfg_do_request(sc, &req, &ls); + return; +} + +static int +umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, + int flag, struct thread *td) +{ + struct umodem_softc *sc = ucom->sc_parent; + int error = 0; + + DPRINTF("cmd=0x%08x\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("unknown\n"); + error = ENOIOCTL; + break; + } + + return (error); +} + +static void +umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umodem_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff=%d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_DTR; + else + sc->sc_line &= ~UCDC_LINE_DTR; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + umodem_cfg_do_request(sc, &req, NULL); + return; +} + +static void +umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umodem_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff=%d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_RTS; + else + sc->sc_line &= ~UCDC_LINE_RTS; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + umodem_cfg_do_request(sc, &req, NULL); + return; +} + +static void +umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umodem_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t temp; + + DPRINTF("onoff=%d\n", onoff); + + if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { + + temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SEND_BREAK; + USETW(req.wValue, temp); + req.wIndex[0] = sc->sc_ctrl_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + umodem_cfg_do_request(sc, &req, NULL); + } + return; +} + +static void +umodem_intr_callback(struct usb2_xfer *xfer) +{ + struct usb2_cdc_notification pkt; + struct umodem_softc *sc = xfer->priv_sc; + uint16_t wLen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < 8) { + DPRINTF("received short packet, " + "%d bytes\n", xfer->actlen); + goto tr_setup; + } + if (xfer->actlen > sizeof(pkt)) { + DPRINTF("truncating message\n"); + xfer->actlen = sizeof(pkt); + } + usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen); + + xfer->actlen -= 8; + + wLen = UGETW(pkt.wLength); + if (xfer->actlen > wLen) { + xfer->actlen = wLen; + } + if (pkt.bmRequestType != UCDC_NOTIFICATION) { + DPRINTF("unknown message type, " + "0x%02x, on notify pipe!\n", + pkt.bmRequestType); + goto tr_setup; + } + switch (pkt.bNotification) { + case UCDC_N_SERIAL_STATE: + /* + * Set the serial state in ucom driver based on + * the bits from the notify message + */ + if (xfer->actlen < 2) { + DPRINTF("invalid notification " + "length, %d bytes!\n", xfer->actlen); + break; + } + DPRINTF("notify bytes = %02x%02x\n", + pkt.data[0], + pkt.data[1]); + + /* Currently, lsr is always zero. */ + sc->sc_lsr = 0; + sc->sc_msr = 0; + + if (pkt.data[0] & UCDC_N_SERIAL_RI) { + sc->sc_msr |= SER_RI; + } + if (pkt.data[0] & UCDC_N_SERIAL_DSR) { + sc->sc_msr |= SER_DSR; + } + if (pkt.data[0] & UCDC_N_SERIAL_DCD) { + sc->sc_msr |= SER_DCD; + } + usb2_com_status_change(&sc->sc_ucom); + break; + + default: + DPRINTF("unknown notify message: 0x%02x\n", + pkt.bNotification); + break; + } + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flag & UMODEM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer_intr[1]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UMODEM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer_intr[1]); + } + return; + + } +} + +static void +umodem_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umodem_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_intr[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UMODEM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umodem_write_callback(struct usb2_xfer *xfer) +{ + struct umodem_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UMODEM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer_data[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UMODEM_BUF_SIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UMODEM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer_data[2]); + } + return; + + } +} + +static void +umodem_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umodem_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UMODEM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umodem_read_callback(struct usb2_xfer *xfer) +{ + struct umodem_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("actlen=%d\n", xfer->actlen); + + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, + xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UMODEM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer_data[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UMODEM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer_data[3]); + } + return; + + } +} + +static void +umodem_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umodem_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UMODEM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void * +umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype) +{ + return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, + type, 0 - 1, subtype, 0 - 1)); +} + +static usb2_error_t +umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no, + uint16_t feature, uint16_t state) +{ + struct usb2_device_request req; + struct usb2_cdc_abstract_state ast; + + DPRINTF("feature=%d state=%d\n", + feature, state); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_COMM_FEATURE; + USETW(req.wValue, feature); + req.wIndex[0] = iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); + USETW(ast.wState, state); + + return (usb2_do_request(udev, &Giant, &req, &ast)); +} + +static int +umodem_detach(device_t dev) +{ + struct umodem_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer_intr, UMODEM_N_INTR_TRANSFER); + + usb2_transfer_unsetup(sc->sc_xfer_data, UMODEM_N_DATA_TRANSFER); + + return (0); +} + +static void +umodem_cfg_do_request(struct umodem_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto error; + } + err = usb2_do_request_flags(sc->sc_udev, &Giant, req, + data, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} diff --git a/sys/dev/usb2/serial/umoscom2.c b/sys/dev/usb2/serial/umoscom2.c new file mode 100644 index 000000000000..3c142a5b1688 --- /dev/null +++ b/sys/dev/usb2/serial/umoscom2.c @@ -0,0 +1,799 @@ +/* $FreeBSD$ */ +/* $OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $ */ + +/* + * Copyright (c) 2006 Jonathan Gray + * + * 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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR umoscom_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int umoscom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom"); +SYSCTL_INT(_hw_usb2_umoscom, OID_AUTO, debug, CTLFLAG_RW, + &umoscom_debug, 0, "Debug level"); +#endif + +#define UMOSCOM_BUFSIZE 1024 /* bytes */ +#define UMOSCOM_N_DATA_TRANSFER 6 /* units */ + +#define UMOSCOM_CONFIG_INDEX 0 +#define UMOSCOM_IFACE_INDEX 0 + +/* interrupt packet */ +#define UMOSCOM_IIR_RLS 0x06 +#define UMOSCOM_IIR_RDA 0x04 +#define UMOSCOM_IIR_CTI 0x0c +#define UMOSCOM_IIR_THR 0x02 +#define UMOSCOM_IIR_MS 0x00 + +/* registers */ +#define UMOSCOM_READ 0x0d +#define UMOSCOM_WRITE 0x0e +#define UMOSCOM_UART_REG 0x0300 +#define UMOSCOM_VEND_REG 0x0000 + +#define UMOSCOM_TXBUF 0x00 /* Write */ +#define UMOSCOM_RXBUF 0x00 /* Read */ +#define UMOSCOM_INT 0x01 +#define UMOSCOM_FIFO 0x02 /* Write */ +#define UMOSCOM_ISR 0x02 /* Read */ +#define UMOSCOM_LCR 0x03 +#define UMOSCOM_MCR 0x04 +#define UMOSCOM_LSR 0x05 +#define UMOSCOM_MSR 0x06 +#define UMOSCOM_SCRATCH 0x07 +#define UMOSCOM_DIV_LO 0x08 +#define UMOSCOM_DIV_HI 0x09 +#define UMOSCOM_EFR 0x0a +#define UMOSCOM_XON1 0x0b +#define UMOSCOM_XON2 0x0c +#define UMOSCOM_XOFF1 0x0d +#define UMOSCOM_XOFF2 0x0e + +#define UMOSCOM_BAUDLO 0x00 +#define UMOSCOM_BAUDHI 0x01 + +#define UMOSCOM_INT_RXEN 0x01 +#define UMOSCOM_INT_TXEN 0x02 +#define UMOSCOM_INT_RSEN 0x04 +#define UMOSCOM_INT_MDMEM 0x08 +#define UMOSCOM_INT_SLEEP 0x10 +#define UMOSCOM_INT_XOFF 0x20 +#define UMOSCOM_INT_RTS 0x40 + +#define UMOSCOM_FIFO_EN 0x01 +#define UMOSCOM_FIFO_RXCLR 0x02 +#define UMOSCOM_FIFO_TXCLR 0x04 +#define UMOSCOM_FIFO_DMA_BLK 0x08 +#define UMOSCOM_FIFO_TXLVL_MASK 0x30 +#define UMOSCOM_FIFO_TXLVL_8 0x00 +#define UMOSCOM_FIFO_TXLVL_16 0x10 +#define UMOSCOM_FIFO_TXLVL_32 0x20 +#define UMOSCOM_FIFO_TXLVL_56 0x30 +#define UMOSCOM_FIFO_RXLVL_MASK 0xc0 +#define UMOSCOM_FIFO_RXLVL_8 0x00 +#define UMOSCOM_FIFO_RXLVL_16 0x40 +#define UMOSCOM_FIFO_RXLVL_56 0x80 +#define UMOSCOM_FIFO_RXLVL_80 0xc0 + +#define UMOSCOM_ISR_MDM 0x00 +#define UMOSCOM_ISR_NONE 0x01 +#define UMOSCOM_ISR_TX 0x02 +#define UMOSCOM_ISR_RX 0x04 +#define UMOSCOM_ISR_LINE 0x06 +#define UMOSCOM_ISR_RXTIMEOUT 0x0c +#define UMOSCOM_ISR_RX_XOFF 0x10 +#define UMOSCOM_ISR_RTSCTS 0x20 +#define UMOSCOM_ISR_FIFOEN 0xc0 + +#define UMOSCOM_LCR_DBITS(x) ((x) - 5) +#define UMOSCOM_LCR_STOP_BITS_1 0x00 +#define UMOSCOM_LCR_STOP_BITS_2 0x04 /* 2 if 6-8 bits/char or 1.5 if 5 */ +#define UMOSCOM_LCR_PARITY_NONE 0x00 +#define UMOSCOM_LCR_PARITY_ODD 0x08 +#define UMOSCOM_LCR_PARITY_EVEN 0x18 +#define UMOSCOM_LCR_BREAK 0x40 +#define UMOSCOM_LCR_DIVLATCH_EN 0x80 + +#define UMOSCOM_MCR_DTR 0x01 +#define UMOSCOM_MCR_RTS 0x02 +#define UMOSCOM_MCR_LOOP 0x04 +#define UMOSCOM_MCR_INTEN 0x08 +#define UMOSCOM_MCR_LOOPBACK 0x10 +#define UMOSCOM_MCR_XONANY 0x20 +#define UMOSCOM_MCR_IRDA_EN 0x40 +#define UMOSCOM_MCR_BAUD_DIV4 0x80 + +#define UMOSCOM_LSR_RXDATA 0x01 +#define UMOSCOM_LSR_RXOVER 0x02 +#define UMOSCOM_LSR_RXPAR_ERR 0x04 +#define UMOSCOM_LSR_RXFRM_ERR 0x08 +#define UMOSCOM_LSR_RXBREAK 0x10 +#define UMOSCOM_LSR_TXEMPTY 0x20 +#define UMOSCOM_LSR_TXALLEMPTY 0x40 +#define UMOSCOM_LSR_TXFIFO_ERR 0x80 + +#define UMOSCOM_MSR_CTS_CHG 0x01 +#define UMOSCOM_MSR_DSR_CHG 0x02 +#define UMOSCOM_MSR_RI_CHG 0x04 +#define UMOSCOM_MSR_CD_CHG 0x08 +#define UMOSCOM_MSR_CTS 0x10 +#define UMOSCOM_MSR_RTS 0x20 +#define UMOSCOM_MSR_RI 0x40 +#define UMOSCOM_MSR_CD 0x80 + +#define UMOSCOM_BAUD_REF 115200 + +struct umoscom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer_data[UMOSCOM_N_DATA_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_mcr; + uint8_t sc_lcr; + uint8_t sc_flags; +#define UMOSCOM_FLAG_READ_STALL 0x01 +#define UMOSCOM_FLAG_WRITE_STALL 0x02 +#define UMOSCOM_FLAG_INTR_STALL 0x04 +}; + +/* prototypes */ + +static device_probe_t umoscom_probe; +static device_attach_t umoscom_attach; +static device_detach_t umoscom_detach; + +static usb2_callback_t umoscom_write_callback; +static usb2_callback_t umoscom_write_clear_stall_callback; +static usb2_callback_t umoscom_read_callback; +static usb2_callback_t umoscom_read_clear_stall_callback; +static usb2_callback_t umoscom_intr_callback; +static usb2_callback_t umoscom_intr_clear_stall_callback; + +static void umoscom_cfg_open(struct usb2_com_softc *ucom); +static void umoscom_cfg_close(struct usb2_com_softc *ucom); +static void umoscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static void umoscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void umoscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static int umoscom_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void umoscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void umoscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static void umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val); +static uint8_t umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg); +static void umoscom_cfg_do_request(struct umoscom_softc *sc, struct usb2_device_request *req, void *data); + +static void umoscom_start_read(struct usb2_com_softc *ucom); +static void umoscom_stop_read(struct usb2_com_softc *ucom); +static void umoscom_start_write(struct usb2_com_softc *ucom); +static void umoscom_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config umoscom_config_data[UMOSCOM_N_DATA_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMOSCOM_BUFSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &umoscom_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMOSCOM_BUFSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umoscom_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umoscom_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umoscom_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umoscom_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &umoscom_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback umoscom_callback = { + /* configuration callbacks */ + .usb2_com_cfg_get_status = &umoscom_cfg_get_status, + .usb2_com_cfg_set_dtr = &umoscom_cfg_set_dtr, + .usb2_com_cfg_set_rts = &umoscom_cfg_set_rts, + .usb2_com_cfg_set_break = &umoscom_cfg_set_break, + .usb2_com_cfg_param = &umoscom_cfg_param, + .usb2_com_cfg_open = &umoscom_cfg_open, + .usb2_com_cfg_close = &umoscom_cfg_close, + + /* other callbacks */ + .usb2_com_pre_param = &umoscom_pre_param, + .usb2_com_start_read = &umoscom_start_read, + .usb2_com_stop_read = &umoscom_stop_read, + .usb2_com_start_write = &umoscom_start_write, + .usb2_com_stop_write = &umoscom_stop_write, +}; + +static device_method_t umoscom_methods[] = { + DEVMETHOD(device_probe, umoscom_probe), + DEVMETHOD(device_attach, umoscom_attach), + DEVMETHOD(device_detach, umoscom_detach), + {0, 0} +}; + +static devclass_t umoscom_devclass; + +static driver_t umoscom_driver = { + .name = "umoscom", + .methods = umoscom_methods, + .size = sizeof(struct umoscom_softc), +}; + +DRIVER_MODULE(umoscom, ushub, umoscom_driver, umoscom_devclass, NULL, 0); +MODULE_DEPEND(umoscom, usb2_serial, 1, 1, 1); +MODULE_DEPEND(umoscom, usb2_core, 1, 1, 1); +MODULE_DEPEND(umoscom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static const struct usb2_device_id umoscom_devs[] = { + {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)} +}; + +static int +umoscom_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa)); +} + +static int +umoscom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umoscom_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_udev = uaa->device; + sc->sc_mcr = 0x08; /* enable interrupts */ + + /* XXX the device doesn't provide any ID string, so set a static one */ + device_set_desc(dev, "MOSCHIP USB Serial Port Adapter"); + device_printf(dev, "\n"); + + iface_index = UMOSCOM_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer_data, umoscom_config_data, + UMOSCOM_N_DATA_TRANSFER, sc, &Giant); + + if (error) { + goto detach; + } + /* clear stall at first run */ + sc->sc_flags |= (UMOSCOM_FLAG_READ_STALL | + UMOSCOM_FLAG_WRITE_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &umoscom_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + device_printf(dev, "attach error: %s\n", usb2_errstr(error)); + umoscom_detach(dev); + return (ENXIO); +} + +static int +umoscom_detach(device_t dev) +{ + struct umoscom_softc *sc = device_get_softc(dev); + + mtx_lock(&Giant); + + mtx_unlock(&Giant); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer_data, UMOSCOM_N_DATA_TRANSFER); + + return (0); +} + +static void +umoscom_cfg_open(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + /* Purge FIFOs or odd things happen */ + umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG); + + /* Enable FIFO */ + umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN | + UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR | + UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK | + UMOSCOM_UART_REG); + + /* Enable Interrupt Registers */ + umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG); + + /* Magic */ + umoscom_cfg_write(sc, 0x01, 0x08); + + /* Magic */ + umoscom_cfg_write(sc, 0x00, 0x02); + + return; +} + +static void +umoscom_cfg_close(struct usb2_com_softc *ucom) +{ + return; +} + +static void +umoscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint16_t val; + + val = sc->sc_lcr; + if (onoff) + val |= UMOSCOM_LCR_BREAK; + + umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG); + return; +} + +static void +umoscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= UMOSCOM_MCR_DTR; + else + sc->sc_mcr &= ~UMOSCOM_MCR_DTR; + + umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); + return; +} + +static void +umoscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= UMOSCOM_MCR_RTS; + else + sc->sc_mcr &= ~UMOSCOM_MCR_RTS; + + umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); + return; +} + +static int +umoscom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200)) + return (EINVAL); + + return (0); +} + +static void +umoscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint16_t data; + + DPRINTF("speed=%d\n", t->c_ospeed); + + data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed); + + if (data == 0) { + DPRINTF("invalid baud rate!\n"); + return; + } + umoscom_cfg_write(sc, UMOSCOM_LCR, + UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG); + + umoscom_cfg_write(sc, UMOSCOM_BAUDLO, + (data & 0xFF) | UMOSCOM_UART_REG); + + umoscom_cfg_write(sc, UMOSCOM_BAUDHI, + ((data >> 8) & 0xFF) | UMOSCOM_UART_REG); + + if (t->c_cflag & CSTOPB) + data = UMOSCOM_LCR_STOP_BITS_2; + else + data = UMOSCOM_LCR_STOP_BITS_1; + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) + data |= UMOSCOM_LCR_PARITY_ODD; + else + data |= UMOSCOM_LCR_PARITY_EVEN; + } else + data |= UMOSCOM_LCR_PARITY_NONE; + + switch (t->c_cflag & CSIZE) { + case CS5: + data |= UMOSCOM_LCR_DBITS(5); + break; + case CS6: + data |= UMOSCOM_LCR_DBITS(6); + break; + case CS7: + data |= UMOSCOM_LCR_DBITS(7); + break; + case CS8: + data |= UMOSCOM_LCR_DBITS(8); + break; + } + + sc->sc_lcr = data; + umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG); + + return; +} + +static void +umoscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint8_t lsr; + uint8_t msr; + + DPRINTFN(5, "\n"); + + /* read status registers */ + + lsr = umoscom_cfg_read(sc, UMOSCOM_LSR); + msr = umoscom_cfg_read(sc, UMOSCOM_MSR); + + /* translate bits */ + + if (msr & UMOSCOM_MSR_CTS) + *p_msr |= SER_CTS; + + if (msr & UMOSCOM_MSR_CD) + *p_msr |= SER_DCD; + + if (msr & UMOSCOM_MSR_RI) + *p_msr |= SER_RI; + + if (msr & UMOSCOM_MSR_RTS) + *p_msr |= SER_DSR; + + return; +} + +static void +umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UMOSCOM_WRITE; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + umoscom_cfg_do_request(sc, &req, NULL); +} + +static uint8_t +umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UMOSCOM_READ; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + umoscom_cfg_do_request(sc, &req, &val); + + DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val); + + return (val); +} + +static void +umoscom_cfg_do_request(struct umoscom_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) + goto error; + + err = usb2_do_request_flags + (sc->sc_udev, &Giant, req, data, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "control request failed: %s\n", + usb2_errstr(err)); +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +umoscom_start_read(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + +#if 0 + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer_data[4]); +#endif + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer_data[1]); + return; +} + +static void +umoscom_stop_read(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + /* stop interrupt transfer */ + usb2_transfer_stop(sc->sc_xfer_data[5]); + usb2_transfer_stop(sc->sc_xfer_data[4]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer_data[3]); + usb2_transfer_stop(sc->sc_xfer_data[1]); + return; +} + +static void +umoscom_start_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer_data[0]); + return; +} + +static void +umoscom_stop_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer_data[2]); + usb2_transfer_stop(sc->sc_xfer_data[0]); + return; +} + +static void +umoscom_write_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + DPRINTF("\n"); + + if (sc->sc_flags & UMOSCOM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer_data[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UMOSCOM_BUFSIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + sc->sc_flags |= UMOSCOM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer_data[2]); + } + return; + } +} + +static void +umoscom_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMOSCOM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umoscom_read_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("got %d bytes\n", xfer->actlen); + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + DPRINTF("\n"); + + if (sc->sc_flags & UMOSCOM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer_data[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + sc->sc_flags |= UMOSCOM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer_data[3]); + } + return; + + } +} + +static void +umoscom_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMOSCOM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umoscom_intr_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen < 2) { + DPRINTF("too short message\n"); + goto tr_setup; + } + usb2_com_status_change(&sc->sc_ucom); + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & UMOSCOM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer_data[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + sc->sc_flags |= UMOSCOM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer_data[5]); + } + return; + } +} + +static void +umoscom_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer_data[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMOSCOM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} diff --git a/sys/dev/usb2/serial/uplcom2.c b/sys/dev/usb2/serial/uplcom2.c new file mode 100644 index 000000000000..ebd8cb4d8832 --- /dev/null +++ b/sys/dev/usb2/serial/uplcom2.c @@ -0,0 +1,964 @@ +/* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . + * 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) 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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR uplcom_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int uplcom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom"); +SYSCTL_INT(_hw_usb2_uplcom, OID_AUTO, debug, CTLFLAG_RW, + &uplcom_debug, 0, "Debug level"); +#endif + +#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 0 /* default */ +#endif + +#define UPLCOM_BULK_BUF_SIZE 1024 /* bytes */ +#define UPLCOM_N_TRANSFER 6 + +#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 usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UPLCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_line; + + uint8_t sc_flag; +#define UPLCOM_FLAG_INTR_STALL 0x01 +#define UPLCOM_FLAG_READ_STALL 0x02 +#define UPLCOM_FLAG_WRITE_STALL 0x04 + + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* uplcom status register */ + uint8_t sc_chiptype; /* type of chip */ + uint8_t sc_ctrl_iface_no; + uint8_t sc_data_iface_no; + uint8_t sc_iface_index[2]; +}; + +/* prototypes */ + +static usb2_error_t uplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev); +static int uplcom_pl2303x_init(struct usb2_device *udev); +static void uplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void uplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void uplcom_cfg_set_break(struct usb2_com_softc *sc, uint8_t onoff); +static int uplcom_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void uplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static void uplcom_start_read(struct usb2_com_softc *ucom); +static void uplcom_stop_read(struct usb2_com_softc *ucom); +static void uplcom_start_write(struct usb2_com_softc *ucom); +static void uplcom_stop_write(struct usb2_com_softc *ucom); +static void uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static int uplcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td); +static void uplcom_cfg_do_request(struct uplcom_softc *sc, struct usb2_device_request *req, void *data); + +static device_probe_t uplcom_probe; +static device_attach_t uplcom_attach; +static device_detach_t uplcom_detach; + +static usb2_callback_t uplcom_intr_callback; +static usb2_callback_t uplcom_intr_clear_stall_callback; +static usb2_callback_t uplcom_write_callback; +static usb2_callback_t uplcom_write_clear_stall_callback; +static usb2_callback_t uplcom_read_callback; +static usb2_callback_t uplcom_read_clear_stall_callback; + +static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UPLCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uplcom_write_callback, + .if_index = 0, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UPLCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uplcom_read_callback, + .if_index = 0, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uplcom_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + .if_index = 0, + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uplcom_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + .if_index = 0, + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &uplcom_intr_callback, + .if_index = 1, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uplcom_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + .if_index = 1, + }, +}; + +struct usb2_com_callback uplcom_callback = { + .usb2_com_cfg_get_status = &uplcom_cfg_get_status, + .usb2_com_cfg_set_dtr = &uplcom_cfg_set_dtr, + .usb2_com_cfg_set_rts = &uplcom_cfg_set_rts, + .usb2_com_cfg_set_break = &uplcom_cfg_set_break, + .usb2_com_cfg_param = &uplcom_cfg_param, + .usb2_com_pre_param = &uplcom_pre_param, + .usb2_com_ioctl = &uplcom_ioctl, + .usb2_com_start_read = &uplcom_start_read, + .usb2_com_stop_read = &uplcom_stop_read, + .usb2_com_start_write = &uplcom_start_write, + .usb2_com_stop_write = &uplcom_stop_write, +}; + +#define USB_UPL(v,p,rl,rh,t) \ + USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl), \ + USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t) + +static const struct usb2_device_id uplcom_devs[] = { + /* Belkin F5U257 */ + {USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)}, + /* I/O DATA USB-RSAQ */ + {USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)}, + /* I/O DATA USB-RSAQ2 */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)}, + /* I/O DATA USB-RSAQ3 */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)}, + /* PLANEX USB-RS232 URS-03 */ + {USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)}, + /* TrendNet TU-S9 */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)}, + /* ST Lab USB-SERIAL-4 */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)}, + /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)}, + /* TDK USB-PHS Adapter UHA6400 */ + {USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)}, + /* RATOC REX-USB60 */ + {USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)}, + /* ELECOM UC-SGT */ + {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)}, + {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)}, + /* Sagem USB-Serial Controller */ + {USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)}, + /* Sony Ericsson USB Cable */ + {USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)}, + /* SOURCENEXT KeikaiDenwa 8 */ + {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)}, + /* SOURCENEXT KeikaiDenwa 8 with charger */ + {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)}, + /* HAL Corporation Crossam2+USB */ + {USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)}, + /* Sitecom USB to Serial */ + {USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)}, + /* Tripp-Lite U209-000-R */ + {USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)}, + {USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)}, + /* Prolific Pharos */ + {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)}, + /* Willcom W-SIM */ + {USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)}, +}; + +static device_method_t uplcom_methods[] = { + DEVMETHOD(device_probe, uplcom_probe), + DEVMETHOD(device_attach, uplcom_attach), + DEVMETHOD(device_detach, uplcom_detach), + {0, 0} +}; + +static devclass_t uplcom_devclass; + +static driver_t uplcom_driver = { + .name = "uplcom", + .methods = uplcom_methods, + .size = sizeof(struct uplcom_softc), +}; + +DRIVER_MODULE(uplcom, ushub, uplcom_driver, uplcom_devclass, NULL, 0); +MODULE_DEPEND(uplcom, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uplcom, usb2_core, 1, 1, 1); +MODULE_DEPEND(uplcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); +MODULE_VERSION(uplcom, UPLCOM_MODVER); + +static int +uplcom_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + DPRINTFN(11, "\n"); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa)); +} + +static int +uplcom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uplcom_softc *sc = device_get_softc(dev); + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + int error; + + DPRINTFN(11, "\n"); + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + DPRINTF("sc = %p\n", sc); + + sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa); + sc->sc_udev = uaa->device; + + DPRINTF("chiptype: %s\n", + (sc->sc_chiptype == TYPE_PL2303X) ? + "2303X" : "2303"); + + /* + * 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) | + */ + + sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX; + + iface = usb2_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX); + if (iface) { + id = usb2_get_interface_descriptor(iface); + if (id == NULL) { + device_printf(dev, "no interface descriptor (2)!\n"); + goto detach; + } + sc->sc_data_iface_no = id->bInterfaceNumber; + sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX; + usb2_set_parent_iface(uaa->device, + UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex); + } else { + sc->sc_data_iface_no = sc->sc_ctrl_iface_no; + sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX; + } + + error = usb2_transfer_setup(uaa->device, + sc->sc_iface_index, sc->sc_xfer, uplcom_config_data, + UPLCOM_N_TRANSFER, sc, &Giant); + if (error) { + DPRINTF("one or more missing USB endpoints, " + "error=%s\n", usb2_errstr(error)); + goto detach; + } + error = uplcom_reset(sc, uaa->device); + if (error) { + device_printf(dev, "reset failed, error=%s\n", + usb2_errstr(error)); + goto detach; + } + /* clear stall at first run */ + sc->sc_flag |= (UPLCOM_FLAG_READ_STALL | + UPLCOM_FLAG_WRITE_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uplcom_callback, &Giant); + if (error) { + goto detach; + } + /* + * do the initialization during attach so that the system does not + * sleep during open: + */ + if (sc->sc_chiptype == TYPE_PL2303X) { + if (uplcom_pl2303x_init(uaa->device)) { + device_printf(dev, "init failed!\n"); + goto detach; + } + } + return (0); + +detach: + uplcom_detach(dev); + return (ENXIO); +} + +static int +uplcom_detach(device_t dev) +{ + struct uplcom_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER); + + return (0); +} + +static usb2_error_t +uplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UPLCOM_SET_REQUEST; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_data_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + return (usb2_do_request(udev, &Giant, &req, NULL)); +} + +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, 1}, + {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0}, + {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, + {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1}, + {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, + {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0}, + {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, + {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1}, + {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}, + {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0}, + {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0}, +}; + +#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0])) + +static int +uplcom_pl2303x_init(struct usb2_device *udev) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t buf[4]; + uint8_t 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 = usb2_do_request(udev, &Giant, &req, buf); + if (err) { + DPRINTF("error=%s\n", usb2_errstr(err)); + return (EIO); + } + } + return (0); +} + +static void +uplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uplcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_DTR; + else + sc->sc_line &= ~UCDC_LINE_DTR; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = sc->sc_data_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uplcom_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uplcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line |= UCDC_LINE_RTS; + else + sc->sc_line &= ~UCDC_LINE_RTS; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line); + req.wIndex[0] = sc->sc_data_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uplcom_cfg_do_request(sc, &req, NULL); + return; +} + +static void +uplcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uplcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t temp; + + DPRINTF("onoff = %d\n", onoff); + + temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SEND_BREAK; + USETW(req.wValue, temp); + req.wIndex[0] = sc->sc_data_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + uplcom_cfg_do_request(sc, &req, NULL); + return; +} + +static const int32_t 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_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + uint8_t i; + + DPRINTF("\n"); + + /* check requested baud rate */ + + for (i = 0;; i++) { + + if (i != N_UPLCOM_RATES) { + if (uplcom_rates[i] == t->c_ospeed) { + break; + } + } else { + DPRINTF("invalid baud rate (%d)\n", t->c_ospeed); + return (EIO); + } + } + + return (0); +} + +static void +uplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uplcom_softc *sc = ucom->sc_parent; + struct usb2_cdc_line_state ls; + struct usb2_device_request req; + + DPRINTF("sc = %p\n", sc); + + bzero(&ls, sizeof(ls)); + + USETDW(ls.dwDTERate, t->c_ospeed); + + if (t->c_cflag & CSTOPB) { + ls.bCharFormat = UCDC_STOP_BIT_2; + } else { + ls.bCharFormat = UCDC_STOP_BIT_1; + } + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + ls.bParityType = UCDC_PARITY_ODD; + } else { + ls.bParityType = UCDC_PARITY_EVEN; + } + } else { + ls.bParityType = UCDC_PARITY_NONE; + } + + switch (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; + } + + DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", + UGETDW(ls.dwDTERate), ls.bCharFormat, + ls.bParityType, ls.bDataBits); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_LINE_CODING; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_data_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, UCDC_LINE_STATE_LENGTH); + + uplcom_cfg_do_request(sc, &req, &ls); + + if (t->c_cflag & CRTSCTS) { + + DPRINTF("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); + + uplcom_cfg_do_request(sc, &req, NULL); + } else { + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UPLCOM_SET_REQUEST; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + uplcom_cfg_do_request(sc, &req, NULL); + } + return; +} + +static void +uplcom_start_read(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer[4]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uplcom_stop_read(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + /* stop interrupt endpoint */ + usb2_transfer_stop(sc->sc_xfer[4]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uplcom_start_write(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uplcom_stop_write(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static int +uplcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, + struct thread *td) +{ + return (ENOTTY); +} + +static void +uplcom_intr_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + uint8_t buf[9]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("actlen = %u\n", xfer->actlen); + + if (xfer->actlen >= 9) { + + usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf)); + + DPRINTF("status = 0x%02x\n", buf[8]); + + sc->sc_lsr = 0; + sc->sc_msr = 0; + + if (buf[8] & RSAQ_STATUS_CTS) { + sc->sc_msr |= SER_CTS; + } + if (buf[8] & RSAQ_STATUS_DSR) { + sc->sc_msr |= SER_DSR; + } + if (buf[8] & RSAQ_STATUS_DCD) { + sc->sc_msr |= SER_DCD; + } + usb2_com_status_change(&sc->sc_ucom); + } + case USB_ST_SETUP: + if (sc->sc_flag & UPLCOM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UPLCOM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + + } +} + +static void +uplcom_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UPLCOM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uplcom_write_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UPLCOM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UPLCOM_BULK_BUF_SIZE, &actlen)) { + + DPRINTF("actlen = %d\n", actlen); + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UPLCOM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uplcom_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UPLCOM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uplcom_read_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UPLCOM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UPLCOM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uplcom_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uplcom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UPLCOM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uplcom_cfg_do_request(struct uplcom_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + goto error; + } + err = usb2_do_request_flags(sc->sc_udev, &Giant, req, + data, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} diff --git a/sys/dev/usb2/serial/usb2_serial.c b/sys/dev/usb2/serial/usb2_serial.c new file mode 100644 index 000000000000..e9cf9e52058f --- /dev/null +++ b/sys/dev/usb2/serial/usb2_serial.c @@ -0,0 +1,1112 @@ +/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ + +/*- + * Copyright (c) 2001-2003, 2005, 2008 + * Shunsuke Akiyama . + * 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 +__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. + */ + +/* + * NOTE: all function names beginning like "usb2_com_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_com_debug +#define usb2_config_td_cc usb2_com_config_copy +#define usb2_config_td_softc usb2_com_softc + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int usb2_com_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); +SYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW, + &usb2_com_debug, 0, "ucom debug level"); +#endif + +struct usb2_com_config_copy { + struct usb2_com_softc *cc_softc; + uint8_t cc_flag0; + uint8_t cc_flag1; + uint8_t cc_flag2; + uint8_t cc_flag3; +}; + +static usb2_config_td_command_t usb2_com_config_copy; +static usb2_config_td_command_t usb2_com_cfg_start_transfers; +static usb2_config_td_command_t usb2_com_cfg_open; +static usb2_config_td_command_t usb2_com_cfg_close; +static usb2_config_td_command_t usb2_com_cfg_break; +static usb2_config_td_command_t usb2_com_cfg_dtr; +static usb2_config_td_command_t usb2_com_cfg_rts; +static usb2_config_td_command_t usb2_com_cfg_status_change; +static usb2_config_td_command_t usb2_com_cfg_param; + +static uint8_t usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit); +static void usb2_com_units_free(uint32_t root_unit, uint32_t sub_units); +static int usb2_com_attach_sub(struct usb2_com_softc *sc); +static void usb2_com_detach_sub(struct usb2_com_softc *sc); +static void usb2_com_queue_command(struct usb2_com_softc *sc, usb2_config_td_command_t *cmd, int flag); +static void usb2_com_shutdown(struct usb2_com_softc *sc); +static void usb2_com_start_transfers(struct usb2_com_softc *sc); +static void usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff); +static void usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff); +static void usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff); + +static tsw_open_t usb2_com_open; +static tsw_close_t usb2_com_close; +static tsw_ioctl_t usb2_com_ioctl; +static tsw_modem_t usb2_com_modem; +static tsw_param_t usb2_com_param; +static tsw_outwakeup_t usb2_com_start_write; +static tsw_free_t usb2_com_free; + +static struct ttydevsw usb2_com_class = { + .tsw_flags = TF_INITLOCK | TF_CALLOUT, + .tsw_open = usb2_com_open, + .tsw_close = usb2_com_close, + .tsw_outwakeup = usb2_com_start_write, + .tsw_ioctl = usb2_com_ioctl, + .tsw_param = usb2_com_param, + .tsw_modem = usb2_com_modem, + .tsw_free = usb2_com_free, +}; + +MODULE_DEPEND(ucom, usb2_core, 1, 1, 1); +MODULE_VERSION(ucom, UCOM_MODVER); +MODULE_VERSION(usb2_serial, 1); + +#define UCOM_UNIT_MAX 0x1000 /* exclusive */ +#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ + +static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8]; + +static uint8_t +usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) +{ + uint32_t n; + uint32_t o; + uint32_t x; + uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); + uint8_t error = 1; + + mtx_lock(&Giant); + + for (n = 0; n < max; n += sub_units) { + + /* check for free consecutive bits */ + + for (o = 0; o < sub_units; o++) { + + x = n + o; + + if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) { + goto skip; + } + } + + /* allocate */ + + for (o = 0; o < sub_units; o++) { + + x = n + o; + + usb2_com_bitmap[x / 8] |= (1 << (x % 8)); + } + + error = 0; + + break; + +skip: ; + } + + mtx_unlock(&Giant); + + /* + * Always set the variable pointed to by "p_root_unit" so that + * the compiler does not think that it is used uninitialised: + */ + *p_root_unit = n; + + return (error); +} + +static void +usb2_com_units_free(uint32_t root_unit, uint32_t sub_units) +{ + uint32_t x; + + mtx_lock(&Giant); + + while (sub_units--) { + x = root_unit + sub_units; + usb2_com_bitmap[x / 8] &= ~(1 << (x % 8)); + } + + mtx_unlock(&Giant); + + return; +} + +/* + * "N" sub_units are setup at a time. All sub-units will + * be given sequential unit numbers. The number of + * sub-units can be used to differentiate among + * different types of devices. + * + * The mutex pointed to by "p_mtx" is applied before all + * callbacks are called back. Also "p_mtx" must be applied + * before calling into the ucom-layer! Currently only Giant + * is supported. + */ +int +usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, + uint32_t sub_units, void *parent, + const struct usb2_com_callback *callback, struct mtx *p_mtx) +{ + uint32_t n; + uint32_t root_unit; + int error = 0; + + if ((sc == NULL) || + (sub_units == 0) || + (sub_units > UCOM_SUB_UNIT_MAX) || + (callback == NULL)) { + return (EINVAL); + } + if (usb2_com_units_alloc(sub_units, &root_unit)) { + return (ENOMEM); + } + if (usb2_config_td_setup + (&ssc->sc_config_td, sc, p_mtx, NULL, + sizeof(struct usb2_com_config_copy), 24 * sub_units)) { + usb2_com_units_free(root_unit, sub_units); + return (ENOMEM); + } + for (n = 0; n < sub_units; n++, sc++) { + sc->sc_unit = root_unit + n; + sc->sc_local_unit = n; + sc->sc_super = ssc; + sc->sc_parent_mtx = p_mtx; + sc->sc_parent = parent; + sc->sc_callback = callback; + + error = usb2_com_attach_sub(sc); + if (error) { + usb2_com_detach(ssc, sc - n, n); + usb2_com_units_free(root_unit + n, sub_units - n); + break; + } + sc->sc_flag |= UCOM_FLAG_ATTACHED; + } + return (error); +} + +/* NOTE: the following function will do nothing if + * the structure pointed to by "ssc" and "sc" is zero. + */ +void +usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, + uint32_t sub_units) +{ + uint32_t n; + + usb2_config_td_drain(&ssc->sc_config_td); + + for (n = 0; n < sub_units; n++, sc++) { + if (sc->sc_flag & UCOM_FLAG_ATTACHED) { + + usb2_com_detach_sub(sc); + + usb2_com_units_free(sc->sc_unit, 1); + + /* avoid duplicate detach: */ + sc->sc_flag &= ~UCOM_FLAG_ATTACHED; + } + } + + usb2_config_td_unsetup(&ssc->sc_config_td); + + return; +} + +static int +usb2_com_attach_sub(struct usb2_com_softc *sc) +{ + struct tty *tp; + int error = 0; + char buf[32]; /* temporary TTY device name buffer */ + + tp = tty_alloc(&usb2_com_class, sc, sc->sc_parent_mtx); + if (tp == NULL) { + error = ENOMEM; + goto done; + } + DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); + + buf[0] = 0; /* set some default value */ + + /* Check if the client has a custom TTY name */ + if (sc->sc_callback->usb2_com_tty_name) { + sc->sc_callback->usb2_com_tty_name(sc, buf, + sizeof(buf), sc->sc_local_unit); + } + if (buf[0] == 0) { + /* Use default TTY name */ + if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { + /* ignore */ + } + } + tty_makedev(tp, NULL, "%s", buf); + + sc->sc_tty = tp; + + DPRINTF("ttycreate: %s\n", buf); + usb2_cv_init(&sc->sc_cv, "usb2_com"); + +done: + return (error); +} + +static void +usb2_com_detach_sub(struct usb2_com_softc *sc) +{ + struct tty *tp = sc->sc_tty; + + DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); + + /* the config thread has been stopped when we get here */ + + mtx_lock(sc->sc_parent_mtx); + sc->sc_flag |= UCOM_FLAG_GONE; + sc->sc_flag &= ~(UCOM_FLAG_HL_READY | + UCOM_FLAG_LL_READY); + mtx_unlock(sc->sc_parent_mtx); + if (tp) { + tty_lock(tp); + + usb2_com_close(tp); /* close, if any */ + + tty_rel_gone(tp); + + mtx_lock(sc->sc_parent_mtx); + /* Wait for the callback after the TTY is torn down */ + while (sc->sc_ttyfreed == 0) + usb2_cv_wait(&sc->sc_cv, sc->sc_parent_mtx); + /* + * make sure that read and write transfers are stopped + */ + if (sc->sc_callback->usb2_com_stop_read) { + (sc->sc_callback->usb2_com_stop_read) (sc); + } + if (sc->sc_callback->usb2_com_stop_write) { + (sc->sc_callback->usb2_com_stop_write) (sc); + } + mtx_unlock(sc->sc_parent_mtx); + } + usb2_cv_destroy(&sc->sc_cv); + return; +} + +static void +usb2_com_config_copy(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + cc->cc_softc = sc + (refcount % UCOM_SUB_UNIT_MAX); + cc->cc_flag0 = (refcount / (1 * UCOM_SUB_UNIT_MAX)) % 2; + cc->cc_flag1 = (refcount / (2 * UCOM_SUB_UNIT_MAX)) % 2; + cc->cc_flag2 = (refcount / (4 * UCOM_SUB_UNIT_MAX)) % 2; + cc->cc_flag3 = (refcount / (8 * UCOM_SUB_UNIT_MAX)) % 2; + return; +} + +static void +usb2_com_queue_command(struct usb2_com_softc *sc, usb2_config_td_command_t *cmd, int flag) +{ + struct usb2_com_super_softc *ssc = sc->sc_super; + + usb2_config_td_queue_command + (&ssc->sc_config_td, &usb2_com_config_copy, + cmd, (cmd == &usb2_com_cfg_status_change) ? 1 : 0, + ((sc->sc_local_unit % UCOM_SUB_UNIT_MAX) + + (flag ? UCOM_SUB_UNIT_MAX : 0))); + return; +} + +static void +usb2_com_shutdown(struct usb2_com_softc *sc) +{ + struct tty *tp = sc->sc_tty; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + DPRINTF("\n"); + + /* + * Hang up if necessary: + */ + if (tp->t_termios.c_cflag & HUPCL) { + usb2_com_modem(tp, 0, SER_DTR); + } + return; +} + +/* + * Return values: + * 0: normal delay + * else: config thread is gone + */ +uint8_t +usb2_com_cfg_sleep(struct usb2_com_softc *sc, uint32_t timeout) +{ + struct usb2_com_super_softc *ssc = sc->sc_super; + + return (usb2_config_td_sleep(&ssc->sc_config_td, timeout)); +} + +/* + * Return values: + * 0: normal + * else: config thread is gone + */ +uint8_t +usb2_com_cfg_is_gone(struct usb2_com_softc *sc) +{ + struct usb2_com_super_softc *ssc = sc->sc_super; + + return (usb2_config_td_is_gone(&ssc->sc_config_td)); +} + +static void +usb2_com_cfg_start_transfers(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + /* TTY device closed */ + return; + } + sc->sc_flag |= UCOM_FLAG_GP_DATA; + + if (sc->sc_callback->usb2_com_start_read) { + (sc->sc_callback->usb2_com_start_read) (sc); + } + if (sc->sc_callback->usb2_com_start_write) { + (sc->sc_callback->usb2_com_start_write) (sc); + } + return; +} + +static void +usb2_com_start_transfers(struct usb2_com_softc *sc) +{ + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + /* + * do a direct call first, to get hardware buffers flushed + */ + + if (sc->sc_callback->usb2_com_start_read) { + (sc->sc_callback->usb2_com_start_read) (sc); + } + if (sc->sc_callback->usb2_com_start_write) { + (sc->sc_callback->usb2_com_start_write) (sc); + } + if (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) { + usb2_com_queue_command(sc, &usb2_com_cfg_start_transfers, 0); + } + return; +} + +static void +usb2_com_cfg_open(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + DPRINTF("\n"); + + if (sc->sc_flag & UCOM_FLAG_LL_READY) { + + /* already opened */ + + } else { + + sc->sc_flag |= UCOM_FLAG_LL_READY; + + if (sc->sc_callback->usb2_com_cfg_open) { + (sc->sc_callback->usb2_com_cfg_open) (sc); + + /* wait a little */ + usb2_com_cfg_sleep(sc, hz / 10); + } + } + return; +} + +static int +usb2_com_open(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + int error; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (sc->sc_flag & UCOM_FLAG_GONE) { + return (ENXIO); + } + if (sc->sc_flag & UCOM_FLAG_HL_READY) { + /* already opened */ + return (0); + } + DPRINTF("tp = %p\n", tp); + + if (sc->sc_callback->usb2_com_pre_open) { + /* + * give the lower layer a chance to disallow TTY open, for + * example if the device is not present: + */ + error = (sc->sc_callback->usb2_com_pre_open) (sc); + if (error) { + return (error); + } + } + sc->sc_flag |= UCOM_FLAG_HL_READY; + + /* Disable transfers */ + sc->sc_flag &= ~UCOM_FLAG_GP_DATA; + + sc->sc_lsr = 0; + sc->sc_msr = 0; + sc->sc_mcr = 0; + + usb2_com_queue_command(sc, &usb2_com_cfg_open, 0); + + usb2_com_start_transfers(sc); + + usb2_com_modem(tp, SER_DTR | SER_RTS, 0); + + usb2_com_break(sc, 0); + + usb2_com_status_change(sc); + + return (0); +} + +static void +usb2_com_cfg_close(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + DPRINTF("\n"); + + if (sc->sc_flag & UCOM_FLAG_LL_READY) { + + sc->sc_flag &= ~(UCOM_FLAG_LL_READY | + UCOM_FLAG_GP_DATA); + + if (sc->sc_callback->usb2_com_cfg_close) { + (sc->sc_callback->usb2_com_cfg_close) (sc); + } + } else { + /* already closed */ + } + return; +} + +static void +usb2_com_close(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + DPRINTF("tp=%p\n", tp); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + DPRINTF("tp=%p already closed\n", tp); + return; + } + usb2_com_shutdown(sc); + + usb2_com_queue_command(sc, &usb2_com_cfg_close, 0); + + sc->sc_flag &= ~(UCOM_FLAG_HL_READY | + UCOM_FLAG_WR_START | + UCOM_FLAG_RTS_IFLOW); + + if (sc->sc_callback->usb2_com_stop_read) { + (sc->sc_callback->usb2_com_stop_read) (sc); + } + if (sc->sc_callback->usb2_com_stop_write) { + (sc->sc_callback->usb2_com_stop_write) (sc); + } + return; +} + +static int +usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) +{ + struct usb2_com_softc *sc = tty_softc(tp); + int error; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return (EIO); + } + DPRINTF("cmd = 0x%08lx\n", cmd); + + switch (cmd) { + case TIOCSBRK: + usb2_com_break(sc, 1); + error = 0; + break; + case TIOCCBRK: + usb2_com_break(sc, 0); + error = 0; + break; + default: + if (sc->sc_callback->usb2_com_ioctl) { + error = (sc->sc_callback->usb2_com_ioctl) + (sc, cmd, data, 0, td); + } else { + error = ENOIOCTL; + } + break; + } + return (error); +} + +static int +usb2_com_modem(struct tty *tp, int sigon, int sigoff) +{ + struct usb2_com_softc *sc = tty_softc(tp); + uint8_t onoff; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return (0); + } + if ((sigon == 0) && (sigoff == 0)) { + + if (sc->sc_mcr & SER_DTR) { + sigon |= SER_DTR; + } + if (sc->sc_mcr & SER_RTS) { + sigon |= SER_RTS; + } + if (sc->sc_msr & SER_CTS) { + sigon |= SER_CTS; + } + if (sc->sc_msr & SER_DCD) { + sigon |= SER_DCD; + } + if (sc->sc_msr & SER_DSR) { + sigon |= SER_DSR; + } + if (sc->sc_msr & SER_RI) { + sigon |= SER_RI; + } + return (sigon); + } + if (sigon & SER_DTR) { + sc->sc_mcr |= SER_DTR; + } + if (sigoff & SER_DTR) { + sc->sc_mcr &= ~SER_DTR; + } + if (sigon & SER_RTS) { + sc->sc_mcr |= SER_RTS; + } + if (sigoff & SER_RTS) { + sc->sc_mcr &= ~SER_RTS; + } + onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; + usb2_com_dtr(sc, onoff); + + onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; + usb2_com_rts(sc, onoff); + + return (0); +} + +static void +usb2_com_cfg_break(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + DPRINTF("onoff=%d\n", cc->cc_flag0); + + if (sc->sc_callback->usb2_com_cfg_set_break) { + (sc->sc_callback->usb2_com_cfg_set_break) (sc, cc->cc_flag0); + } + return; +} + +static void +usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff) +{ + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + DPRINTF("onoff = %d\n", onoff); + + usb2_com_queue_command(sc, &usb2_com_cfg_break, onoff); + return; +} + +static void +usb2_com_cfg_dtr(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + DPRINTF("onoff=%d\n", cc->cc_flag0); + + if (sc->sc_callback->usb2_com_cfg_set_dtr) { + (sc->sc_callback->usb2_com_cfg_set_dtr) (sc, cc->cc_flag0); + } + return; +} + +static void +usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff) +{ + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + DPRINTF("onoff = %d\n", onoff); + + usb2_com_queue_command(sc, &usb2_com_cfg_dtr, onoff); + return; +} + +static void +usb2_com_cfg_rts(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + sc = cc->cc_softc; + + DPRINTF("onoff=%d\n", cc->cc_flag0); + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + if (sc->sc_callback->usb2_com_cfg_set_rts) { + (sc->sc_callback->usb2_com_cfg_set_rts) (sc, cc->cc_flag0); + } + return; +} + +static void +usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff) +{ + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + DPRINTF("onoff = %d\n", onoff); + + usb2_com_queue_command(sc, &usb2_com_cfg_rts, onoff); + + return; +} + +static void +usb2_com_cfg_status_change(struct usb2_com_softc *sc, + struct usb2_com_config_copy *cc, uint16_t refcount) +{ + struct tty *tp; + + uint8_t new_msr; + uint8_t new_lsr; + uint8_t onoff; + + sc = cc->cc_softc; + tp = sc->sc_tty; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + if (sc->sc_callback->usb2_com_cfg_get_status == NULL) { + return; + } + /* get status */ + + new_msr = 0; + new_lsr = 0; + + (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + /* TTY device closed */ + return; + } + onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); + + sc->sc_msr = new_msr; + sc->sc_lsr = new_lsr; + + if (onoff) { + + onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; + + DPRINTF("DCD changed to %d\n", onoff); + + ttydisc_modem(tp, onoff); + } + return; +} + +void +usb2_com_status_change(struct usb2_com_softc *sc) +{ + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + DPRINTF("\n"); + + usb2_com_queue_command(sc, &usb2_com_cfg_status_change, 0); + return; +} + +static void +usb2_com_cfg_param(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, + uint16_t refcount) +{ + struct termios t_copy; + + sc = cc->cc_softc; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + if (sc->sc_callback->usb2_com_cfg_param == NULL) { + return; + } + t_copy = sc->sc_termios_copy; + + (sc->sc_callback->usb2_com_cfg_param) (sc, &t_copy); + + /* wait a little */ + usb2_com_cfg_sleep(sc, hz / 10); + + return; +} + +static int +usb2_com_param(struct tty *tp, struct termios *t) +{ + struct usb2_com_softc *sc = tty_softc(tp); + uint8_t opened; + int error; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + opened = 0; + error = 0; + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + + /* XXX the TTY layer should call "open()" first! */ + + error = usb2_com_open(tp); + if (error) { + goto done; + } + opened = 1; + } + DPRINTF("sc = %p\n", sc); + + /* Check requested parameters. */ + if (t->c_ospeed < 0) { + DPRINTF("negative ospeed\n"); + error = EINVAL; + goto done; + } + if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { + DPRINTF("mismatch ispeed and ospeed\n"); + error = EINVAL; + goto done; + } + t->c_ispeed = t->c_ospeed; + + if (sc->sc_callback->usb2_com_pre_param) { + /* Let the lower layer verify the parameters */ + error = (sc->sc_callback->usb2_com_pre_param) (sc, t); + if (error) { + DPRINTF("callback error = %d\n", error); + goto done; + } + } + /* Make a copy of the termios parameters */ + sc->sc_termios_copy = *t; + + /* Disable transfers */ + sc->sc_flag &= ~UCOM_FLAG_GP_DATA; + + /* Queue baud rate programming command first */ + usb2_com_queue_command(sc, &usb2_com_cfg_param, 0); + + /* Queue transfer enable command last */ + usb2_com_start_transfers(sc); + + if (t->c_cflag & CRTS_IFLOW) { + sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; + } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { + sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; + usb2_com_modem(tp, SER_RTS, 0); + } +done: + if (error) { + if (opened) { + usb2_com_close(tp); + } + } + return (error); +} + +static void +usb2_com_start_write(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + DPRINTF("sc = %p\n", sc); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + /* The higher layer is not ready */ + return; + } + sc->sc_flag |= UCOM_FLAG_WR_START; + + usb2_com_start_transfers(sc); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_com_get_data + * + * Return values: + * 0: No data is available. + * Else: Data is available. + *------------------------------------------------------------------------*/ +uint8_t +usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, + uint32_t offset, uint32_t len, uint32_t *actlen) +{ + struct usb2_page_search res; + struct tty *tp = sc->sc_tty; + uint32_t cnt; + uint32_t offset_orig; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || + (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) || + (!(sc->sc_flag & UCOM_FLAG_WR_START))) { + actlen[0] = 0; + return (0); /* multiport device polling */ + } + offset_orig = offset; + + while (len != 0) { + + usb2_get_page(pc, offset, &res); + + if (res.length > len) { + res.length = len; + } + /* copy data directly into USB buffer */ + cnt = ttydisc_getc(tp, res.buffer, res.length); + + offset += cnt; + len -= cnt; + + if (cnt < res.length) { + /* end of buffer */ + break; + } + } + + actlen[0] = offset - offset_orig; + + DPRINTF("cnt=%d\n", actlen[0]); + + if (actlen[0] == 0) { + return (0); + } + return (1); +} + +void +usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, + uint32_t offset, uint32_t len) +{ + struct usb2_page_search res; + struct tty *tp = sc->sc_tty; + char *buf; + uint32_t cnt; + + mtx_assert(sc->sc_parent_mtx, MA_OWNED); + + if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || + (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) { + return; /* multiport device polling */ + } + if (len == 0) + return; /* no data */ + + /* set a flag to prevent recursation ? */ + + while (len > 0) { + + usb2_get_page(pc, offset, &res); + + if (res.length > len) { + res.length = len; + } + len -= res.length; + offset += res.length; + + /* pass characters to tty layer */ + + buf = res.buffer; + cnt = res.length; + + /* first check if we can pass the buffer directly */ + + if (ttydisc_can_bypass(tp)) { + if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { + DPRINTF("tp=%p, data lost\n", tp); + } + continue; + } + /* need to loop */ + + for (cnt = 0; cnt != res.length; cnt++) { + if (ttydisc_rint(tp, buf[cnt], 0) == -1) { + /* XXX what should we do? */ + + DPRINTF("tp=%p, lost %d " + "chars\n", tp, res.length - cnt); + break; + } + } + } + ttydisc_rint_done(tp); + return; +} + +static void +usb2_com_free(void *xsc) +{ + struct usb2_com_softc *sc = xsc; + + mtx_lock(sc->sc_parent_mtx); + sc->sc_ttyfreed = 1; + usb2_cv_signal(&sc->sc_cv); + mtx_unlock(sc->sc_parent_mtx); +} diff --git a/sys/dev/usb2/serial/usb2_serial.h b/sys/dev/usb2/serial/usb2_serial.h new file mode 100644 index 000000000000..aa4c7d04da68 --- /dev/null +++ b/sys/dev/usb2/serial/usb2_serial.h @@ -0,0 +1,159 @@ +/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2001-2002, Shunsuke Akiyama . + * 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. + */ + +#ifndef _USB2_SERIAL_H_ +#define _USB2_SERIAL_H_ + +#include +#include +#include +#include + +/* Module interface related macros */ +#define UCOM_MODVER 1 + +#define UCOM_MINVER 1 +#define UCOM_PREFVER UCOM_MODVER +#define UCOM_MAXVER 1 + +struct usb2_com_softc; +struct thread; + +/* NOTE: Only callbacks with "_cfg_" in its name are called + * from a config thread, and are allowed to sleep! The other + * callbacks are _not_ allowed to sleep! + * + * NOTE: There is no guarantee that "usb2_com_cfg_close()" will + * be called after "usb2_com_cfg_open()" if the device is detached + * while it is open! + */ +struct usb2_com_callback { + void (*usb2_com_cfg_get_status) (struct usb2_com_softc *, uint8_t *plsr, uint8_t *pmsr); + void (*usb2_com_cfg_set_dtr) (struct usb2_com_softc *, uint8_t); + void (*usb2_com_cfg_set_rts) (struct usb2_com_softc *, uint8_t); + void (*usb2_com_cfg_set_break) (struct usb2_com_softc *, uint8_t); + void (*usb2_com_cfg_param) (struct usb2_com_softc *, struct termios *); + void (*usb2_com_cfg_open) (struct usb2_com_softc *); + void (*usb2_com_cfg_close) (struct usb2_com_softc *); + int (*usb2_com_pre_open) (struct usb2_com_softc *); + int (*usb2_com_pre_param) (struct usb2_com_softc *, struct termios *); + int (*usb2_com_ioctl) (struct usb2_com_softc *, uint32_t, caddr_t, int, struct thread *); + void (*usb2_com_start_read) (struct usb2_com_softc *); + void (*usb2_com_stop_read) (struct usb2_com_softc *); + void (*usb2_com_start_write) (struct usb2_com_softc *); + void (*usb2_com_stop_write) (struct usb2_com_softc *); + void (*usb2_com_tty_name) (struct usb2_com_softc *, char *pbuf, uint16_t buflen, uint16_t local_subunit); +}; + +/* 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 */ + +struct usb2_com_super_softc { + struct usb2_config_td sc_config_td; +}; + +struct usb2_com_softc { + struct termios sc_termios_copy; + struct cv sc_cv; + const struct usb2_com_callback *sc_callback; + struct usb2_com_super_softc *sc_super; + struct tty *sc_tty; + struct mtx *sc_parent_mtx; + void *sc_parent; + uint32_t sc_unit; + uint32_t sc_local_unit; + uint16_t sc_portno; + uint8_t sc_flag; +#define UCOM_FLAG_RTS_IFLOW 0x01 /* use RTS input flow control */ +#define UCOM_FLAG_GONE 0x02 /* the device is gone */ +#define UCOM_FLAG_ATTACHED 0x04 /* set if attached */ +#define UCOM_FLAG_GP_DATA 0x08 /* set if get and put data is possible */ +#define UCOM_FLAG_WR_START 0x10 /* set if write start was issued */ +#define UCOM_FLAG_LL_READY 0x20 /* set if low layer is ready */ +#define UCOM_FLAG_HL_READY 0x40 /* set if high layer is ready */ + uint8_t sc_lsr; + uint8_t sc_msr; + uint8_t sc_mcr; + uint8_t sc_ttyfreed; /* set when TTY has been freed */ +}; + +int usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, uint32_t sub_units, void *parent, const struct usb2_com_callback *callback, struct mtx *p_mtx); +void usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, uint32_t sub_units); +void usb2_com_status_change(struct usb2_com_softc *); +uint8_t usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, uint32_t offset, uint32_t len, uint32_t *actlen); +void usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, uint32_t offset, uint32_t len); +uint8_t usb2_com_cfg_sleep(struct usb2_com_softc *sc, uint32_t timeout); +uint8_t usb2_com_cfg_is_gone(struct usb2_com_softc *sc); + +#endif /* _USB2_SERIAL_H_ */ diff --git a/sys/dev/usb2/serial/uvisor2.c b/sys/dev/usb2/serial/uvisor2.c new file mode 100644 index 000000000000..7280401c68af --- /dev/null +++ b/sys/dev/usb2/serial/uvisor2.c @@ -0,0 +1,675 @@ +/* $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 +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uvisor_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int uvisor_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor"); +SYSCTL_INT(_hw_usb2_uvisor, OID_AUTO, debug, CTLFLAG_RW, + &uvisor_debug, 0, "Debug level"); +#endif + +#define UVISOR_CONFIG_INDEX 0 +#define UVISOR_IFACE_INDEX 0 +#define UVISOR_N_TRANSFER 4 /* units */ +#define UVISOR_BUFSIZE 1024 /* bytes */ + +/* 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; + } __packed connections[UVISOR_MAX_CONN]; +} __packed; + +#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; + } __packed connections[UVISOR_MAX_CONN]; +} __packed; + +struct uvisor_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UVISOR_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_flag; +#define UVISOR_FLAG_PALM4 0x0001 +#define UVISOR_FLAG_VISOR 0x0002 +#define UVISOR_FLAG_PALM35 0x0004 +#define UVISOR_FLAG_SEND_NOTIFY 0x0008 +#define UVISOR_FLAG_WRITE_STALL 0x0010 +#define UVISOR_FLAG_READ_STALL 0x0020 + + uint8_t sc_iface_no; + uint8_t sc_iface_index; +}; + +/* prototypes */ + +static device_probe_t uvisor_probe; +static device_attach_t uvisor_attach; +static device_detach_t uvisor_detach; + +static usb2_callback_t uvisor_write_callback; +static usb2_callback_t uvisor_write_clear_stall_callback; +static usb2_callback_t uvisor_read_callback; +static usb2_callback_t uvisor_read_clear_stall_callback; + +static usb2_error_t uvisor_init(struct uvisor_softc *sc, struct usb2_device *udev, struct usb2_config *config); +static void uvisor_cfg_open(struct usb2_com_softc *ucom); +static void uvisor_cfg_close(struct usb2_com_softc *ucom); +static void uvisor_start_read(struct usb2_com_softc *ucom); +static void uvisor_stop_read(struct usb2_com_softc *ucom); +static void uvisor_start_write(struct usb2_com_softc *ucom); +static void uvisor_stop_write(struct usb2_com_softc *ucom); + +static const struct usb2_config uvisor_config[UVISOR_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UVISOR_BUFSIZE, /* bytes */ + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uvisor_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UVISOR_BUFSIZE, /* bytes */ + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uvisor_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uvisor_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uvisor_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback uvisor_callback = { + .usb2_com_cfg_open = &uvisor_cfg_open, + .usb2_com_cfg_close = &uvisor_cfg_close, + .usb2_com_start_read = &uvisor_start_read, + .usb2_com_stop_read = &uvisor_stop_read, + .usb2_com_start_write = &uvisor_start_write, + .usb2_com_stop_write = &uvisor_stop_write, +}; + +static device_method_t uvisor_methods[] = { + DEVMETHOD(device_probe, uvisor_probe), + DEVMETHOD(device_attach, uvisor_attach), + DEVMETHOD(device_detach, uvisor_detach), + {0, 0} +}; + +static devclass_t uvisor_devclass; + +static driver_t uvisor_driver = { + .name = "uvisor", + .methods = uvisor_methods, + .size = sizeof(struct uvisor_softc), +}; + +DRIVER_MODULE(uvisor, ushub, uvisor_driver, uvisor_devclass, NULL, 0); +MODULE_DEPEND(uvisor, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uvisor, usb2_core, 1, 1, 1); +MODULE_DEPEND(uvisor, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static const struct usb2_device_id uvisor_devs[] = { + {USB_VPI(USB_VENDOR_ACEECA, USB_PRODUCT_ACEECA_MEZ1000, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_GARMIN, USB_PRODUCT_GARMIN_IQUE_3600, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_FOSSIL, USB_PRODUCT_FOSSIL_WRISTPDA, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR, UVISOR_FLAG_VISOR)}, + {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M500, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M505, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M515, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_I705, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M125, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M130, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_I500, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40, 0)}, + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60, UVISOR_FLAG_PALM4)}, + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35, UVISOR_FLAG_PALM35)}, +/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25, UVISOR_FLAG_PALM4 )}, */ + {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37, UVISOR_FLAG_PALM4)}, +/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TH55, UVISOR_FLAG_PALM4 )}, See PR 80935 */ + {USB_VPI(USB_VENDOR_TAPWAVE, USB_PRODUCT_TAPWAVE_ZODIAC, UVISOR_FLAG_PALM4)}, +}; + +static int +uvisor_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UVISOR_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UVISOR_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uvisor_devs, sizeof(uvisor_devs), uaa)); +} + +static int +uvisor_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uvisor_softc *sc = device_get_softc(dev); + struct usb2_config uvisor_config_copy[UVISOR_N_TRANSFER]; + int error; + + DPRINTF("sc=%p\n", sc); + bcopy(uvisor_config, uvisor_config_copy, + sizeof(uvisor_config_copy)); + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + /* configure the device */ + + sc->sc_flag = USB_GET_DRIVER_INFO(uaa); + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = UVISOR_IFACE_INDEX; + + error = uvisor_init(sc, uaa->device, uvisor_config_copy); + + if (error) { + DPRINTF("init failed, error=%s\n", + usb2_errstr(error)); + goto detach; + } + error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index, + sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER, + sc, &Giant); + if (error) { + DPRINTF("could not allocate all pipes\n"); + goto detach; + } + /* clear stall at first run */ + sc->sc_flag |= (UVISOR_FLAG_WRITE_STALL | + UVISOR_FLAG_READ_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uvisor_callback, &Giant); + if (error) { + DPRINTF("usb2_com_attach failed\n"); + goto detach; + } + return (0); + +detach: + uvisor_detach(dev); + return (ENXIO); +} + +static int +uvisor_detach(device_t dev) +{ + struct uvisor_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER); + + return (0); +} + +static usb2_error_t +uvisor_init(struct uvisor_softc *sc, struct usb2_device *udev, struct usb2_config *config) +{ + usb2_error_t err = 0; + struct usb2_device_request req; + struct uvisor_connection_info coninfo; + struct uvisor_palm_connection_info pconinfo; + uint16_t actlen; + uWord wAvail; + uint8_t buffer[256]; + + if (sc->sc_flag & UVISOR_FLAG_VISOR) { + DPRINTF("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 = usb2_do_request_flags + (udev, &Giant, &req, &coninfo, USB_SHORT_XFER_OK, + &actlen, USB_DEFAULT_TIMEOUT); + + if (err) { + goto done; + } + } +#if USB_DEBUG + if (sc->sc_flag & UVISOR_FLAG_VISOR) { + uint16_t i, np; + const char *desc; + + np = UGETW(coninfo.num_ports); + if (np > UVISOR_MAX_CONN) { + np = UVISOR_MAX_CONN; + } + DPRINTF("Number of ports: %d\n", np); + + for (i = 0; i < np; ++i) { + switch (coninfo.connections[i].port_function_id) { + case UVISOR_FUNCTION_GENERIC: + desc = "Generic"; + break; + case UVISOR_FUNCTION_DEBUGGER: + desc = "Debugger"; + break; + case UVISOR_FUNCTION_HOTSYNC: + desc = "HotSync"; + break; + case UVISOR_FUNCTION_REMOTE_FILE_SYS: + desc = "Remote File System"; + break; + default: + desc = "unknown"; + break; + } + DPRINTF("Port %d is for %s\n", + coninfo.connections[i].port, desc); + } + } +#endif + + if (sc->sc_flag & UVISOR_FLAG_PALM4) { + uint8_t 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 = usb2_do_request_flags + (udev, &Giant, &req, &pconinfo, USB_SHORT_XFER_OK, + &actlen, USB_DEFAULT_TIMEOUT); + + if (err) { + goto done; + } + if (actlen < 12) { + DPRINTF("too little data\n"); + err = USB_ERR_INVAL; + goto done; + } + if (pconinfo.endpoint_numbers_different) { + port = pconinfo.connections[0].end_point_info; + config[0].endpoint = (port & 0xF); /* output */ + config[1].endpoint = (port >> 4); /* input */ + } else { + port = pconinfo.connections[0].port; + config[0].endpoint = (port & 0xF); /* output */ + config[1].endpoint = (port & 0xF); /* input */ + } +#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 = usb2_do_request(udev, &req, buffer); + if (err) { + goto done; + } +#endif + } + if (sc->sc_flag & UVISOR_FLAG_PALM35) { + /* get the config number */ + DPRINTF("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 = usb2_do_request(udev, &Giant, &req, buffer); + if (err) { + goto done; + } + /* get the interface number */ + DPRINTF("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 = usb2_do_request(udev, &Giant, &req, buffer); + if (err) { + goto done; + } + } + DPRINTF("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(wAvail)); + err = usb2_do_request(udev, &Giant, &req, &wAvail); + if (err) { + goto done; + } + DPRINTF("avail=%d\n", UGETW(wAvail)); + + DPRINTF("done\n"); +done: + return (err); +} + +static void +uvisor_cfg_open(struct usb2_com_softc *ucom) +{ + return; +} + +static void +uvisor_cfg_close(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + uint8_t buffer[UVISOR_CONNECTION_INFO_SIZE]; + struct usb2_device_request req; + usb2_error_t err; + + 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); + + err = usb2_do_request_flags + (sc->sc_udev, &Giant, &req, &buffer, 0, NULL, 1000); + + if (err) { + DPRINTFN(0, "close notification failed, error=%s\n", + usb2_errstr(err)); + } + return; +} + +static void +uvisor_start_read(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uvisor_stop_read(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uvisor_start_write(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uvisor_stop_write(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +uvisor_write_callback(struct usb2_xfer *xfer) +{ + struct uvisor_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UVISOR_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UVISOR_BUFSIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UVISOR_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uvisor_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uvisor_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UVISOR_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uvisor_read_callback(struct usb2_xfer *xfer) +{ + struct uvisor_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UVISOR_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UVISOR_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uvisor_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uvisor_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UVISOR_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} diff --git a/sys/dev/usb2/serial/uvscom2.c b/sys/dev/usb2/serial/uvscom2.c new file mode 100644 index 000000000000..189b26b97357 --- /dev/null +++ b/sys/dev/usb2/serial/uvscom2.c @@ -0,0 +1,827 @@ +/* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . + * 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. + * + */ + +/* + * 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 +#include +#include +#include +#include + +#define USB_DEBUG_VAR uvscom_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int uvscom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom"); +SYSCTL_INT(_hw_usb2_uvscom, OID_AUTO, debug, CTLFLAG_RW, + &uvscom_debug, 0, "Debug level"); +#endif + +#define UVSCOM_MODVER 1 /* module version */ + +#define UVSCOM_CONFIG_INDEX 0 +#define UVSCOM_IFACE_INDEX 0 + +/* 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 +#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) + +#define UVSCOM_BULK_BUF_SIZE 1024 /* bytes */ + +#define UVSCOM_N_TRANSFER 6 /* units */ + +struct uvscom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UVSCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_line; /* line control register */ + + uint8_t sc_flag; +#define UVSCOM_FLAG_WRITE_STALL 0x0001 +#define UVSCOM_FLAG_READ_STALL 0x0002 +#define UVSCOM_FLAG_INTR_STALL 0x0004 + uint8_t sc_iface_no; /* interface number */ + uint8_t sc_iface_index; /* interface index */ + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* uvscom status register */ + uint8_t sc_unit_status; /* unit status */ +}; + +/* prototypes */ + +static device_probe_t uvscom_probe; +static device_attach_t uvscom_attach; +static device_detach_t uvscom_detach; + +static usb2_callback_t uvscom_write_callback; +static usb2_callback_t uvscom_write_clear_stall_callback; +static usb2_callback_t uvscom_read_callback; +static usb2_callback_t uvscom_read_clear_stall_callback; +static usb2_callback_t uvscom_intr_callback; +static usb2_callback_t uvscom_intr_clear_stall_callback; + +static void uvscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff); +static void uvscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff); +static void uvscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff); +static int uvscom_pre_param(struct usb2_com_softc *ucom, struct termios *t); +static void uvscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t); +static int uvscom_pre_open(struct usb2_com_softc *ucom); +static void uvscom_cfg_open(struct usb2_com_softc *ucom); +static void uvscom_cfg_close(struct usb2_com_softc *ucom); +static void uvscom_start_read(struct usb2_com_softc *ucom); +static void uvscom_stop_read(struct usb2_com_softc *ucom); +static void uvscom_start_write(struct usb2_com_softc *ucom); +static void uvscom_stop_write(struct usb2_com_softc *ucom); +static void uvscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr); +static int uvscom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int fflag, struct thread *td); +static void uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value); +static uint16_t uvscom_cfg_read_status(struct uvscom_softc *sc); + +static const struct usb2_config uvscom_config[UVSCOM_N_TRANSFER] = { + + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UVSCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uvscom_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UVSCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uvscom_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uvscom_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uvscom_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [4] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &uvscom_intr_callback, + }, + + [5] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &uvscom_intr_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static const struct usb2_com_callback uvscom_callback = { + .usb2_com_cfg_get_status = &uvscom_cfg_get_status, + .usb2_com_cfg_set_dtr = &uvscom_cfg_set_dtr, + .usb2_com_cfg_set_rts = &uvscom_cfg_set_rts, + .usb2_com_cfg_set_break = &uvscom_cfg_set_break, + .usb2_com_cfg_param = &uvscom_cfg_param, + .usb2_com_cfg_open = &uvscom_cfg_open, + .usb2_com_cfg_close = &uvscom_cfg_close, + .usb2_com_pre_open = &uvscom_pre_open, + .usb2_com_pre_param = &uvscom_pre_param, + .usb2_com_ioctl = &uvscom_ioctl, + .usb2_com_start_read = &uvscom_start_read, + .usb2_com_stop_read = &uvscom_stop_read, + .usb2_com_start_write = &uvscom_start_write, + .usb2_com_stop_write = &uvscom_stop_write, +}; + +static const struct usb2_device_id uvscom_devs[] = { + /* SUNTAC U-Cable type A4 */ + {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, 0)}, + /* SUNTAC U-Cable type D2 */ + {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, 0)}, + /* SUNTAC Ir-Trinity */ + {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, 0)}, + /* SUNTAC U-Cable type P1 */ + {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, 0)}, + /* SUNTAC Slipper U */ + {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, 0)}, +}; + +static device_method_t uvscom_methods[] = { + DEVMETHOD(device_probe, uvscom_probe), + DEVMETHOD(device_attach, uvscom_attach), + DEVMETHOD(device_detach, uvscom_detach), + {0, 0} +}; + +static devclass_t uvscom_devclass; + +static driver_t uvscom_driver = { + .name = "uvscom", + .methods = uvscom_methods, + .size = sizeof(struct uvscom_softc), +}; + +DRIVER_MODULE(uvscom, ushub, uvscom_driver, uvscom_devclass, NULL, 0); +MODULE_DEPEND(uvscom, usb2_serial, 1, 1, 1); +MODULE_DEPEND(uvscom, usb2_core, 1, 1, 1); +MODULE_DEPEND(uvscom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); +MODULE_VERSION(uvscom, UVSCOM_MODVER); + +static int +uvscom_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != UVSCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UVSCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa)); +} + +static int +uvscom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uvscom_softc *sc = device_get_softc(dev); + int error; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + DPRINTF("sc=%p\n", sc); + + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = UVSCOM_IFACE_INDEX; + + error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index, + sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &Giant); + + if (error) { + DPRINTF("could not allocate all USB transfers!\n"); + goto detach; + } + sc->sc_line = UVSCOM_LINE_INIT; + + /* clear stall at first run */ + sc->sc_flag |= (UVSCOM_FLAG_WRITE_STALL | + UVSCOM_FLAG_READ_STALL); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uvscom_callback, &Giant); + if (error) { + goto detach; + } + /* start interrupt pipe */ + mtx_lock(sc->sc_xfer[4]->priv_mtx); + usb2_transfer_start(sc->sc_xfer[4]); + mtx_unlock(sc->sc_xfer[4]->priv_mtx); + + return (0); + +detach: + uvscom_detach(dev); + return (ENXIO); +} + +static int +uvscom_detach(device_t dev) +{ + struct uvscom_softc *sc = device_get_softc(dev); + + DPRINTF("sc=%p\n", sc); + + /* stop interrupt pipe */ + + if (sc->sc_xfer[4]) { + usb2_transfer_stop(sc->sc_xfer[4]); + } + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER); + + return (0); +} + +static void +uvscom_write_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: + if (sc->sc_flag & UVSCOM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + return; + } + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UVSCOM_BULK_BUF_SIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UVSCOM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + return; + + } +} + +static void +uvscom_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UVSCOM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uvscom_read_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: + if (sc->sc_flag & UVSCOM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UVSCOM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +uvscom_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UVSCOM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uvscom_intr_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen >= 2) { + + usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf)); + + sc->sc_lsr = 0; + sc->sc_msr = 0; + sc->sc_unit_status = buf[1]; + + if (buf[0] & UVSCOM_TXRDY) { + sc->sc_lsr |= ULSR_TXRDY; + } + if (buf[0] & UVSCOM_RXRDY) { + sc->sc_lsr |= ULSR_RXRDY; + } + if (buf[1] & UVSCOM_CTS) { + sc->sc_msr |= SER_CTS; + } + if (buf[1] & UVSCOM_DSR) { + sc->sc_msr |= SER_DSR; + } + if (buf[1] & UVSCOM_DCD) { + sc->sc_msr |= SER_DCD; + } + /* + * the UCOM layer will ignore this call if the TTY + * device is closed! + */ + usb2_com_status_change(&sc->sc_ucom); + } + case USB_ST_SETUP: + if (sc->sc_flag & UVSCOM_FLAG_INTR_STALL) { + usb2_transfer_start(sc->sc_xfer[5]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + sc->sc_flag |= UVSCOM_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[5]); + } + return; + + } +} + +static void +uvscom_intr_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct uvscom_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[4]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flag &= ~UVSCOM_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +uvscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line |= UVSCOM_DTR; + else + sc->sc_line &= ~UVSCOM_DTR; + + uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); + return; +} + +static void +uvscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line |= UVSCOM_RTS; + else + sc->sc_line &= ~UVSCOM_RTS; + + uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); + return; +} + +static void +uvscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line |= UVSCOM_BREAK; + else + sc->sc_line &= ~UVSCOM_BREAK; + + uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); + return; +} + +static int +uvscom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + switch (t->c_ospeed) { + case B150: + case B300: + case B600: + case B1200: + case B2400: + case B4800: + case B9600: + case B19200: + case B38400: + case B57600: + case B115200: + default: + return (EINVAL); + } + return (0); +} + +static void +uvscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uvscom_softc *sc = ucom->sc_parent; + uint16_t value; + + DPRINTF("\n"); + + switch (t->c_ospeed) { + case B150: + value = UVSCOM_SPEED_150BPS; + break; + case B300: + value = UVSCOM_SPEED_300BPS; + break; + case B600: + value = UVSCOM_SPEED_600BPS; + break; + case B1200: + value = UVSCOM_SPEED_1200BPS; + break; + case B2400: + value = UVSCOM_SPEED_2400BPS; + break; + case B4800: + value = UVSCOM_SPEED_4800BPS; + break; + case B9600: + value = UVSCOM_SPEED_9600BPS; + break; + case B19200: + value = UVSCOM_SPEED_19200BPS; + break; + case B38400: + value = UVSCOM_SPEED_38400BPS; + break; + case B57600: + value = UVSCOM_SPEED_57600BPS; + break; + case B115200: + value = UVSCOM_SPEED_115200BPS; + break; + default: + return; + } + + uvscom_cfg_write(sc, UVSCOM_SET_SPEED, value); + + value = 0; + + if (t->c_cflag & CSTOPB) { + value |= UVSCOM_STOP_BIT_2; + } + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) { + value |= UVSCOM_PARITY_ODD; + } else { + value |= UVSCOM_PARITY_EVEN; + } + } else { + value |= UVSCOM_PARITY_NONE; + } + + switch (t->c_cflag & CSIZE) { + case CS5: + value |= UVSCOM_DATA_BIT_5; + break; + case CS6: + value |= UVSCOM_DATA_BIT_6; + break; + case CS7: + value |= UVSCOM_DATA_BIT_7; + break; + default: + case CS8: + value |= UVSCOM_DATA_BIT_8; + break; + } + + uvscom_cfg_write(sc, UVSCOM_SET_PARAM, value); + return; +} + +static int +uvscom_pre_open(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("sc = %p\n", sc); + + /* check if PC card was inserted */ + + if (sc->sc_unit_status & UVSCOM_NOCARD) { + DPRINTF("no PC card!\n"); + return (ENXIO); + } + return (0); +} + +static void +uvscom_cfg_open(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("sc = %p\n", sc); + + uvscom_cfg_read_status(sc); + + return; +} + +static void +uvscom_cfg_close(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + DPRINTF("sc=%p\n", sc); + + uvscom_cfg_write(sc, UVSCOM_SHUTDOWN, 0); + + return; +} + +static void +uvscom_start_read(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[1]); + return; +} + +static void +uvscom_stop_read(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[3]); + usb2_transfer_stop(sc->sc_xfer[1]); + return; +} + +static void +uvscom_start_write(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +uvscom_stop_write(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[0]); + return; +} + +static void +uvscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; + return; +} + +static int +uvscom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int fflag, + struct thread *td) +{ + return (ENOTTY); +} + +static void +uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + return; + } + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = index; + USETW(req.wValue, value); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usb2_do_request_flags(sc->sc_udev, &Giant, &req, + NULL, 0, NULL, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + return; +} + +static uint16_t +uvscom_cfg_read_status(struct uvscom_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t data[2]; + + if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { + return (0); + } + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UVSCOM_READ_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 2); + + err = usb2_do_request_flags(sc->sc_udev, &Giant, &req, + data, 0, NULL, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + data[0] = 0; + data[1] = 0; + } + return (data[0] | (data[1] << 8)); +} diff --git a/sys/dev/usb2/sound/uaudio2.c b/sys/dev/usb2/sound/uaudio2.c new file mode 100644 index 000000000000..d6e3878f8de4 --- /dev/null +++ b/sys/dev/usb2/sound/uaudio2.c @@ -0,0 +1,3786 @@ +/* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */ +/* $FreeBSD$ */ + +/*- + * 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. + */ + +/* + * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf + * http://www.usb.org/developers/devclass_docs/frmts10.pdf + * http://www.usb.org/developers/devclass_docs/termt10.pdf + */ + +/* + * Also merged: + * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $ + * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $ + * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $ + * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $ + */ + +#include +#include +#include +#include + +#define USB_DEBUG_VAR uaudio_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include /* for bootverbose */ + +#include +#include +#include "feeder_if.h" + +#if USB_DEBUG +static int uaudio_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, debug, CTLFLAG_RW, + &uaudio_debug, 0, "uaudio debug level"); +#endif + +static uint32_t uaudio_default_rate = 96000; +static uint8_t uaudio_default_bits = 32; +static uint8_t uaudio_default_channels = 2; + +#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ +#define UAUDIO_NFRAMES 25 /* ms of sound in each request */ +#define UAUDIO_RECURSE_LIMIT 24 /* rounds */ +#define UAUDIO_DEFAULT_BUFSZ ((2 * 96000 * 4 * 2) / (1000 / UAUDIO_NCHANBUFS)) /* bytes */ + +#define MAKE_WORD(h,l) (((h) << 8) | (l)) +#define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) + +struct uaudio_mixer_node { + int32_t minval; + int32_t maxval; +#define MIX_MAX_CHAN 8 + int32_t wValue[MIX_MAX_CHAN]; /* using nchan */ + uint32_t delta; + uint32_t mul; + uint32_t ctl; + + uint16_t wData[MIX_MAX_CHAN]; /* using nchan */ + uint16_t wIndex; + + uint8_t update[(MIX_MAX_CHAN + 7) / 8]; + uint8_t nchan; + uint8_t type; +#define MIX_ON_OFF 1 +#define MIX_SIGNED_16 2 +#define MIX_UNSIGNED_16 3 +#define MIX_SIGNED_8 4 +#define MIX_SELECTOR 5 +#define MIX_UNKNOWN 6 +#define MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \ + ((n) == MIX_UNSIGNED_16)) ? 2 : 1) +#define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) + +#define MAX_SELECTOR_INPUT_PIN 256 + uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; + uint8_t class; + + struct uaudio_mixer_node *next; +}; + +struct uaudio_chan { + struct pcmchan_caps pcm_cap; /* capabilities */ + + struct snd_dbuf *pcm_buf; + const struct usb2_config *usb2_cfg; + struct mtx *pcm_mtx; /* lock protecting this structure */ + struct uaudio_softc *priv_sc; + struct pcm_channel *pcm_ch; + struct usb2_xfer *xfer[UAUDIO_NCHANBUFS]; + const struct usb2_audio_streaming_interface_descriptor *p_asid; + const struct usb2_audio_streaming_type1_descriptor *p_asf1d; + const struct usb2_audio_streaming_endpoint_descriptor *p_sed; + const usb2_endpoint_descriptor_audio_t *p_ed1; + const usb2_endpoint_descriptor_audio_t *p_ed2; + const struct uaudio_format *p_fmt; + + uint8_t *buf; /* pointer to buffer */ + uint8_t *start; /* upper layer buffer start */ + uint8_t *end; /* upper layer buffer end */ + uint8_t *cur; /* current position in upper layer + * buffer */ + + uint32_t block_size; + uint32_t sample_rate; + uint32_t format; + uint32_t pcm_format[2]; + + uint16_t bytes_per_frame; + + uint8_t valid; + uint8_t iface_index; + uint8_t iface_alt_index; +}; + +#define UMIDI_N_TRANSFER 4 /* units */ +#define UMIDI_CABLES_MAX 16 /* units */ +#define UMIDI_BULK_SIZE 1024 /* bytes */ + +struct umidi_sub_chan { + struct usb2_fifo_sc fifo; + uint8_t *temp_cmd; + uint8_t temp_0[4]; + uint8_t temp_1[4]; + uint8_t state; +#define UMIDI_ST_UNKNOWN 0 /* scan for command */ +#define UMIDI_ST_1PARAM 1 +#define UMIDI_ST_2PARAM_1 2 +#define UMIDI_ST_2PARAM_2 3 +#define UMIDI_ST_SYSEX_0 4 +#define UMIDI_ST_SYSEX_1 5 +#define UMIDI_ST_SYSEX_2 6 + + uint8_t read_open:1; + uint8_t write_open:1; + uint8_t unused:6; +}; + +struct umidi_chan { + + struct umidi_sub_chan sub[UMIDI_CABLES_MAX]; + struct mtx mtx; + + struct usb2_xfer *xfer[UMIDI_N_TRANSFER]; + + uint8_t iface_index; + uint8_t iface_alt_index; + + uint8_t flags; +#define UMIDI_FLAG_READ_STALL 0x01 +#define UMIDI_FLAG_WRITE_STALL 0x02 + + uint8_t read_open_refcount; + uint8_t write_open_refcount; + + uint8_t curr_cable; + uint8_t max_cable; + uint8_t valid; +}; + +struct uaudio_softc { + struct sbuf sc_sndstat; + struct sndcard_func sc_sndcard_func; + struct uaudio_chan sc_rec_chan; + struct uaudio_chan sc_play_chan; + struct umidi_chan sc_midi_chan; + + struct usb2_device *sc_udev; + struct usb2_xfer *sc_mixer_xfer[1]; + struct uaudio_mixer_node *sc_mixer_root; + struct uaudio_mixer_node *sc_mixer_curr; + + uint32_t sc_mix_info; + uint32_t sc_recsrc_info; + + uint16_t sc_audio_rev; + uint16_t sc_mixer_count; + + uint8_t sc_sndstat_valid; + uint8_t sc_mixer_iface_index; + uint8_t sc_mixer_iface_no; + uint8_t sc_mixer_chan; + uint8_t sc_pcm_registered:1; + uint8_t sc_mixer_init:1; + uint8_t sc_uq_audio_swap_lr:1; + uint8_t sc_uq_au_inp_async:1; + uint8_t sc_uq_au_no_xu:1; + uint8_t sc_uq_bad_adc:1; +}; + +struct uaudio_search_result { + uint8_t bit_input[(256 + 7) / 8]; + uint8_t bit_output[(256 + 7) / 8]; + uint8_t bit_visited[(256 + 7) / 8]; + uint8_t recurse_level; + uint8_t id_max; +}; + +struct uaudio_terminal_node { + union { + const struct usb2_descriptor *desc; + const struct usb2_audio_input_terminal *it; + const struct usb2_audio_output_terminal *ot; + const struct usb2_audio_mixer_unit_0 *mu; + const struct usb2_audio_selector_unit *su; + const struct usb2_audio_feature_unit *fu; + const struct usb2_audio_processing_unit_0 *pu; + const struct usb2_audio_extension_unit_0 *eu; + } u; + struct uaudio_search_result usr; + struct uaudio_terminal_node *root; +}; + +struct uaudio_format { + uint16_t wFormat; + uint8_t bPrecision; + uint32_t freebsd_fmt; + const char *description; +}; + +static const struct uaudio_format uaudio_formats[] = { + + {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, + {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, + {UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, + {UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, + + {UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, + {UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, + {UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, + {UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, + + {UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, + {UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, + + {0, 0, 0, NULL} +}; + +#define UAC_OUTPUT 0 +#define UAC_INPUT 1 +#define UAC_EQUAL 2 +#define UAC_RECORD 3 +#define UAC_NCLASSES 4 + +#if USB_DEBUG +static const char *uac_names[] = { + "outputs", "inputs", "equalization", "record" +}; + +#endif + +/* prototypes */ + +static device_probe_t uaudio_probe; +static device_attach_t uaudio_attach; +static device_detach_t uaudio_detach; + +static usb2_callback_t uaudio_chan_play_callback; +static usb2_callback_t uaudio_chan_record_callback; +static usb2_callback_t uaudio_mixer_write_cfg_callback; +static usb2_callback_t umidi_read_clear_stall_callback; +static usb2_callback_t umidi_bulk_read_callback; +static usb2_callback_t umidi_write_clear_stall_callback; +static usb2_callback_t umidi_bulk_write_callback; + +#if USB_DEBUG +static void uaudio_chan_dump_ep_desc(const usb2_endpoint_descriptor_audio_t *ed); + +#endif + +static void uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb2_device *udev, uint32_t rate, uint16_t fps, uint8_t channels, uint8_t bit_resolution); +static void uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb2_device *udev); +static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc); +static void uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc); +static void uaudio_mixer_add_input(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_output(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_mixer(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_selector(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static uint32_t uaudio_mixer_feature_get_bmaControls(const struct usb2_audio_feature_unit *d, uint8_t index); +static void uaudio_mixer_add_feature(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_processing(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static void uaudio_mixer_add_extension(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id); +static const void *uaudio_mixer_verify_desc(const void *arg, uint32_t len); + +#if USB_DEBUG +static void uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot); + +#endif + +static struct usb2_audio_cluster uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot); + +#if USB_DEBUG +static const char *uaudio_mixer_get_terminal_name(uint16_t terminal_type); + +#endif + +static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, struct uaudio_mixer_node *mix); +static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot, struct uaudio_mixer_node *mix); +static const struct uaudio_terminal_node *uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t index); +static const struct uaudio_terminal_node *uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t index); +static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, const uint8_t *p_id, uint8_t n_id, struct uaudio_search_result *info); +static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id, uint8_t n_id, struct uaudio_search_result *info); +static void uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb2_device *udev, void *desc); +static uint16_t uaudio_mixer_get(struct usb2_device *udev, uint8_t what, struct uaudio_mixer_node *mc); +static usb2_error_t uaudio_set_speed(struct usb2_device *udev, uint8_t endpt, uint32_t speed); +static int uaudio_mixer_signext(uint8_t type, int val); +static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val); + +static void uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc, uint8_t chan, int32_t val); +static void uaudio_mixer_init(struct uaudio_softc *sc); +static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b); +static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb2_fifo *fifo); +static void umidi_start_read(struct usb2_fifo *fifo); +static void umidi_stop_read(struct usb2_fifo *fifo); +static void umidi_start_write(struct usb2_fifo *fifo); +static void umidi_stop_write(struct usb2_fifo *fifo); +static int umidi_open(struct usb2_fifo *fifo, int fflags, struct thread *td); +static int umidi_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, int fflags, struct thread *td); +static void umidi_close(struct usb2_fifo *fifo, int fflags, struct thread *td); +static void umidi_init(device_t dev); +static int32_t umidi_probe(device_t dev); +static int32_t umidi_detach(device_t dev); + +static const struct usb2_config + uaudio_cfg_record_full_speed[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_record_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_record_callback, + }, +}; + +static const struct usb2_config + uaudio_cfg_record_high_speed[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_record_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_record_callback, + }, +}; + +static const struct usb2_config + uaudio_cfg_play_full_speed[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_play_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_NFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_play_callback, + }, +}; + +static const struct usb2_config + uaudio_cfg_play_high_speed[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_play_callback, + }, + + [1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_play_callback, + }, +}; + +static const struct usb2_config + uaudio_mixer_config[1] = { + [0] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + 4), + .mh.callback = &uaudio_mixer_write_cfg_callback, + .mh.timeout = 1000, /* 1 second */ + }, +}; + +static const +uint8_t umidi_cmd_to_len[16] = { + [0x0] = 0, /* reserved */ + [0x1] = 0, /* reserved */ + [0x2] = 2, /* bytes */ + [0x3] = 3, /* bytes */ + [0x4] = 3, /* bytes */ + [0x5] = 1, /* bytes */ + [0x6] = 2, /* bytes */ + [0x7] = 3, /* bytes */ + [0x8] = 3, /* bytes */ + [0x9] = 3, /* bytes */ + [0xA] = 3, /* bytes */ + [0xB] = 3, /* bytes */ + [0xC] = 2, /* bytes */ + [0xD] = 2, /* bytes */ + [0xE] = 3, /* bytes */ + [0xF] = 1, /* bytes */ +}; + +static const struct usb2_config + umidi_config[UMIDI_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMIDI_BULK_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umidi_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMIDI_BULK_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umidi_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umidi_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umidi_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t uaudio_devclass; + +static device_method_t uaudio_methods[] = { + DEVMETHOD(device_probe, uaudio_probe), + DEVMETHOD(device_attach, uaudio_attach), + DEVMETHOD(device_detach, uaudio_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(bus_print_child, bus_generic_print_child), + {0, 0} +}; + +static driver_t uaudio_driver = { + .name = "uaudio", + .methods = uaudio_methods, + .size = sizeof(struct uaudio_softc), +}; + +static int +uaudio_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + /* trigger on the control interface */ + + if ((uaa->info.bInterfaceClass == UICLASS_AUDIO) && + (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL)) { + if (usb2_test_quirk(uaa, UQ_BAD_AUDIO)) + return (ENXIO); + else + return (0); + } + return (ENXIO); +} + +static int +uaudio_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uaudio_softc *sc = device_get_softc(dev); + struct usb2_interface_descriptor *id; + device_t child; + + if (sc == NULL) { + return (ENOMEM); + } + sc->sc_play_chan.priv_sc = sc; + sc->sc_rec_chan.priv_sc = sc; + sc->sc_udev = uaa->device; + + if (usb2_test_quirk(uaa, UQ_AUDIO_SWAP_LR)) + sc->sc_uq_audio_swap_lr = 1; + + if (usb2_test_quirk(uaa, UQ_AU_INP_ASYNC)) + sc->sc_uq_au_inp_async = 1; + + if (usb2_test_quirk(uaa, UQ_AU_NO_XU)) + sc->sc_uq_au_no_xu = 1; + + if (usb2_test_quirk(uaa, UQ_BAD_ADC)) + sc->sc_uq_bad_adc = 1; + + umidi_init(dev); + + device_set_usb2_desc(dev); + + id = usb2_get_interface_descriptor(uaa->iface); + + uaudio_chan_fill_info(sc, uaa->device); + + uaudio_mixer_fill_info(sc, uaa->device, id); + + sc->sc_mixer_iface_index = uaa->info.bIfaceIndex; + sc->sc_mixer_iface_no = uaa->info.bIfaceNum; + + DPRINTF("audio rev %d.%02x\n", + sc->sc_audio_rev >> 8, + sc->sc_audio_rev & 0xff); + + DPRINTF("%d mixer controls\n", + sc->sc_mixer_count); + + if (sc->sc_play_chan.valid) { + device_printf(dev, "Play: %d Hz, %d ch, %s format\n", + sc->sc_play_chan.sample_rate, + sc->sc_play_chan.p_asf1d->bNrChannels, + sc->sc_play_chan.p_fmt->description); + } else { + device_printf(dev, "No playback!\n"); + } + + if (sc->sc_rec_chan.valid) { + device_printf(dev, "Record: %d Hz, %d ch, %s format\n", + sc->sc_rec_chan.sample_rate, + sc->sc_rec_chan.p_asf1d->bNrChannels, + sc->sc_rec_chan.p_fmt->description); + } else { + device_printf(dev, "No recording!\n"); + } + + if (sc->sc_midi_chan.valid) { + + if (umidi_probe(dev)) { + goto detach; + } + device_printf(dev, "MIDI sequencer\n"); + } else { + device_printf(dev, "No midi sequencer\n"); + } + + DPRINTF("doing child attach\n"); + + /* attach the children */ + + sc->sc_sndcard_func.func = SCF_PCM; + + child = device_add_child(dev, "pcm", -1); + + if (child == NULL) { + DPRINTF("out of memory\n"); + goto detach; + } + device_set_ivars(child, &sc->sc_sndcard_func); + + if (bus_generic_attach(dev)) { + DPRINTF("child attach failed\n"); + goto detach; + } + return (0); /* success */ + +detach: + uaudio_detach(dev); + return (ENXIO); +} + +static void +uaudio_pcm_setflags(device_t dev, uint32_t flags) +{ + pcm_setflags(dev, pcm_getflags(dev) | flags); +} + +int +uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class) +{ + struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); + char status[SND_STATUSLEN]; + + if (bootverbose) { + device_printf(dev, "using a default buffer " + "size of %u bytes\n", UAUDIO_DEFAULT_BUFSZ); + } + uaudio_mixer_init(sc); + + if (sc->sc_uq_audio_swap_lr) { + DPRINTF("hardware has swapped left and right\n"); + uaudio_pcm_setflags(dev, SD_F_PSWAPLR); + } + if (!(sc->sc_mix_info & SOUND_MASK_PCM)) { + + DPRINTF("emulating master volume\n"); + + /* + * Emulate missing pcm mixer controller + * through FEEDER_VOLUME + */ + uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL); + } + if (mixer_init(dev, mixer_class, sc)) { + goto detach; + } + sc->sc_mixer_init = 1; + + snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio)); + + if (pcm_register(dev, sc, + sc->sc_play_chan.valid ? 1 : 0, + sc->sc_rec_chan.valid ? 1 : 0)) { + goto detach; + } + sc->sc_pcm_registered = 1; + + if (sc->sc_play_chan.valid) { + pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc); + } + if (sc->sc_rec_chan.valid) { + pcm_addchan(dev, PCMDIR_REC, chan_class, sc); + } + pcm_setstatus(dev, status); + + return (0); /* success */ + +detach: + uaudio_detach_sub(dev); + return (ENXIO); +} + +int +uaudio_detach_sub(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); + int error = 0; + +repeat: + if (sc->sc_pcm_registered) { + error = pcm_unregister(dev); + } else { + if (sc->sc_mixer_init) { + error = mixer_uninit(dev); + } + } + + if (error) { + device_printf(dev, "Waiting for sound application to exit!\n"); + mtx_lock(&Giant); + usb2_pause_mtx(&Giant, 2000); + mtx_unlock(&Giant); + goto repeat; /* try again */ + } + return (0); /* success */ +} + +static int +uaudio_detach(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + + if (bus_generic_detach(dev)) { + DPRINTF("detach failed!\n"); + } + sbuf_delete(&sc->sc_sndstat); + sc->sc_sndstat_valid = 0; + + umidi_detach(dev); + + return (0); +} + +/*========================================================================* + * AS - Audio Stream - routines + *========================================================================*/ + +#if USB_DEBUG +static void +uaudio_chan_dump_ep_desc(const usb2_endpoint_descriptor_audio_t *ed) +{ + if (ed) { + DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" + "bEndpointAddress=%d bmAttributes=0x%x \n" + "wMaxPacketSize=%d bInterval=%d \n" + "bRefresh=%d bSynchAddress=%d\n", + ed, ed->bLength, ed->bDescriptorType, + ed->bEndpointAddress, ed->bmAttributes, + UGETW(ed->wMaxPacketSize), ed->bInterval, + ed->bRefresh, ed->bSynchAddress); + } + return; +} + +#endif + +static void +uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb2_device *udev, + uint32_t rate, uint16_t fps, uint8_t channels, + uint8_t bit_resolution) +{ + struct usb2_descriptor *desc = NULL; + const struct usb2_audio_streaming_interface_descriptor *asid = NULL; + const struct usb2_audio_streaming_type1_descriptor *asf1d = NULL; + const struct usb2_audio_streaming_endpoint_descriptor *sed = NULL; + const usb2_endpoint_descriptor_audio_t *ed1 = NULL; + const usb2_endpoint_descriptor_audio_t *ed2 = NULL; + struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev); + struct usb2_interface_descriptor *id; + const struct uaudio_format *p_fmt; + struct uaudio_chan *chan; + uint16_t curidx = 0xFFFF; + uint16_t lastidx = 0xFFFF; + uint16_t alt_index = 0; + uint16_t wFormat; + uint8_t ep_dir; + uint8_t ep_type; + uint8_t ep_sync; + uint8_t bChannels; + uint8_t bBitResolution; + uint8_t x; + uint8_t audio_if = 0; + uint8_t sample_size; + + while ((desc = usb2_desc_foreach(cd, desc))) { + + if ((desc->bDescriptorType == UDESC_INTERFACE) && + (desc->bLength >= sizeof(*id))) { + + id = (void *)desc; + + if (id->bInterfaceNumber != lastidx) { + lastidx = id->bInterfaceNumber; + curidx++; + alt_index = 0; + + } else { + alt_index++; + } + + if ((id->bInterfaceClass == UICLASS_AUDIO) && + (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) { + audio_if = 1; + } else { + audio_if = 0; + } + + if ((id->bInterfaceClass == UICLASS_AUDIO) && + (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) { + + /* + * XXX could allow multiple MIDI interfaces + * XXX + */ + + if ((sc->sc_midi_chan.valid == 0) && + usb2_get_iface(udev, curidx)) { + sc->sc_midi_chan.iface_index = curidx; + sc->sc_midi_chan.iface_alt_index = alt_index; + sc->sc_midi_chan.valid = 1; + } + } + asid = NULL; + asf1d = NULL; + ed1 = NULL; + ed2 = NULL; + sed = NULL; + } + if ((desc->bDescriptorType == UDESC_CS_INTERFACE) && + (desc->bDescriptorSubtype == AS_GENERAL) && + (desc->bLength >= sizeof(*asid))) { + if (asid == NULL) { + asid = (void *)desc; + } + } + if ((desc->bDescriptorType == UDESC_CS_INTERFACE) && + (desc->bDescriptorSubtype == FORMAT_TYPE) && + (desc->bLength >= sizeof(*asf1d))) { + if (asf1d == NULL) { + asf1d = (void *)desc; + if (asf1d->bFormatType != FORMAT_TYPE_I) { + DPRINTFN(11, "ignored bFormatType = %d\n", + asf1d->bFormatType); + asf1d = NULL; + continue; + } + if (asf1d->bLength < (sizeof(*asf1d) + + (asf1d->bSamFreqType == 0) ? 6 : + (asf1d->bSamFreqType * 3))) { + DPRINTFN(11, "'asf1d' descriptor is too short\n"); + asf1d = NULL; + continue; + } + } + } + if ((desc->bDescriptorType == UDESC_ENDPOINT) && + (desc->bLength >= sizeof(*ed1))) { + if (ed1 == NULL) { + ed1 = (void *)desc; + if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) { + ed1 = NULL; + } + } else { + if (ed2 == NULL) { + ed2 = (void *)desc; + if (UE_GET_XFERTYPE(ed2->bmAttributes) != UE_ISOCHRONOUS) { + ed2 = NULL; + continue; + } + if (ed2->bSynchAddress != 0) { + DPRINTFN(11, "invalid endpoint: bSynchAddress != 0\n"); + ed2 = NULL; + continue; + } + if (ed2->bEndpointAddress != ed1->bSynchAddress) { + DPRINTFN(11, "invalid endpoint addresses: " + "ep[0]->bSynchAddress=0x%x " + "ep[1]->bEndpointAddress=0x%x\n", + ed1->bSynchAddress, + ed2->bEndpointAddress); + ed2 = NULL; + continue; + } + } + } + } + if ((desc->bDescriptorType == UDESC_CS_ENDPOINT) && + (desc->bDescriptorSubtype == AS_GENERAL) && + (desc->bLength >= sizeof(*sed))) { + if (sed == NULL) { + sed = (void *)desc; + } + } + if (audio_if && asid && asf1d && ed1 && sed) { + + ep_dir = UE_GET_DIR(ed1->bEndpointAddress); + ep_type = UE_GET_ISO_TYPE(ed1->bmAttributes); + ep_sync = 0; + + if ((sc->sc_uq_au_inp_async) && + (ep_dir == UE_DIR_IN) && (ep_type == UE_ISO_ADAPT)) { + ep_type = UE_ISO_ASYNC; + } + if ((ep_dir == UE_DIR_IN) && (ep_type == UE_ISO_ADAPT)) { + ep_sync = 1; + } + if ((ep_dir != UE_DIR_IN) && (ep_type == UE_ISO_ASYNC)) { + ep_sync = 1; + } + /* Ignore sync endpoint information until further. */ +#if 0 + if (ep_sync && (!ed2)) { + continue; + } + /* + * we can't handle endpoints that need a sync pipe + * yet + */ + + if (ep_sync) { + DPRINTF("skipped sync interface\n"); + audio_if = 0; + continue; + } +#endif + + wFormat = UGETW(asid->wFormatTag); + bChannels = asf1d->bNrChannels; + bBitResolution = asf1d->bBitResolution; + + if (asf1d->bSamFreqType == 0) { + DPRINTFN(16, "Sample rate: %d-%dHz\n", + UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); + + if ((rate >= UA_SAMP_LO(asf1d)) && + (rate <= UA_SAMP_HI(asf1d))) { + goto found_rate; + } + } else { + + for (x = 0; x < asf1d->bSamFreqType; x++) { + DPRINTFN(16, "Sample rate = %dHz\n", + UA_GETSAMP(asf1d, x)); + + if (rate == UA_GETSAMP(asf1d, x)) { + goto found_rate; + } + } + } + + audio_if = 0; + continue; + + found_rate: + + for (p_fmt = uaudio_formats; + p_fmt->wFormat; + p_fmt++) { + if ((p_fmt->wFormat == wFormat) && + (p_fmt->bPrecision == bBitResolution)) { + goto found_format; + } + } + + audio_if = 0; + continue; + + found_format: + + if ((bChannels == channels) && + (bBitResolution == bit_resolution)) { + + chan = (ep_dir == UE_DIR_IN) ? + &sc->sc_rec_chan : + &sc->sc_play_chan; + + if ((chan->valid == 0) && usb2_get_iface(udev, curidx)) { + + chan->valid = 1; +#if USB_DEBUG + uaudio_chan_dump_ep_desc(ed1); + uaudio_chan_dump_ep_desc(ed2); + + if (sed->bmAttributes & UA_SED_FREQ_CONTROL) { + DPRINTFN(2, "FREQ_CONTROL\n"); + } + if (sed->bmAttributes & UA_SED_PITCH_CONTROL) { + DPRINTFN(2, "PITCH_CONTROL\n"); + } +#endif + DPRINTF("Sample rate = %dHz, channels = %d, " + "bits = %d, format = %s\n", rate, channels, + bit_resolution, p_fmt->description); + + chan->sample_rate = rate; + chan->p_asid = asid; + chan->p_asf1d = asf1d; + chan->p_ed1 = ed1; + chan->p_ed2 = ed2; + chan->p_fmt = p_fmt; + chan->p_sed = sed; + chan->iface_index = curidx; + chan->iface_alt_index = alt_index; + + chan->usb2_cfg = + (ep_dir == UE_DIR_IN) ? + ((fps == 1000) ? + uaudio_cfg_record_full_speed : + uaudio_cfg_record_high_speed) : + ((fps == 1000) ? + uaudio_cfg_play_full_speed : + uaudio_cfg_play_high_speed); + + + sample_size = ((chan->p_asf1d->bNrChannels * + chan->p_asf1d->bBitResolution) / 8); + + chan->bytes_per_frame = ((rate / fps) * sample_size); + + if (sc->sc_sndstat_valid) { + sbuf_printf(&sc->sc_sndstat, "\n\t" + "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz", + curidx, alt_index, + (ep_dir == UE_DIR_IN) ? "input" : "output", + asf1d->bNrChannels, asf1d->bBitResolution, + asf1d->bSubFrameSize * 8, + p_fmt->description, rate); + } + } + } + audio_if = 0; + continue; + } + } + return; +} + +static void +uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb2_device *udev) +{ + uint32_t rate = uaudio_default_rate; + uint32_t z; + uint16_t fps = (usb2_get_speed(udev) == USB_SPEED_HIGH) ? 8000 : 1000; + uint8_t bits = uaudio_default_bits; + uint8_t y; + uint8_t channels = uaudio_default_channels; + uint8_t x; + + bits -= (bits % 8); + rate -= (rate % fps); + + if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) { + sc->sc_sndstat_valid = 1; + } + /* try to search for a valid config */ + + for (x = channels; x; x--) { + for (y = bits; y; y -= 8) { + for (z = rate; z; z -= fps) { + uaudio_chan_fill_info_sub(sc, udev, z, fps, x, y); + + if (sc->sc_rec_chan.valid && + sc->sc_play_chan.valid) { + goto done; + } + } + } + } + +done: + if (sc->sc_sndstat_valid) { + sbuf_finish(&sc->sc_sndstat); + } + return; +} + +static void +uaudio_chan_play_callback(struct usb2_xfer *xfer) +{ + struct uaudio_chan *ch = xfer->priv_sc; + uint32_t *p_len = xfer->frlengths; + uint32_t total = (sndbuf_getblkcnt(ch->pcm_buf) * + sndbuf_getblksz(ch->pcm_buf)) / 2; + uint32_t blockcount; + uint32_t n; + uint32_t offset; + + /* allow dynamic sizing of play buffer */ + blockcount = total / ch->bytes_per_frame; + + /* align to 8 units */ + blockcount &= ~7; + + /* range check - min */ + if (blockcount == 0) { + blockcount = 8; + } + /* range check - max */ + if (blockcount > xfer->max_frame_count) { + blockcount = xfer->max_frame_count; + } + /* compute the total length */ + total = blockcount * ch->bytes_per_frame; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + if (xfer->actlen < xfer->sumlen) { + DPRINTF("short transfer, " + "%d of %d bytes\n", xfer->actlen, total); + } + chn_intr(ch->pcm_ch); + + case USB_ST_SETUP: + if (ch->bytes_per_frame > xfer->max_frame_size) { + DPRINTF("bytes per transfer, %d, " + "exceeds maximum, %d!\n", + ch->bytes_per_frame, + xfer->max_frame_size); + break; + } + /* setup frame length */ + xfer->nframes = blockcount; + for (n = 0; n != blockcount; n++) { + p_len[n] = ch->bytes_per_frame; + } + + if (ch->end == ch->start) { + DPRINTF("no buffer!\n"); + break; + } + DPRINTFN(6, "transfer %d bytes\n", total); + + offset = 0; + + while (total > 0) { + + n = (ch->end - ch->cur); + if (n > total) { + n = total; + } + usb2_copy_in(xfer->frbuffers, offset, ch->cur, n); + + total -= n; + ch->cur += n; + offset += n; + + if (ch->cur >= ch->end) { + ch->cur = ch->start; + } + } + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + goto tr_transferred; + } + return; +} + +static void +uaudio_chan_record_callback(struct usb2_xfer *xfer) +{ + struct uaudio_chan *ch = xfer->priv_sc; + uint32_t *p_len = xfer->frlengths; + uint32_t n; + uint32_t m; + uint32_t total = (sndbuf_getblkcnt(ch->pcm_buf) * + sndbuf_getblksz(ch->pcm_buf)) / 2; + uint32_t blockcount; + uint32_t offset0; + uint32_t offset1; + + /* allow dynamic sizing of play buffer */ + blockcount = total / ch->bytes_per_frame; + + /* align to 8 units */ + blockcount &= ~7; + + /* range check - min */ + if (blockcount == 0) { + blockcount = 8; + } + /* range check - max */ + if (blockcount > xfer->max_frame_count) { + blockcount = xfer->max_frame_count; + } + /* compute the total length */ + total = blockcount * ch->bytes_per_frame; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + if (xfer->actlen < total) { + DPRINTF("short transfer, " + "%d of %d bytes\n", xfer->actlen, total); + } else { + DPRINTFN(6, "transferred %d bytes\n", xfer->actlen); + } + + offset0 = 0; + + for (n = 0; n != xfer->nframes; n++) { + + offset1 = offset0; + + while (p_len[n] > 0) { + + m = (ch->end - ch->cur); + + if (m > p_len[n]) { + m = p_len[n]; + } + usb2_copy_out(xfer->frbuffers, offset1, ch->cur, m); + + p_len[n] -= m; + offset1 += m; + ch->cur += m; + + if (ch->cur >= ch->end) { + ch->cur = ch->start; + } + } + + offset0 += ch->bytes_per_frame; + } + + chn_intr(ch->pcm_ch); + + case USB_ST_SETUP: + if (ch->bytes_per_frame > xfer->max_frame_size) { + DPRINTF("bytes per transfer, %d, " + "exceeds maximum, %d!\n", + ch->bytes_per_frame, + xfer->max_frame_size); + return; + } + xfer->nframes = blockcount; + for (n = 0; n != xfer->nframes; n++) { + p_len[n] = ch->bytes_per_frame; + } + + if (ch->end == ch->start) { + DPRINTF("no buffer!\n"); + return; + } + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + return; + } + goto tr_transferred; + } +} + +void * +uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ? + &sc->sc_play_chan : &sc->sc_rec_chan); + uint8_t endpoint; + uint8_t iface_index; + uint8_t alt_index; + usb2_error_t err; + + ch->buf = malloc(UAUDIO_DEFAULT_BUFSZ, M_DEVBUF, M_WAITOK | M_ZERO); + + if (ch->buf == NULL) { + goto error; + } + if (sndbuf_setup(b, ch->buf, UAUDIO_DEFAULT_BUFSZ) != 0) { + goto error; + } + ch->start = ch->buf; + ch->end = ch->buf + UAUDIO_DEFAULT_BUFSZ; + ch->cur = ch->buf; + ch->pcm_ch = c; + ch->pcm_mtx = c->lock; + ch->pcm_buf = b; + + if (ch->pcm_mtx == NULL) { + DPRINTF("ERROR: PCM channels does not have a mutex!\n"); + goto error; + } + /* setup play/record format */ + + ch->pcm_cap.fmtlist = ch->pcm_format; + + ch->pcm_format[0] = 0; + ch->pcm_format[1] = 0; + + ch->pcm_cap.minspeed = ch->sample_rate; + ch->pcm_cap.maxspeed = ch->sample_rate; + + ch->pcm_cap.fmtlist[0] = ch->p_fmt->freebsd_fmt; + + if (ch->p_asf1d->bNrChannels == 2) { + ch->pcm_cap.fmtlist[0] |= AFMT_STEREO; + } + ch->pcm_cap.fmtlist[1] = 0; + + + /* set alternate interface corresponding to the mode */ + + endpoint = ch->p_ed1->bEndpointAddress; + iface_index = ch->iface_index; + alt_index = ch->iface_alt_index; + + DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n", + endpoint, ch->sample_rate, iface_index, alt_index); + + err = usb2_set_alt_interface_index(sc->sc_udev, iface_index, alt_index); + if (err) { + DPRINTF("setting of alternate index failed: %s!\n", + usb2_errstr(err)); + goto error; + } + usb2_set_parent_iface(sc->sc_udev, iface_index, sc->sc_mixer_iface_index); + + /* + * If just one sampling rate is supported, + * no need to call "uaudio_set_speed()". + * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request. + */ + if (ch->p_asf1d->bSamFreqType != 1) { + if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) { + /* + * If the endpoint is adaptive setting the speed may + * fail. + */ + DPRINTF("setting of sample rate failed! (continuing anyway)\n"); + } + } + if (usb2_transfer_setup(sc->sc_udev, &iface_index, ch->xfer, + ch->usb2_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_mtx)) { + DPRINTF("could not allocate USB transfers!\n"); + goto error; + } + return (ch); + +error: + uaudio_chan_free(ch); + return (NULL); +} + +int +uaudio_chan_free(struct uaudio_chan *ch) +{ + if (ch->buf != NULL) { + free(ch->buf, M_DEVBUF); + ch->buf = NULL; + } + usb2_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS); + + ch->valid = 0; + + return (0); +} + +int +uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize) +{ + uaudio_chan_set_param_fragments(ch, blocksize, 0 - 1); + + return (ch->block_size); +} + +int +uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, + uint32_t blockcount) +{ + uint32_t max = sndbuf_getmaxsize(ch->pcm_buf); + + RANGE(blocksize, 128, max / 2); + + blockcount = max / blocksize; + RANGE(blockcount, 2, 512); + + if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) || + (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) { + + if (sndbuf_resize(ch->pcm_buf, blockcount, blocksize)) { + DPRINTFN(0, "failed to resize sound buffer, count=%u, " + "size=%u\n", blockcount, blocksize); + } + } + ch->block_size = sndbuf_getblksz(ch->pcm_buf); + + return (1); +} + +int +uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed) +{ + if (speed != ch->sample_rate) { + DPRINTF("rate conversion required\n"); + } + return (ch->sample_rate); +} + +int +uaudio_chan_getptr(struct uaudio_chan *ch) +{ + return (ch->cur - ch->start); +} + +struct pcmchan_caps * +uaudio_chan_getcaps(struct uaudio_chan *ch) +{ + return (&ch->pcm_cap); +} + +int +uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) +{ + ch->format = format; + return (0); +} + +int +uaudio_chan_start(struct uaudio_chan *ch) +{ + ch->cur = ch->start; + +#if (UAUDIO_NCHANBUFS != 2) +#error "please update code" +#endif + if (ch->xfer[0]) { + usb2_transfer_start(ch->xfer[0]); + } + if (ch->xfer[1]) { + usb2_transfer_start(ch->xfer[1]); + } + return (0); +} + +int +uaudio_chan_stop(struct uaudio_chan *ch) +{ +#if (UAUDIO_NCHANBUFS != 2) +#error "please update code" +#endif + usb2_transfer_stop(ch->xfer[0]); + usb2_transfer_stop(ch->xfer[1]); + return (0); +} + +/*========================================================================* + * AC - Audio Controller - routines + *========================================================================*/ + +static void +uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) +{ + struct uaudio_mixer_node *p_mc_new = + malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK); + + if (p_mc_new) { + bcopy(mc, p_mc_new, sizeof(*p_mc_new)); + p_mc_new->next = sc->sc_mixer_root; + sc->sc_mixer_root = p_mc_new; + sc->sc_mixer_count++; + } else { + DPRINTF("out of memory\n"); + } + return; +} + +static void +uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) +{ + int32_t res; + + if (mc->class < UAC_NCLASSES) { + DPRINTF("adding %s.%d\n", + uac_names[mc->class], mc->ctl); + } else { + DPRINTF("adding %d\n", mc->ctl); + } + + mc->delta = 0; + if (mc->type == MIX_ON_OFF) { + mc->minval = 0; + mc->maxval = 1; + } else if (mc->type == MIX_SELECTOR) { + + } else { + + /* determine min and max values */ + + mc->minval = uaudio_mixer_get(sc->sc_udev, GET_MIN, mc); + + mc->minval = uaudio_mixer_signext(mc->type, mc->minval); + + mc->maxval = uaudio_mixer_get(sc->sc_udev, GET_MAX, mc); + + mc->maxval = 1 + uaudio_mixer_signext(mc->type, mc->maxval); + + mc->mul = mc->maxval - mc->minval; + if (mc->mul == 0) { + mc->mul = 1; + } + res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc); + if (res > 0) { + mc->delta = ((res * 255) + (mc->mul / 2)) / mc->mul; + } + } + + if (mc->maxval < mc->minval) { + mc->maxval = mc->minval; + } + uaudio_mixer_add_ctl_sub(sc, mc); + +#if USB_DEBUG + if (uaudio_debug > 2) { + uint8_t i; + + for (i = 0; i < mc->nchan; i++) { + DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]); + } + DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' " + "min=%d max=%d\n", + mc->wIndex, mc->type, mc->ctl, + mc->minval, mc->maxval); + } +#endif + return; +} + +static void +uaudio_mixer_add_input(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ +#if USB_DEBUG + const struct usb2_audio_input_terminal *d = iot[id].u.it; + + DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " + "iChannelNames=%d\n", + d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, + d->bNrChannels, UGETW(d->wChannelConfig), + d->iChannelNames); +#endif + return; +} + +static void +uaudio_mixer_add_output(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ +#if USB_DEBUG + const struct usb2_audio_output_terminal *d = iot[id].u.ot; + + DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", + d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, + d->bSourceId, d->iTerminal); +#endif + return; +} + +static void +uaudio_mixer_add_mixer(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + struct uaudio_mixer_node mix; + + const struct usb2_audio_mixer_unit_0 *d0 = iot[id].u.mu; + const struct usb2_audio_mixer_unit_1 *d1; + + uint32_t bno; /* bit number */ + uint32_t p; /* bit number accumulator */ + uint32_t mo; /* matching outputs */ + uint32_t mc; /* matching channels */ + uint32_t ichs; /* input channels */ + uint32_t ochs; /* output channels */ + uint32_t c; + uint32_t chs; /* channels */ + uint32_t i; + uint32_t o; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d0->bUnitId, d0->bNrInPins); + + /* compute the number of input channels */ + + ichs = 0; + for (i = 0; i < d0->bNrInPins; i++) { + ichs += (uaudio_mixer_get_cluster(d0->baSourceId[i], iot) + .bNrChannels); + } + + d1 = (const void *)(d0->baSourceId + d0->bNrInPins); + + /* and the number of output channels */ + + ochs = d1->bNrChannels; + + DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); + + bzero(&mix, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.type = MIX_SIGNED_16; + + if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) { + return; + } + for (p = i = 0; i < d0->bNrInPins; i++) { + chs = uaudio_mixer_get_cluster(d0->baSourceId[i], iot).bNrChannels; + mc = 0; + for (c = 0; c < chs; c++) { + mo = 0; + for (o = 0; o < ochs; o++) { + bno = ((p + c) * ochs) + o; + if (BIT_TEST(d1->bmControls, bno)) { + mo++; + } + } + if (mo == 1) { + mc++; + } + } + if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { + + /* repeat bit-scan */ + + mc = 0; + for (c = 0; c < chs; c++) { + for (o = 0; o < ochs; o++) { + bno = ((p + c) * ochs) + o; + if (BIT_TEST(d1->bmControls, bno)) { + mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); + } + } + } + mix.nchan = chs; + uaudio_mixer_add_ctl(sc, &mix); + } else { + /* XXX */ + } + p += chs; + } + return; +} + +static void +uaudio_mixer_add_selector(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb2_audio_selector_unit *d = iot[id].u.su; + struct uaudio_mixer_node mix; + uint16_t i; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins); + + if (d->bNrInPins == 0) { + return; + } + bzero(&mix, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + mix.wValue[0] = MAKE_WORD(0, 0); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.nchan = 1; + mix.type = MIX_SELECTOR; + + mix.ctl = SOUND_MIXER_NRDEVICES; + mix.minval = 1; + mix.maxval = d->bNrInPins; + + if (mix.maxval > MAX_SELECTOR_INPUT_PIN) { + mix.maxval = MAX_SELECTOR_INPUT_PIN; + } + mix.mul = (mix.maxval - mix.minval); + for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { + mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + } + + for (i = 0; i < mix.maxval; i++) { + mix.slctrtype[i] = uaudio_mixer_feature_name + (&iot[d->baSourceId[i]], &mix); + } + + mix.class = 0; /* not used */ + + uaudio_mixer_add_ctl(sc, &mix); + return; +} + +static uint32_t +uaudio_mixer_feature_get_bmaControls(const struct usb2_audio_feature_unit *d, + uint8_t index) +{ + uint32_t temp = 0; + uint32_t offset = (index * d->bControlSize); + + if (d->bControlSize > 0) { + temp |= d->bmaControls[offset]; + if (d->bControlSize > 1) { + temp |= d->bmaControls[offset + 1] << 8; + if (d->bControlSize > 2) { + temp |= d->bmaControls[offset + 2] << 16; + if (d->bControlSize > 3) { + temp |= d->bmaControls[offset + 3] << 24; + } + } + } + } + return (temp); +} + +static void +uaudio_mixer_add_feature(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb2_audio_feature_unit *d = iot[id].u.fu; + struct uaudio_mixer_node mix; + uint32_t fumask; + uint32_t mmask; + uint32_t cmask; + uint16_t mixernumber; + uint8_t nchan; + uint8_t chan; + uint8_t ctl; + uint8_t i; + + if (d->bControlSize == 0) { + return; + } + bzero(&mix, sizeof(mix)); + + nchan = (d->bLength - 7) / d->bControlSize; + mmask = uaudio_mixer_feature_get_bmaControls(d, 0); + cmask = 0; + + if (nchan == 0) { + return; + } + /* figure out what we can control */ + + for (chan = 1; chan < nchan; chan++) { + DPRINTFN(10, "chan=%d mask=%x\n", + chan, uaudio_mixer_feature_get_bmaControls(d, chan)); + + cmask |= uaudio_mixer_feature_get_bmaControls(d, chan); + } + + if (nchan > MIX_MAX_CHAN) { + nchan = MIX_MAX_CHAN; + } + mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + + for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) { + + fumask = FU_MASK(ctl); + + DPRINTFN(5, "ctl=%d fumask=0x%04x\n", + ctl, fumask); + + if (mmask & fumask) { + mix.nchan = 1; + mix.wValue[0] = MAKE_WORD(ctl, 0); + } else if (cmask & fumask) { + mix.nchan = nchan - 1; + for (i = 1; i < nchan; i++) { + if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask) + mix.wValue[i - 1] = MAKE_WORD(ctl, i); + else + mix.wValue[i - 1] = -1; + } + } else { + continue; + } + + mixernumber = uaudio_mixer_feature_name(&iot[id], &mix); + + switch (ctl) { + case MUTE_CONTROL: + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; + break; + + case VOLUME_CONTROL: + mix.type = MIX_SIGNED_16; + mix.ctl = mixernumber; + break; + + case BASS_CONTROL: + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_BASS; + break; + + case MID_CONTROL: + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + break; + + case TREBLE_CONTROL: + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_TREBLE; + break; + + case GRAPHIC_EQUALIZER_CONTROL: + continue; /* XXX don't add anything */ + break; + + case AGC_CONTROL: + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + break; + + case DELAY_CONTROL: + mix.type = MIX_UNSIGNED_16; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + break; + + case BASS_BOOST_CONTROL: + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + break; + + case LOUDNESS_CONTROL: + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ + break; + + default: + mix.type = MIX_UNKNOWN; + break; + } + + if (mix.type != MIX_UNKNOWN) { + uaudio_mixer_add_ctl(sc, &mix); + } + } + return; +} + +static void +uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb2_audio_processing_unit_1 *d1 = + (const void *)(d0->baSourceId + d0->bNrInPins); + const struct usb2_audio_processing_unit_updown *ud = + (const void *)(d1->bmControls + d1->bControlSize); + struct uaudio_mixer_node mix; + uint8_t i; + + if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) { + return; + } + if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes)) + == NULL) { + return; + } + DPRINTFN(3, "bUnitId=%d bNrModes=%d\n", + d0->bUnitId, ud->bNrModes); + + if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) { + DPRINTF("no mode select\n"); + return; + } + bzero(&mix, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + mix.nchan = 1; + mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.type = MIX_ON_OFF; /* XXX */ + + for (i = 0; i < ud->bNrModes; i++) { + DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i])); + /* XXX */ + } + + uaudio_mixer_add_ctl(sc, &mix); + return; +} + +static void +uaudio_mixer_add_processing(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb2_audio_processing_unit_1 *d1 = + (const void *)(d0->baSourceId + d0->bNrInPins); + struct uaudio_mixer_node mix; + uint16_t ptype; + + bzero(&mix, sizeof(mix)); + + ptype = UGETW(d0->wProcessType); + + DPRINTFN(3, "wProcessType=%d bUnitId=%d " + "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins); + + if (d1->bControlSize == 0) { + return; + } + if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { + mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + mix.nchan = 1; + mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.type = MIX_ON_OFF; + uaudio_mixer_add_ctl(sc, &mix); + } + switch (ptype) { + case UPDOWNMIX_PROCESS: + uaudio_mixer_add_processing_updown(sc, iot, id); + break; + + case DOLBY_PROLOGIC_PROCESS: + case P3D_STEREO_EXTENDER_PROCESS: + case REVERBATION_PROCESS: + case CHORUS_PROCESS: + case DYN_RANGE_COMP_PROCESS: + default: + DPRINTF("unit %d, type=%d is not implemented\n", + d0->bUnitId, ptype); + break; + } + return; +} + +static void +uaudio_mixer_add_extension(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb2_audio_extension_unit_0 *d0 = iot[id].u.eu; + const struct usb2_audio_extension_unit_1 *d1 = + (const void *)(d0->baSourceId + d0->bNrInPins); + struct uaudio_mixer_node mix; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d0->bUnitId, d0->bNrInPins); + + if (sc->sc_uq_au_no_xu) { + return; + } + if (d1->bControlSize == 0) { + return; + } + if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { + + bzero(&mix, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + mix.nchan = 1; + mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.type = MIX_ON_OFF; + + uaudio_mixer_add_ctl(sc, &mix); + } + return; +} + +static const void * +uaudio_mixer_verify_desc(const void *arg, uint32_t len) +{ + const struct usb2_audio_mixer_unit_1 *d1; + const struct usb2_audio_extension_unit_1 *e1; + const struct usb2_audio_processing_unit_1 *u1; + + union { + const struct usb2_descriptor *desc; + const struct usb2_audio_input_terminal *it; + const struct usb2_audio_output_terminal *ot; + const struct usb2_audio_mixer_unit_0 *mu; + const struct usb2_audio_selector_unit *su; + const struct usb2_audio_feature_unit *fu; + const struct usb2_audio_processing_unit_0 *pu; + const struct usb2_audio_extension_unit_0 *eu; + } u; + + u.desc = arg; + + if (u.desc == NULL) { + goto error; + } + if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) { + goto error; + } + switch (u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + len += sizeof(*u.it); + break; + + case UDESCSUB_AC_OUTPUT: + len += sizeof(*u.ot); + break; + + case UDESCSUB_AC_MIXER: + len += sizeof(*u.mu); + + if (u.desc->bLength < len) { + goto error; + } + len += u.mu->bNrInPins; + + if (u.desc->bLength < len) { + goto error; + } + d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); + + len += sizeof(*d1); + break; + + case UDESCSUB_AC_SELECTOR: + len += sizeof(*u.su); + + if (u.desc->bLength < len) { + goto error; + } + len += u.su->bNrInPins; + break; + + case UDESCSUB_AC_FEATURE: + len += (sizeof(*u.fu) + 1); + break; + + case UDESCSUB_AC_PROCESSING: + len += sizeof(*u.pu); + + if (u.desc->bLength < len) { + goto error; + } + len += u.pu->bNrInPins; + + if (u.desc->bLength < len) { + goto error; + } + u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); + + len += sizeof(*u1); + + if (u.desc->bLength < len) { + goto error; + } + len += u1->bControlSize; + + break; + + case UDESCSUB_AC_EXTENSION: + len += sizeof(*u.eu); + + if (u.desc->bLength < len) { + goto error; + } + len += u.eu->bNrInPins; + + if (u.desc->bLength < len) { + goto error; + } + e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); + + len += sizeof(*e1); + + if (u.desc->bLength < len) { + goto error; + } + len += e1->bControlSize; + break; + + default: + goto error; + } + + if (u.desc->bLength < len) { + goto error; + } + return (u.desc); + +error: + if (u.desc) { + DPRINTF("invalid descriptor, type=%d, " + "sub_type=%d, len=%d of %d bytes\n", + u.desc->bDescriptorType, + u.desc->bDescriptorSubtype, + u.desc->bLength, len); + } + return (NULL); +} + +#if USB_DEBUG +static void +uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot) +{ + static const char *channel_names[16] = { + "LEFT", "RIGHT", "CENTER", "LFE", + "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER", + "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP", + "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15", + }; + uint16_t cc; + uint8_t i; + const struct usb2_audio_cluster cl = uaudio_mixer_get_cluster(id, iot); + + cc = UGETW(cl.wChannelConfig); + + DPRINTF("cluster: bNrChannels=%u iChannelNames=%u wChannelConfig=" + "0x%04x:\n", cl.iChannelNames, cl.bNrChannels, cc); + + for (i = 0; cc; i++) { + if (cc & 1) { + DPRINTF(" - %s\n", channel_names[i]); + } + cc >>= 1; + } + return; +} + +#endif + +static struct usb2_audio_cluster +uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) +{ + struct usb2_audio_cluster r; + const struct usb2_descriptor *dp; + uint8_t i; + + for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ + dp = iot[id].u.desc; + if (dp == NULL) { + goto error; + } + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + r.bNrChannels = iot[id].u.it->bNrChannels; + r.wChannelConfig[0] = iot[id].u.it->wChannelConfig[0]; + r.wChannelConfig[1] = iot[id].u.it->wChannelConfig[1]; + r.iChannelNames = iot[id].u.it->iChannelNames; + goto done; + + case UDESCSUB_AC_OUTPUT: + id = iot[id].u.ot->bSourceId; + break; + + case UDESCSUB_AC_MIXER: + r = *(const struct usb2_audio_cluster *) + &iot[id].u.mu->baSourceId[iot[id].u.mu-> + bNrInPins]; + goto done; + + case UDESCSUB_AC_SELECTOR: + if (iot[id].u.su->bNrInPins > 0) { + /* XXX This is not really right */ + id = iot[id].u.su->baSourceId[0]; + } + break; + + case UDESCSUB_AC_FEATURE: + id = iot[id].u.fu->bSourceId; + break; + + case UDESCSUB_AC_PROCESSING: + r = *((const struct usb2_audio_cluster *) + &iot[id].u.pu->baSourceId[iot[id].u.pu-> + bNrInPins]); + goto done; + + case UDESCSUB_AC_EXTENSION: + r = *((const struct usb2_audio_cluster *) + &iot[id].u.eu->baSourceId[iot[id].u.eu-> + bNrInPins]); + goto done; + + default: + goto error; + } + } +error: + DPRINTF("bad data\n"); + bzero(&r, sizeof(r)); +done: + return (r); +} + +#if USB_DEBUG + +struct uaudio_tt_to_string { + uint16_t terminal_type; + const char *desc; +}; + +static const struct uaudio_tt_to_string uaudio_tt_to_string[] = { + + /* USB terminal types */ + {UAT_UNDEFINED, "UAT_UNDEFINED"}, + {UAT_STREAM, "UAT_STREAM"}, + {UAT_VENDOR, "UAT_VENDOR"}, + + /* input terminal types */ + {UATI_UNDEFINED, "UATI_UNDEFINED"}, + {UATI_MICROPHONE, "UATI_MICROPHONE"}, + {UATI_DESKMICROPHONE, "UATI_DESKMICROPHONE"}, + {UATI_PERSONALMICROPHONE, "UATI_PERSONALMICROPHONE"}, + {UATI_OMNIMICROPHONE, "UATI_OMNIMICROPHONE"}, + {UATI_MICROPHONEARRAY, "UATI_MICROPHONEARRAY"}, + {UATI_PROCMICROPHONEARR, "UATI_PROCMICROPHONEARR"}, + + /* output terminal types */ + {UATO_UNDEFINED, "UATO_UNDEFINED"}, + {UATO_SPEAKER, "UATO_SPEAKER"}, + {UATO_HEADPHONES, "UATO_HEADPHONES"}, + {UATO_DISPLAYAUDIO, "UATO_DISPLAYAUDIO"}, + {UATO_DESKTOPSPEAKER, "UATO_DESKTOPSPEAKER"}, + {UATO_ROOMSPEAKER, "UATO_ROOMSPEAKER"}, + {UATO_COMMSPEAKER, "UATO_COMMSPEAKER"}, + {UATO_SUBWOOFER, "UATO_SUBWOOFER"}, + + /* bidir terminal types */ + {UATB_UNDEFINED, "UATB_UNDEFINED"}, + {UATB_HANDSET, "UATB_HANDSET"}, + {UATB_HEADSET, "UATB_HEADSET"}, + {UATB_SPEAKERPHONE, "UATB_SPEAKERPHONE"}, + {UATB_SPEAKERPHONEESUP, "UATB_SPEAKERPHONEESUP"}, + {UATB_SPEAKERPHONEECANC, "UATB_SPEAKERPHONEECANC"}, + + /* telephony terminal types */ + {UATT_UNDEFINED, "UATT_UNDEFINED"}, + {UATT_PHONELINE, "UATT_PHONELINE"}, + {UATT_TELEPHONE, "UATT_TELEPHONE"}, + {UATT_DOWNLINEPHONE, "UATT_DOWNLINEPHONE"}, + + /* external terminal types */ + {UATE_UNDEFINED, "UATE_UNDEFINED"}, + {UATE_ANALOGCONN, "UATE_ANALOGCONN"}, + {UATE_LINECONN, "UATE_LINECONN"}, + {UATE_LEGACYCONN, "UATE_LEGACYCONN"}, + {UATE_DIGITALAUIFC, "UATE_DIGITALAUIFC"}, + {UATE_SPDIF, "UATE_SPDIF"}, + {UATE_1394DA, "UATE_1394DA"}, + {UATE_1394DV, "UATE_1394DV"}, + + /* embedded function terminal types */ + {UATF_UNDEFINED, "UATF_UNDEFINED"}, + {UATF_CALIBNOISE, "UATF_CALIBNOISE"}, + {UATF_EQUNOISE, "UATF_EQUNOISE"}, + {UATF_CDPLAYER, "UATF_CDPLAYER"}, + {UATF_DAT, "UATF_DAT"}, + {UATF_DCC, "UATF_DCC"}, + {UATF_MINIDISK, "UATF_MINIDISK"}, + {UATF_ANALOGTAPE, "UATF_ANALOGTAPE"}, + {UATF_PHONOGRAPH, "UATF_PHONOGRAPH"}, + {UATF_VCRAUDIO, "UATF_VCRAUDIO"}, + {UATF_VIDEODISCAUDIO, "UATF_VIDEODISCAUDIO"}, + {UATF_DVDAUDIO, "UATF_DVDAUDIO"}, + {UATF_TVTUNERAUDIO, "UATF_TVTUNERAUDIO"}, + {UATF_SATELLITE, "UATF_SATELLITE"}, + {UATF_CABLETUNER, "UATF_CABLETUNER"}, + {UATF_DSS, "UATF_DSS"}, + {UATF_RADIORECV, "UATF_RADIORECV"}, + {UATF_RADIOXMIT, "UATF_RADIOXMIT"}, + {UATF_MULTITRACK, "UATF_MULTITRACK"}, + {UATF_SYNTHESIZER, "UATF_SYNTHESIZER"}, + + /* unknown */ + {0x0000, "UNKNOWN"}, +}; + +static const char * +uaudio_mixer_get_terminal_name(uint16_t terminal_type) +{ + const struct uaudio_tt_to_string *uat = uaudio_tt_to_string; + + while (uat->terminal_type) { + if (uat->terminal_type == terminal_type) { + break; + } + uat++; + } + if (uat->terminal_type == 0) { + DPRINTF("unknown terminal type (0x%04x)", terminal_type); + } + return (uat->desc); +} + +#endif + +static uint16_t +uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, + struct uaudio_mixer_node *mix) +{ + uint16_t terminal_type = 0x0000; + const struct uaudio_terminal_node *input[2]; + const struct uaudio_terminal_node *output[2]; + + input[0] = uaudio_mixer_get_input(iot, 0); + input[1] = uaudio_mixer_get_input(iot, 1); + + output[0] = uaudio_mixer_get_output(iot, 0); + output[1] = uaudio_mixer_get_output(iot, 1); + + /* + * check if there is only + * one output terminal: + */ + if (output[0] && (!output[1])) { + terminal_type = UGETW(output[0]->u.ot->wTerminalType); + } + /* + * If the only output terminal is USB, + * the class is UAC_RECORD. + */ + if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { + + mix->class = UAC_RECORD; + if (input[0] && (!input[1])) { + terminal_type = UGETW(input[0]->u.it->wTerminalType); + } else { + terminal_type = 0; + } + goto done; + } + /* + * if the unit is connected to just + * one input terminal, the + * class is UAC_INPUT: + */ + if (input[0] && (!input[1])) { + mix->class = UAC_INPUT; + terminal_type = UGETW(input[0]->u.it->wTerminalType); + goto done; + } + /* + * Otherwise, the class is UAC_OUTPUT. + */ + mix->class = UAC_OUTPUT; +done: + return (terminal_type); +} + +struct uaudio_tt_to_feature { + uint16_t terminal_type; + uint16_t feature; +}; + +static const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = { + + {UAT_STREAM, SOUND_MIXER_PCM}, + + {UATI_MICROPHONE, SOUND_MIXER_MIC}, + {UATI_DESKMICROPHONE, SOUND_MIXER_MIC}, + {UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC}, + {UATI_OMNIMICROPHONE, SOUND_MIXER_MIC}, + {UATI_MICROPHONEARRAY, SOUND_MIXER_MIC}, + {UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC}, + + {UATO_SPEAKER, SOUND_MIXER_SPEAKER}, + {UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER}, + {UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER}, + {UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER}, + + {UATE_ANALOGCONN, SOUND_MIXER_LINE}, + {UATE_LINECONN, SOUND_MIXER_LINE}, + {UATE_LEGACYCONN, SOUND_MIXER_LINE}, + + {UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM}, + {UATE_SPDIF, SOUND_MIXER_ALTPCM}, + {UATE_1394DA, SOUND_MIXER_ALTPCM}, + {UATE_1394DV, SOUND_MIXER_ALTPCM}, + + {UATF_CDPLAYER, SOUND_MIXER_CD}, + + {UATF_SYNTHESIZER, SOUND_MIXER_SYNTH}, + + {UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO}, + {UATF_DVDAUDIO, SOUND_MIXER_VIDEO}, + {UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO}, + + /* telephony terminal types */ + {UATT_UNDEFINED, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ + {UATT_PHONELINE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ + {UATT_TELEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ + {UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ + + {UATF_RADIORECV, SOUND_MIXER_RADIO}, + {UATF_RADIOXMIT, SOUND_MIXER_RADIO}, + + {UAT_UNDEFINED, SOUND_MIXER_VOLUME}, + {UAT_VENDOR, SOUND_MIXER_VOLUME}, + {UATI_UNDEFINED, SOUND_MIXER_VOLUME}, + + /* output terminal types */ + {UATO_UNDEFINED, SOUND_MIXER_VOLUME}, + {UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME}, + {UATO_SUBWOOFER, SOUND_MIXER_VOLUME}, + {UATO_HEADPHONES, SOUND_MIXER_VOLUME}, + + /* bidir terminal types */ + {UATB_UNDEFINED, SOUND_MIXER_VOLUME}, + {UATB_HANDSET, SOUND_MIXER_VOLUME}, + {UATB_HEADSET, SOUND_MIXER_VOLUME}, + {UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME}, + {UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME}, + {UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME}, + + /* external terminal types */ + {UATE_UNDEFINED, SOUND_MIXER_VOLUME}, + + /* embedded function terminal types */ + {UATF_UNDEFINED, SOUND_MIXER_VOLUME}, + {UATF_CALIBNOISE, SOUND_MIXER_VOLUME}, + {UATF_EQUNOISE, SOUND_MIXER_VOLUME}, + {UATF_DAT, SOUND_MIXER_VOLUME}, + {UATF_DCC, SOUND_MIXER_VOLUME}, + {UATF_MINIDISK, SOUND_MIXER_VOLUME}, + {UATF_ANALOGTAPE, SOUND_MIXER_VOLUME}, + {UATF_PHONOGRAPH, SOUND_MIXER_VOLUME}, + {UATF_VCRAUDIO, SOUND_MIXER_VOLUME}, + {UATF_SATELLITE, SOUND_MIXER_VOLUME}, + {UATF_CABLETUNER, SOUND_MIXER_VOLUME}, + {UATF_DSS, SOUND_MIXER_VOLUME}, + {UATF_MULTITRACK, SOUND_MIXER_VOLUME}, + {0xffff, SOUND_MIXER_VOLUME}, + + /* default */ + {0x0000, SOUND_MIXER_VOLUME}, +}; + +static uint16_t +uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot, + struct uaudio_mixer_node *mix) +{ + const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature; + uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix); + + if ((mix->class == UAC_RECORD) && (terminal_type == 0)) { + return (SOUND_MIXER_IMIX); + } + while (uat->terminal_type) { + if (uat->terminal_type == terminal_type) { + break; + } + uat++; + } + + DPRINTF("terminal_type=%s (0x%04x) -> %d\n", + uaudio_mixer_get_terminal_name(terminal_type), + terminal_type, uat->feature); + + return (uat->feature); +} + +const static struct uaudio_terminal_node * +uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t index) +{ + struct uaudio_terminal_node *root = iot->root; + uint8_t n; + + n = iot->usr.id_max; + do { + if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) { + if (!index--) { + return (root + n); + } + } + } while (n--); + + return (NULL); +} + +const static struct uaudio_terminal_node * +uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t index) +{ + struct uaudio_terminal_node *root = iot->root; + uint8_t n; + + n = iot->usr.id_max; + do { + if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) { + if (!index--) { + return (root + n); + } + } + } while (n--); + + return (NULL); +} + +static void +uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, + const uint8_t *p_id, uint8_t n_id, + struct uaudio_search_result *info) +{ + struct uaudio_terminal_node *iot; + uint8_t n; + uint8_t i; + + if (info->recurse_level >= UAUDIO_RECURSE_LIMIT) { + return; + } + info->recurse_level++; + + for (n = 0; n < n_id; n++) { + + i = p_id[n]; + + if (info->bit_visited[i / 8] & (1 << (i % 8))) { + /* don't go into a circle */ + DPRINTF("avoided going into a circle at id=%d!\n", i); + continue; + } else { + info->bit_visited[i / 8] |= (1 << (i % 8)); + } + + iot = (root + i); + + if (iot->u.desc == NULL) { + continue; + } + switch (iot->u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + info->bit_input[i / 8] |= (1 << (i % 8)); + break; + + case UDESCSUB_AC_FEATURE: + uaudio_mixer_find_inputs_sub + (root, &iot->u.fu->bSourceId, 1, info); + break; + + case UDESCSUB_AC_OUTPUT: + uaudio_mixer_find_inputs_sub + (root, &iot->u.ot->bSourceId, 1, info); + break; + + case UDESCSUB_AC_MIXER: + uaudio_mixer_find_inputs_sub + (root, iot->u.mu->baSourceId, + iot->u.mu->bNrInPins, info); + break; + + case UDESCSUB_AC_SELECTOR: + uaudio_mixer_find_inputs_sub + (root, iot->u.su->baSourceId, + iot->u.su->bNrInPins, info); + break; + + case UDESCSUB_AC_PROCESSING: + uaudio_mixer_find_inputs_sub + (root, iot->u.pu->baSourceId, + iot->u.pu->bNrInPins, info); + break; + + case UDESCSUB_AC_EXTENSION: + uaudio_mixer_find_inputs_sub + (root, iot->u.eu->baSourceId, + iot->u.eu->bNrInPins, info); + break; + + case UDESCSUB_AC_HEADER: + default: + break; + } + } + info->recurse_level--; + return; +} + +static void +uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id, + uint8_t n_id, struct uaudio_search_result *info) +{ + struct uaudio_terminal_node *iot = (root + id); + uint8_t j; + + j = n_id; + do { + if ((j != id) && ((root + j)->u.desc) && + ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) { + + /* + * "j" (output) <--- virtual wire <--- "id" (input) + * + * if "j" has "id" on the input, then "id" have "j" on + * the output, because they are connected: + */ + if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) { + iot->usr.bit_output[j / 8] |= (1 << (j % 8)); + } + } + } while (j--); + + return; +} + +static void +uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb2_device *udev, + void *desc) +{ + const struct usb2_audio_control_descriptor *acdp; + struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev); + const struct usb2_descriptor *dp; + const struct usb2_audio_unit *au; + struct uaudio_terminal_node *iot = NULL; + uint16_t wTotalLen; + uint8_t ID_max = 0; /* inclusive */ + uint8_t i; + + desc = usb2_desc_foreach(cd, desc); + + if (desc == NULL) { + DPRINTF("no Audio Control header\n"); + goto done; + } + acdp = desc; + + if ((acdp->bLength < sizeof(*acdp)) || + (acdp->bDescriptorType != UDESC_CS_INTERFACE) || + (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) { + DPRINTF("invalid Audio Control header\n"); + goto done; + } + wTotalLen = UGETW(cd->wTotalLength); + sc->sc_audio_rev = UGETW(acdp->bcdADC); + + DPRINTFN(3, "found AC header, vers=%03x, len=%d\n", + sc->sc_audio_rev, wTotalLen); + + if (sc->sc_audio_rev != UAUDIO_VERSION) { + + if (sc->sc_uq_bad_adc) { + + } else { + DPRINTF("invalid audio version\n"); + goto done; + } + } + iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP, + M_WAITOK | M_ZERO); + + if (iot == NULL) { + DPRINTF("no memory!\n"); + goto done; + } + while ((desc = usb2_desc_foreach(cd, desc))) { + + dp = desc; + + if (dp->bLength > wTotalLen) { + break; + } else { + wTotalLen -= dp->bLength; + } + + au = uaudio_mixer_verify_desc(dp, 0); + + if (au) { + iot[au->bUnitId].u.desc = (const void *)au; + if (au->bUnitId > ID_max) { + ID_max = au->bUnitId; + } + } + } + + DPRINTF("Maximum ID=%d\n", ID_max); + + /* + * determine sourcing inputs for + * all nodes in the tree: + */ + i = ID_max; + do { + uaudio_mixer_find_inputs_sub(iot, &i, 1, &((iot + i)->usr)); + } while (i--); + + /* + * determine outputs for + * all nodes in the tree: + */ + i = ID_max; + do { + uaudio_mixer_find_outputs_sub(iot, i, ID_max, &((iot + i)->usr)); + } while (i--); + + /* set "id_max" and "root" */ + + i = ID_max; + do { + (iot + i)->usr.id_max = ID_max; + (iot + i)->root = iot; + } while (i--); + +#if USB_DEBUG + i = ID_max; + do { + uint8_t j; + + if (iot[i].u.desc == NULL) { + continue; + } + DPRINTF("id %d:\n", i); + + switch (iot[i].u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + DPRINTF(" - AC_INPUT type=%s\n", + uaudio_mixer_get_terminal_name + (UGETW(iot[i].u.it->wTerminalType))); + uaudio_mixer_dump_cluster(i, iot); + break; + + case UDESCSUB_AC_OUTPUT: + DPRINTF(" - AC_OUTPUT type=%s " + "src=%d\n", uaudio_mixer_get_terminal_name + (UGETW(iot[i].u.ot->wTerminalType)), + iot[i].u.ot->bSourceId); + break; + + case UDESCSUB_AC_MIXER: + DPRINTF(" - AC_MIXER src:\n"); + for (j = 0; j < iot[i].u.mu->bNrInPins; j++) { + DPRINTF(" - %d\n", iot[i].u.mu->baSourceId[j]); + } + uaudio_mixer_dump_cluster(i, iot); + break; + + case UDESCSUB_AC_SELECTOR: + DPRINTF(" - AC_SELECTOR src:\n"); + for (j = 0; j < iot[i].u.su->bNrInPins; j++) { + DPRINTF(" - %d\n", iot[i].u.su->baSourceId[j]); + } + break; + + case UDESCSUB_AC_FEATURE: + DPRINTF(" - AC_FEATURE src=%d\n", iot[i].u.fu->bSourceId); + break; + + case UDESCSUB_AC_PROCESSING: + DPRINTF(" - AC_PROCESSING src:\n"); + for (j = 0; j < iot[i].u.pu->bNrInPins; j++) { + DPRINTF(" - %d\n", iot[i].u.pu->baSourceId[j]); + } + uaudio_mixer_dump_cluster(i, iot); + break; + + case UDESCSUB_AC_EXTENSION: + DPRINTF(" - AC_EXTENSION src:\n"); + for (j = 0; j < iot[i].u.eu->bNrInPins; j++) { + DPRINTF("%d ", iot[i].u.eu->baSourceId[j]); + } + uaudio_mixer_dump_cluster(i, iot); + break; + + default: + DPRINTF("unknown audio control (subtype=%d)\n", + iot[i].u.desc->bDescriptorSubtype); + } + + DPRINTF("Inputs to this ID are:\n"); + + j = ID_max; + do { + if (iot[i].usr.bit_input[j / 8] & (1 << (j % 8))) { + DPRINTF(" -- ID=%d\n", j); + } + } while (j--); + + DPRINTF("Outputs from this ID are:\n"); + + j = ID_max; + do { + if (iot[i].usr.bit_output[j / 8] & (1 << (j % 8))) { + DPRINTF(" -- ID=%d\n", j); + } + } while (j--); + + } while (i--); +#endif + + /* + * scan the config to create a linked + * list of "mixer" nodes: + */ + + i = ID_max; + do { + dp = iot[i].u.desc; + + if (dp == NULL) { + continue; + } + DPRINTFN(11, "id=%d subtype=%d\n", + i, dp->bDescriptorSubtype); + + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_HEADER: + DPRINTF("unexpected AC header\n"); + break; + + case UDESCSUB_AC_INPUT: + uaudio_mixer_add_input(sc, iot, i); + break; + + case UDESCSUB_AC_OUTPUT: + uaudio_mixer_add_output(sc, iot, i); + break; + + case UDESCSUB_AC_MIXER: + uaudio_mixer_add_mixer(sc, iot, i); + break; + + case UDESCSUB_AC_SELECTOR: + uaudio_mixer_add_selector(sc, iot, i); + break; + + case UDESCSUB_AC_FEATURE: + uaudio_mixer_add_feature(sc, iot, i); + break; + + case UDESCSUB_AC_PROCESSING: + uaudio_mixer_add_processing(sc, iot, i); + break; + + case UDESCSUB_AC_EXTENSION: + uaudio_mixer_add_extension(sc, iot, i); + break; + + default: + DPRINTF("bad AC desc subtype=0x%02x\n", + dp->bDescriptorSubtype); + break; + } + + } while (i--); + +done: + if (iot) { + free(iot, M_TEMP); + } + return; +} + +static uint16_t +uaudio_mixer_get(struct usb2_device *udev, uint8_t what, + struct uaudio_mixer_node *mc) +{ + struct usb2_device_request req; + uint16_t val; + uint16_t len = MIX_SIZE(mc->type); + uint8_t data[4]; + usb2_error_t err; + + if (mc->wValue[0] == -1) { + return (0); + } + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = what; + USETW(req.wValue, mc->wValue[0]); + USETW(req.wIndex, mc->wIndex); + USETW(req.wLength, len); + + err = usb2_do_request(udev, &Giant, &req, data); + if (err) { + DPRINTF("err=%s\n", usb2_errstr(err)); + return (0); + } + if (len < 1) { + data[0] = 0; + } + if (len < 2) { + data[1] = 0; + } + val = (data[0] | (data[1] << 8)); + + DPRINTFN(3, "val=%d\n", val); + + return (val); +} + +static void +uaudio_mixer_write_cfg_callback(struct usb2_xfer *xfer) +{ + struct usb2_device_request req; + struct uaudio_softc *sc = xfer->priv_sc; + struct uaudio_mixer_node *mc = sc->sc_mixer_curr; + uint16_t len; + uint8_t repeat = 1; + uint8_t update; + uint8_t chan; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + case USB_ST_SETUP: +tr_setup: + + if (mc == NULL) { + mc = sc->sc_mixer_root; + sc->sc_mixer_curr = mc; + sc->sc_mixer_chan = 0; + repeat = 0; + } + while (mc) { + while (sc->sc_mixer_chan < mc->nchan) { + + len = MIX_SIZE(mc->type); + + chan = sc->sc_mixer_chan; + + sc->sc_mixer_chan++; + + update = ((mc->update[chan / 8] & (1 << (chan % 8))) && + (mc->wValue[chan] != -1)); + + mc->update[chan / 8] &= ~(1 << (chan % 8)); + + if (update) { + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = SET_CUR; + USETW(req.wValue, mc->wValue[chan]); + USETW(req.wIndex, mc->wIndex); + USETW(req.wLength, len); + + if (len > 0) { + buf[0] = (mc->wData[chan] & 0xFF); + } + if (len > 1) { + buf[1] = (mc->wData[chan] >> 8) & 0xFF; + } + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_copy_in(xfer->frbuffers + 1, 0, buf, len); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + usb2_start_hardware(xfer); + return; + } + } + + mc = mc->next; + sc->sc_mixer_curr = mc; + sc->sc_mixer_chan = 0; + } + + if (repeat) { + goto tr_setup; + } + return; + + default: /* Error */ + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + + goto tr_transferred; + } +} + +static usb2_error_t +uaudio_set_speed(struct usb2_device *udev, uint8_t endpt, uint32_t speed) +{ + struct usb2_device_request req; + uint8_t data[3]; + + DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed); + + req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; + req.bRequest = SET_CUR; + USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); + USETW(req.wIndex, endpt); + USETW(req.wLength, 3); + data[0] = speed; + data[1] = speed >> 8; + data[2] = speed >> 16; + + return (usb2_do_request(udev, &Giant, &req, data)); +} + +static int +uaudio_mixer_signext(uint8_t type, int val) +{ + if (!MIX_UNSIGNED(type)) { + if (MIX_SIZE(type) == 2) { + val = (int16_t)val; + } else { + val = (int8_t)val; + } + } + return (val); +} + +static int +uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val) +{ + DPRINTFN(6, "type=%03x val=%d min=%d max=%d ", + mc->type, val, mc->minval, mc->maxval); + + if (mc->type == MIX_ON_OFF) { + val = (val != 0); + } else if (mc->type == MIX_SELECTOR) { + if ((val < mc->minval) || + (val > mc->maxval)) { + val = mc->minval; + } + } else { + val = (((val + (mc->delta / 2)) * mc->mul) / 255) + mc->minval; + } + + DPRINTFN(6, "val=%d\n", val); + return (val); +} + +static void +uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc, + uint8_t chan, int32_t val) +{ + val = uaudio_mixer_bsd2value(mc, val); + + mc->update[chan / 8] |= (1 << (chan % 8)); + mc->wData[chan] = val; + + /* start the transfer, if not already started */ + + usb2_transfer_start(sc->sc_mixer_xfer[0]); + + return; +} + +static void +uaudio_mixer_init(struct uaudio_softc *sc) +{ + struct uaudio_mixer_node *mc; + int32_t i; + + for (mc = sc->sc_mixer_root; mc; + mc = mc->next) { + + if (mc->ctl != SOUND_MIXER_NRDEVICES) { + /* + * Set device mask bits. See + * /usr/include/machine/soundcard.h + */ + sc->sc_mix_info |= (1 << mc->ctl); + } + if ((mc->ctl == SOUND_MIXER_NRDEVICES) && + (mc->type == MIX_SELECTOR)) { + + for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { + if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) { + continue; + } + sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1]; + } + } + } + return; +} + +int +uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m) +{ + DPRINTF("\n"); + + if (usb2_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index, + sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc, + mixer_get_lock(m))) { + DPRINTFN(0, "could not allocate USB " + "transfer for audio mixer!\n"); + return (ENOMEM); + } + if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) { + mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + } + mix_setdevs(m, sc->sc_mix_info); + mix_setrecdevs(m, sc->sc_recsrc_info); + return (0); +} + +int +uaudio_mixer_uninit_sub(struct uaudio_softc *sc) +{ + DPRINTF("\n"); + + usb2_transfer_unsetup(sc->sc_mixer_xfer, 1); + + return (0); +} + +void +uaudio_mixer_set(struct uaudio_softc *sc, unsigned type, + unsigned left, unsigned right) +{ + struct uaudio_mixer_node *mc; + + for (mc = sc->sc_mixer_root; mc; + mc = mc->next) { + + if (mc->ctl == type) { + if (mc->nchan == 2) { + /* set Right */ + uaudio_mixer_ctl_set(sc, mc, 1, (int)(right * 255) / 100); + } + /* set Left or Mono */ + uaudio_mixer_ctl_set(sc, mc, 0, (int)(left * 255) / 100); + } + } + return; +} + +uint32_t +uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src) +{ + struct uaudio_mixer_node *mc; + uint32_t mask; + uint32_t temp; + int32_t i; + + for (mc = sc->sc_mixer_root; mc; + mc = mc->next) { + + if ((mc->ctl == SOUND_MIXER_NRDEVICES) && + (mc->type == MIX_SELECTOR)) { + + /* compute selector mask */ + + mask = 0; + for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { + mask |= (1 << mc->slctrtype[i - 1]); + } + + temp = mask & src; + if (temp == 0) { + continue; + } + /* find the first set bit */ + temp = (-temp) & temp; + + /* update "src" */ + src &= ~mask; + src |= temp; + + for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { + if (temp != (1 << mc->slctrtype[i - 1])) { + continue; + } + uaudio_mixer_ctl_set(sc, mc, 0, i); + break; + } + } + } + return (src); +} + +/*========================================================================* + * MIDI support routines + *========================================================================*/ + +static void +umidi_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct usb2_xfer *xfer_other = chan->xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + chan->flags &= ~UMIDI_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +umidi_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct umidi_sub_chan *sub; + uint8_t buf[1]; + uint8_t cmd_len; + uint8_t cn; + uint16_t pos; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("actlen=%d bytes\n", xfer->actlen); + + if (xfer->actlen == 0) { + /* should not happen */ + goto tr_error; + } + pos = 0; + + while (xfer->actlen >= 4) { + + usb2_copy_out(xfer->frbuffers, pos, buf, 1); + + cmd_len = umidi_cmd_to_len[buf[0] & 0xF]; /* command length */ + cn = buf[0] >> 4; /* cable number */ + sub = &chan->sub[cn]; + + if (cmd_len && (cn < chan->max_cable) && sub->read_open) { + usb2_fifo_put_data(sub->fifo.fp[USB_FIFO_RX], xfer->frbuffers, + pos + 1, cmd_len, 1); + } else { + /* ignore the command */ + } + + xfer->actlen -= 4; + pos += 4; + } + + case USB_ST_SETUP: + DPRINTF("start\n"); + + if (chan->flags & UMIDI_FLAG_READ_STALL) { + usb2_transfer_start(chan->xfer[3]); + return; + } + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: +tr_error: + + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + chan->flags |= UMIDI_FLAG_READ_STALL; + usb2_transfer_start(chan->xfer[3]); + } + return; + + } +} + +static void +umidi_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct usb2_xfer *xfer_other = chan->xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + chan->flags &= ~UMIDI_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/* + * The following statemachine, that converts MIDI commands to + * USB MIDI packets, derives from Linux's usbmidi.c, which + * was written by "Clemens Ladisch": + * + * Returns: + * 0: No command + * Else: Command is complete + */ +static uint8_t +umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b) +{ + uint8_t p0 = (cn << 4); + + if (b >= 0xf8) { + sub->temp_0[0] = p0 | 0x0f; + sub->temp_0[1] = b; + sub->temp_0[2] = 0; + sub->temp_0[3] = 0; + sub->temp_cmd = sub->temp_0; + return (1); + + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: /* system exclusive begin */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_SYSEX_1; + break; + case 0xf1: /* MIDI time code */ + case 0xf3: /* song select */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_1PARAM; + break; + case 0xf2: /* song position pointer */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_2PARAM_1; + break; + case 0xf4: /* unknown */ + case 0xf5: /* unknown */ + sub->state = UMIDI_ST_UNKNOWN; + break; + case 0xf6: /* tune request */ + sub->temp_1[0] = p0 | 0x05; + sub->temp_1[1] = 0xf6; + sub->temp_1[2] = 0; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return (1); + + case 0xf7: /* system exclusive end */ + switch (sub->state) { + case UMIDI_ST_SYSEX_0: + sub->temp_1[0] = p0 | 0x05; + sub->temp_1[1] = 0xf7; + sub->temp_1[2] = 0; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return (1); + case UMIDI_ST_SYSEX_1: + sub->temp_1[0] = p0 | 0x06; + sub->temp_1[2] = 0xf7; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return (1); + case UMIDI_ST_SYSEX_2: + sub->temp_1[0] = p0 | 0x07; + sub->temp_1[3] = 0xf7; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return (1); + } + sub->state = UMIDI_ST_UNKNOWN; + break; + } + } else if (b >= 0x80) { + sub->temp_1[1] = b; + if ((b >= 0xc0) && (b <= 0xdf)) { + sub->state = UMIDI_ST_1PARAM; + } else { + sub->state = UMIDI_ST_2PARAM_1; + } + } else { /* b < 0x80 */ + switch (sub->state) { + case UMIDI_ST_1PARAM: + if (sub->temp_1[1] < 0xf0) { + p0 |= sub->temp_1[1] >> 4; + } else { + p0 |= 0x02; + sub->state = UMIDI_ST_UNKNOWN; + } + sub->temp_1[0] = p0; + sub->temp_1[2] = b; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + return (1); + case UMIDI_ST_2PARAM_1: + sub->temp_1[2] = b; + sub->state = UMIDI_ST_2PARAM_2; + break; + case UMIDI_ST_2PARAM_2: + if (sub->temp_1[1] < 0xf0) { + p0 |= sub->temp_1[1] >> 4; + sub->state = UMIDI_ST_2PARAM_1; + } else { + p0 |= 0x03; + sub->state = UMIDI_ST_UNKNOWN; + } + sub->temp_1[0] = p0; + sub->temp_1[3] = b; + sub->temp_cmd = sub->temp_1; + return (1); + case UMIDI_ST_SYSEX_0: + sub->temp_1[1] = b; + sub->state = UMIDI_ST_SYSEX_1; + break; + case UMIDI_ST_SYSEX_1: + sub->temp_1[2] = b; + sub->state = UMIDI_ST_SYSEX_2; + break; + case UMIDI_ST_SYSEX_2: + sub->temp_1[0] = p0 | 0x04; + sub->temp_1[3] = b; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_SYSEX_0; + return (1); + } + } + return (0); +} + +static void +umidi_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct umidi_sub_chan *sub; + uint32_t actlen; + uint16_t total_length; + uint8_t buf; + uint8_t start_cable; + uint8_t tr_any; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("actlen=%d bytes\n", xfer->actlen); + + case USB_ST_SETUP: + + DPRINTF("start\n"); + + if (chan->flags & UMIDI_FLAG_WRITE_STALL) { + usb2_transfer_start(chan->xfer[2]); + return; + } + total_length = 0; /* reset */ + + start_cable = chan->curr_cable; + + tr_any = 0; + + while (1) { + + /* round robin de-queueing */ + + sub = &chan->sub[chan->curr_cable]; + + if (sub->write_open) { + usb2_fifo_get_data(sub->fifo.fp[USB_FIFO_TX], + xfer->frbuffers, total_length, + 1, &actlen, 0); + } else { + actlen = 0; + } + + if (actlen) { + usb2_copy_out(xfer->frbuffers, total_length, &buf, 1); + + tr_any = 1; + + DPRINTF("byte=0x%02x\n", buf); + + if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) { + + DPRINTF("sub= %02x %02x %02x %02x\n", + sub->temp_cmd[0], sub->temp_cmd[1], + sub->temp_cmd[2], sub->temp_cmd[3]); + + usb2_copy_in(xfer->frbuffers, total_length, + sub->temp_cmd, 4); + + total_length += 4; + + if (total_length >= UMIDI_BULK_SIZE) { + break; + } + } else { + continue; + } + } + chan->curr_cable++; + if (chan->curr_cable >= chan->max_cable) { + chan->curr_cable = 0; + } + if (chan->curr_cable == start_cable) { + if (tr_any == 0) { + break; + } + tr_any = 0; + } + } + + if (total_length) { + xfer->frlengths[0] = total_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + chan->flags |= UMIDI_FLAG_WRITE_STALL; + usb2_transfer_start(chan->xfer[2]); + } + return; + + } +} + +static struct umidi_sub_chan * +umidi_sub_by_fifo(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + struct umidi_sub_chan *sub; + uint32_t n; + + for (n = 0; n < UMIDI_CABLES_MAX; n++) { + sub = &chan->sub[n]; + if ((sub->fifo.fp[USB_FIFO_RX] == fifo) || + (sub->fifo.fp[USB_FIFO_TX] == fifo)) { + return (sub); + } + } + + panic("%s:%d cannot find usb2_fifo!\n", + __FILE__, __LINE__); + + return (NULL); +} + +static void +umidi_start_read(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + + usb2_transfer_start(chan->xfer[1]); + return; +} + +static void +umidi_stop_read(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); + + DPRINTF("\n"); + + sub->read_open = 0; + + if (--(chan->read_open_refcount) == 0) { + /* + * XXX don't stop the read transfer here, hence that causes + * problems with some MIDI adapters + */ + DPRINTF("(stopping read transfer)\n"); + } + return; +} + +static void +umidi_start_write(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + + usb2_transfer_start(chan->xfer[0]); + return; +} + +static void +umidi_stop_write(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); + + DPRINTF("\n"); + + sub->write_open = 0; + + if (--(chan->write_open_refcount) == 0) { + DPRINTF("(stopping write transfer)\n"); + usb2_transfer_stop(chan->xfer[2]); + usb2_transfer_stop(chan->xfer[0]); + } + return; +} + +static int +umidi_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct umidi_chan *chan = fifo->priv_sc0; + struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); + + if (fflags & FREAD) { + if (usb2_fifo_alloc_buffer(fifo, 4, (1024 / 4))) { + return (ENOMEM); + } + mtx_lock(fifo->priv_mtx); + chan->read_open_refcount++; + sub->read_open = 1; + mtx_unlock(fifo->priv_mtx); + } + if (fflags & FWRITE) { + if (usb2_fifo_alloc_buffer(fifo, 32, (1024 / 32))) { + return (ENOMEM); + } + /* clear stall first */ + mtx_lock(fifo->priv_mtx); + chan->flags |= UMIDI_FLAG_WRITE_STALL; + chan->write_open_refcount++; + sub->write_open = 1; + + /* reset */ + sub->state = UMIDI_ST_UNKNOWN; + mtx_unlock(fifo->priv_mtx); + } + return (0); /* success */ +} + +static void +umidi_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & FREAD) { + usb2_fifo_free_buffer(fifo); + } + if (fflags & FWRITE) { + usb2_fifo_free_buffer(fifo); + } + return; +} + + +static int +umidi_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, + int fflags, struct thread *td) +{ + return (ENODEV); +} + +static void +umidi_init(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct umidi_chan *chan = &sc->sc_midi_chan; + + mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE); + return; +} + +static struct usb2_fifo_methods umidi_fifo_methods = { + .f_start_read = &umidi_start_read, + .f_start_write = &umidi_start_write, + .f_stop_read = &umidi_stop_read, + .f_stop_write = &umidi_stop_write, + .f_open = &umidi_open, + .f_close = &umidi_close, + .f_ioctl = &umidi_ioctl, + .basename[0] = "umidi", +}; + +static int32_t +umidi_probe(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umidi_chan *chan = &sc->sc_midi_chan; + struct umidi_sub_chan *sub; + int unit = device_get_unit(dev); + int error; + uint32_t n; + + if (usb2_set_alt_interface_index(sc->sc_udev, chan->iface_index, + chan->iface_alt_index)) { + DPRINTF("setting of alternate index failed!\n"); + goto detach; + } + usb2_set_parent_iface(sc->sc_udev, chan->iface_index, sc->sc_mixer_iface_index); + + error = usb2_transfer_setup(uaa->device, &chan->iface_index, + chan->xfer, umidi_config, UMIDI_N_TRANSFER, + chan, &chan->mtx); + if (error) { + DPRINTF("error=%s\n", usb2_errstr(error)); + goto detach; + } + if ((chan->max_cable > UMIDI_CABLES_MAX) || + (chan->max_cable == 0)) { + chan->max_cable = UMIDI_CABLES_MAX; + } + /* set interface permissions */ + usb2_set_iface_perm(sc->sc_udev, chan->iface_index, + UID_ROOT, GID_OPERATOR, 0644); + + for (n = 0; n < chan->max_cable; n++) { + + sub = &chan->sub[n]; + + error = usb2_fifo_attach(sc->sc_udev, chan, &chan->mtx, + &umidi_fifo_methods, &sub->fifo, unit, n, + chan->iface_index); + if (error) { + goto detach; + } + } + + mtx_lock(&chan->mtx); + + /* clear stall first */ + chan->flags |= UMIDI_FLAG_READ_STALL; + + /* + * NOTE: at least one device will not work properly unless + * the BULK pipe is open all the time. + */ + usb2_transfer_start(chan->xfer[1]); + + mtx_unlock(&chan->mtx); + + return (0); /* success */ + +detach: + return (ENXIO); /* failure */ +} + +static int32_t +umidi_detach(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct umidi_chan *chan = &sc->sc_midi_chan; + uint32_t n; + + for (n = 0; n < UMIDI_CABLES_MAX; n++) { + usb2_fifo_detach(&chan->sub[n].fifo); + } + + mtx_lock(&chan->mtx); + + usb2_transfer_stop(chan->xfer[3]); + usb2_transfer_stop(chan->xfer[1]); + + mtx_unlock(&chan->mtx); + + usb2_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER); + + mtx_destroy(&chan->mtx); + + return (0); +} + +DRIVER_MODULE(uaudio, ushub, uaudio_driver, uaudio_devclass, NULL, 0); +MODULE_DEPEND(uaudio, usb2_sound, 1, 1, 1); +MODULE_DEPEND(uaudio, usb2_core, 1, 1, 1); +MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(uaudio, 1); diff --git a/sys/dev/usb2/sound/uaudio2.h b/sys/dev/usb2/sound/uaudio2.h new file mode 100644 index 000000000000..f01d1c70937f --- /dev/null +++ b/sys/dev/usb2/sound/uaudio2.h @@ -0,0 +1,55 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2000-2002 Hiroyuki Aizu + * + * 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. + */ + +/* prototypes from "uaudio.c" used by "uaudio_pcm.c" */ + +struct uaudio_chan; +struct uaudio_softc; +struct snd_dbuf; +struct snd_mixer; +struct pcm_channel; + +extern int uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class); +extern int uaudio_detach_sub(device_t dev); +extern void *uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, struct pcm_channel *c, int dir); +extern int uaudio_chan_free(struct uaudio_chan *ch); +extern int uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize); +extern int uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, uint32_t blockcount); +extern int uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed); +extern int uaudio_chan_getptr(struct uaudio_chan *ch); +extern struct pcmchan_caps *uaudio_chan_getcaps(struct uaudio_chan *ch); +extern int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format); +extern int uaudio_chan_start(struct uaudio_chan *ch); +extern int uaudio_chan_stop(struct uaudio_chan *ch); +extern int uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m); +extern int uaudio_mixer_uninit_sub(struct uaudio_softc *sc); +extern void uaudio_mixer_set(struct uaudio_softc *sc, unsigned type, unsigned left, unsigned right); +extern uint32_t uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src); + +int uaudio_get_vendor(device_t dev); +int uaudio_get_product(device_t dev); +int uaudio_get_release(device_t dev); diff --git a/sys/dev/usb2/sound/uaudio2_pcm.c b/sys/dev/usb2/sound/uaudio2_pcm.c new file mode 100644 index 000000000000..c073c19d3644 --- /dev/null +++ b/sys/dev/usb2/sound/uaudio2_pcm.c @@ -0,0 +1,234 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2000-2002 Hiroyuki Aizu + * Copyright (c) 2006 Hans Petter Selasky + * + * 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 +#include +#include + +#include + +#include "mixer_if.h" + +/************************************************************/ +static void * +ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + return (uaudio_chan_init(devinfo, b, c, dir)); +} + +static int +ua_chan_free(kobj_t obj, void *data) +{ + return (uaudio_chan_free(data)); +} + +static int +ua_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + /* + * At this point, no need to query as we + * shouldn't select an unsorted format + */ + return (uaudio_chan_set_param_format(data, format)); +} + +static int +ua_chan_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + return (uaudio_chan_set_param_speed(data, speed)); +} + +static int +ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +{ + return (uaudio_chan_set_param_blocksize(data, blocksize)); +} + +static int +ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount) +{ + return (uaudio_chan_set_param_fragments(data, blocksize, blockcount)); +} + +static int +ua_chan_trigger(kobj_t obj, void *data, int go) +{ + if (!PCMTRIG_COMMON(go)) { + return (0); + } + if (go == PCMTRIG_START) { + return (uaudio_chan_start(data)); + } else { + return (uaudio_chan_stop(data)); + } +} + +static int +ua_chan_getptr(kobj_t obj, void *data) +{ + return (uaudio_chan_getptr(data)); +} + +static struct pcmchan_caps * +ua_chan_getcaps(kobj_t obj, void *data) +{ + return (uaudio_chan_getcaps(data)); +} + +static kobj_method_t ua_chan_methods[] = { + KOBJMETHOD(channel_init, ua_chan_init), + KOBJMETHOD(channel_free, ua_chan_free), + KOBJMETHOD(channel_setformat, ua_chan_setformat), + KOBJMETHOD(channel_setspeed, ua_chan_setspeed), + KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize), + KOBJMETHOD(channel_setfragments, ua_chan_setfragments), + KOBJMETHOD(channel_trigger, ua_chan_trigger), + KOBJMETHOD(channel_getptr, ua_chan_getptr), + KOBJMETHOD(channel_getcaps, ua_chan_getcaps), + {0, 0} +}; + +CHANNEL_DECLARE(ua_chan); + +/************************************************************/ +static int +ua_mixer_init(struct snd_mixer *m) +{ + return (uaudio_mixer_init_sub(mix_getdevinfo(m), m)); +} + +static int +ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right) +{ + struct mtx *mtx = mixer_get_lock(m); + uint8_t do_unlock; + + if (mtx_owned(mtx)) { + do_unlock = 0; + } else { + do_unlock = 1; + mtx_lock(mtx); + } + uaudio_mixer_set(mix_getdevinfo(m), type, left, right); + if (do_unlock) { + mtx_unlock(mtx); + } + return (left | (right << 8)); +} + +static int +ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct mtx *mtx = mixer_get_lock(m); + int retval; + uint8_t do_unlock; + + if (mtx_owned(mtx)) { + do_unlock = 0; + } else { + do_unlock = 1; + mtx_lock(mtx); + } + retval = uaudio_mixer_setrecsrc(mix_getdevinfo(m), src); + if (do_unlock) { + mtx_unlock(mtx); + } + return (retval); +} + +static int +ua_mixer_uninit(struct snd_mixer *m) +{ + return (uaudio_mixer_uninit_sub(mix_getdevinfo(m))); +} + +static kobj_method_t ua_mixer_methods[] = { + KOBJMETHOD(mixer_init, ua_mixer_init), + KOBJMETHOD(mixer_uninit, ua_mixer_uninit), + KOBJMETHOD(mixer_set, ua_mixer_set), + KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc), + + {0, 0} +}; + +MIXER_DECLARE(ua_mixer); +/************************************************************/ + + +static int +ua_probe(device_t dev) +{ + struct sndcard_func *func; + + /* the parent device has already been probed */ + + func = device_get_ivars(dev); + + if ((func == NULL) || + (func->func != SCF_PCM)) { + return (ENXIO); + } + device_set_desc(dev, "USB audio"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ua_attach(device_t dev) +{ + return (uaudio_attach_sub(dev, &ua_mixer_class, &ua_chan_class)); +} + +static int +ua_detach(device_t dev) +{ + return (uaudio_detach_sub(dev)); +} + +/************************************************************/ + +static device_method_t ua_pcm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ua_probe), + DEVMETHOD(device_attach, ua_attach), + DEVMETHOD(device_detach, ua_detach), + + {0, 0} +}; + +static driver_t ua_pcm_driver = { + "pcm", + ua_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1); +MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(ua_pcm, 1); diff --git a/sys/dev/usb2/sound/uaudio2_reg.h b/sys/dev/usb2/sound/uaudio2_reg.h new file mode 100644 index 000000000000..2bf68a17c609 --- /dev/null +++ b/sys/dev/usb2/sound/uaudio2_reg.h @@ -0,0 +1,406 @@ +/* $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $ */ +/* $FreeBSD$ */ + +/*- + * 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. + */ + +#define UAUDIO_VERSION 0x100 + +#define UDESC_CS_CONFIG 0x22 +#define UDESC_CS_STRING 0x23 +#define UDESC_CS_INTERFACE 0x24 +#define UDESC_CS_ENDPOINT 0x25 + +#define UDESCSUB_AC_HEADER 1 +#define UDESCSUB_AC_INPUT 2 +#define UDESCSUB_AC_OUTPUT 3 +#define UDESCSUB_AC_MIXER 4 +#define UDESCSUB_AC_SELECTOR 5 +#define UDESCSUB_AC_FEATURE 6 +#define UDESCSUB_AC_PROCESSING 7 +#define UDESCSUB_AC_EXTENSION 8 + +/* The first fields are identical to struct usb2_endpoint_descriptor */ +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bEndpointAddress; + uByte bmAttributes; + uWord wMaxPacketSize; + uByte bInterval; + /* + * The following two entries are only used by the Audio Class. + * And according to the specs the Audio Class is the only one + * allowed to extend the endpoint descriptor. + * Who knows what goes on in the minds of the people in the USB + * standardization? :-( + */ + uByte bRefresh; + uByte bSynchAddress; +} __packed usb2_endpoint_descriptor_audio_t; + +struct usb2_audio_control_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdADC; + uWord wTotalLength; + uByte bInCollection; + uByte baInterfaceNr[1]; +} __packed; + +struct usb2_audio_streaming_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalLink; + uByte bDelay; + uWord wFormatTag; +} __packed; + +struct usb2_audio_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmAttributes; +#define UA_SED_FREQ_CONTROL 0x01 +#define UA_SED_PITCH_CONTROL 0x02 +#define UA_SED_MAXPACKETSONLY 0x80 + uByte bLockDelayUnits; + uWord wLockDelay; +} __packed; + +struct usb2_audio_streaming_type1_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bFormatType; + uByte bNrChannels; + uByte bSubFrameSize; + uByte bBitResolution; + uByte bSamFreqType; +#define UA_SAMP_CONTNUOUS 0 + uByte tSamFreq[0]; +#define UA_GETSAMP(p, n) (((p)->tSamFreq[((n)*3)+0]) | \ + ((p)->tSamFreq[((n)*3)+1] << 8) | \ + ((p)->tSamFreq[((n)*3)+2] << 16)) +#define UA_SAMP_LO(p) UA_GETSAMP(p, 0) +#define UA_SAMP_HI(p) UA_GETSAMP(p, 1) +} __packed; + +struct usb2_audio_cluster { + uByte bNrChannels; + uWord wChannelConfig; +#define UA_CHANNEL_LEFT 0x0001 +#define UA_CHANNEL_RIGHT 0x0002 +#define UA_CHANNEL_CENTER 0x0004 +#define UA_CHANNEL_LFE 0x0008 +#define UA_CHANNEL_L_SURROUND 0x0010 +#define UA_CHANNEL_R_SURROUND 0x0020 +#define UA_CHANNEL_L_CENTER 0x0040 +#define UA_CHANNEL_R_CENTER 0x0080 +#define UA_CHANNEL_SURROUND 0x0100 +#define UA_CHANNEL_L_SIDE 0x0200 +#define UA_CHANNEL_R_SIDE 0x0400 +#define UA_CHANNEL_TOP 0x0800 + uByte iChannelNames; +} __packed; + +/* Shared by all units and terminals */ +struct usb2_audio_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; +}; + +/* UDESCSUB_AC_INPUT */ +struct usb2_audio_input_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; +/* uByte iTerminal; */ +} __packed; + +/* UDESCSUB_AC_OUTPUT */ +struct usb2_audio_output_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte iTerminal; +} __packed; + +/* UDESCSUB_AC_MIXER */ +struct usb2_audio_mixer_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[0]; /* [bNrInPins] */ + /* struct usb2_audio_mixer_unit_1 */ +} __packed; +struct usb2_audio_mixer_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bmControls[0]; /* [see source code] */ + /* uByte iMixer; */ +} __packed; + +/* UDESCSUB_AC_SELECTOR */ +struct usb2_audio_selector_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[0]; /* [bNrInPins] */ + /* uByte iSelector; */ +} __packed; + +/* UDESCSUB_AC_FEATURE */ +struct usb2_audio_feature_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uByte bControlSize; + uByte bmaControls[0]; /* [bControlSize * x] */ + /* uByte iFeature; */ +} __packed; + +/* UDESCSUB_AC_PROCESSING */ +struct usb2_audio_processing_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wProcessType; + uByte bNrInPins; + uByte baSourceId[0]; /* [bNrInPins] */ + /* struct usb2_audio_processing_unit_1 */ +} __packed; +struct usb2_audio_processing_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[0]; /* [bControlSize] */ +#define UA_PROC_ENABLE_MASK 1 +} __packed; + +struct usb2_audio_processing_unit_updown { + uByte iProcessing; + uByte bNrModes; + uWord waModes[0]; /* [bNrModes] */ +} __packed; + +/* UDESCSUB_AC_EXTENSION */ +struct usb2_audio_extension_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wExtensionCode; + uByte bNrInPins; + uByte baSourceId[0]; /* [bNrInPins] */ + /* struct usb2_audio_extension_unit_1 */ +} __packed; +struct usb2_audio_extension_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[0]; /* [bControlSize] */ +#define UA_EXT_ENABLE_MASK 1 +#define UA_EXT_ENABLE 1 + /* uByte iExtension; */ +} __packed; + +/* USB terminal types */ +#define UAT_UNDEFINED 0x0100 +#define UAT_STREAM 0x0101 +#define UAT_VENDOR 0x01ff +/* input terminal types */ +#define UATI_UNDEFINED 0x0200 +#define UATI_MICROPHONE 0x0201 +#define UATI_DESKMICROPHONE 0x0202 +#define UATI_PERSONALMICROPHONE 0x0203 +#define UATI_OMNIMICROPHONE 0x0204 +#define UATI_MICROPHONEARRAY 0x0205 +#define UATI_PROCMICROPHONEARR 0x0206 +/* output terminal types */ +#define UATO_UNDEFINED 0x0300 +#define UATO_SPEAKER 0x0301 +#define UATO_HEADPHONES 0x0302 +#define UATO_DISPLAYAUDIO 0x0303 +#define UATO_DESKTOPSPEAKER 0x0304 +#define UATO_ROOMSPEAKER 0x0305 +#define UATO_COMMSPEAKER 0x0306 +#define UATO_SUBWOOFER 0x0307 +/* bidir terminal types */ +#define UATB_UNDEFINED 0x0400 +#define UATB_HANDSET 0x0401 +#define UATB_HEADSET 0x0402 +#define UATB_SPEAKERPHONE 0x0403 +#define UATB_SPEAKERPHONEESUP 0x0404 +#define UATB_SPEAKERPHONEECANC 0x0405 +/* telephony terminal types */ +#define UATT_UNDEFINED 0x0500 +#define UATT_PHONELINE 0x0501 +#define UATT_TELEPHONE 0x0502 +#define UATT_DOWNLINEPHONE 0x0503 +/* external terminal types */ +#define UATE_UNDEFINED 0x0600 +#define UATE_ANALOGCONN 0x0601 +#define UATE_DIGITALAUIFC 0x0602 +#define UATE_LINECONN 0x0603 +#define UATE_LEGACYCONN 0x0604 +#define UATE_SPDIF 0x0605 +#define UATE_1394DA 0x0606 +#define UATE_1394DV 0x0607 +/* embedded function terminal types */ +#define UATF_UNDEFINED 0x0700 +#define UATF_CALIBNOISE 0x0701 +#define UATF_EQUNOISE 0x0702 +#define UATF_CDPLAYER 0x0703 +#define UATF_DAT 0x0704 +#define UATF_DCC 0x0705 +#define UATF_MINIDISK 0x0706 +#define UATF_ANALOGTAPE 0x0707 +#define UATF_PHONOGRAPH 0x0708 +#define UATF_VCRAUDIO 0x0709 +#define UATF_VIDEODISCAUDIO 0x070a +#define UATF_DVDAUDIO 0x070b +#define UATF_TVTUNERAUDIO 0x070c +#define UATF_SATELLITE 0x070d +#define UATF_CABLETUNER 0x070e +#define UATF_DSS 0x070f +#define UATF_RADIORECV 0x0710 +#define UATF_RADIOXMIT 0x0711 +#define UATF_MULTITRACK 0x0712 +#define UATF_SYNTHESIZER 0x0713 + + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AGC_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +#define FU_MASK(u) (1 << ((u)-1)) + +#define MASTER_CHAN 0 + +#define AS_GENERAL 1 +#define FORMAT_TYPE 2 +#define FORMAT_SPECIFIC 3 + +#define UA_FMT_PCM 1 +#define UA_FMT_PCM8 2 +#define UA_FMT_IEEE_FLOAT 3 +#define UA_FMT_ALAW 4 +#define UA_FMT_MULAW 5 +#define UA_FMT_MPEG 0x1001 +#define UA_FMT_AC3 0x1002 + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 + +#define FORMAT_TYPE_UNDEFINED 0 +#define FORMAT_TYPE_I 1 +#define FORMAT_TYPE_II 2 +#define FORMAT_TYPE_III 3 + +#define UA_PROC_MASK(n) (1<< ((n)-1)) +#define PROCESS_UNDEFINED 0 +#define XX_ENABLE_CONTROL 1 +#define UPDOWNMIX_PROCESS 1 +#define UD_ENABLE_CONTROL 1 +#define UD_MODE_SELECT_CONTROL 2 +#define DOLBY_PROLOGIC_PROCESS 2 +#define DP_ENABLE_CONTROL 1 +#define DP_MODE_SELECT_CONTROL 2 +#define P3D_STEREO_EXTENDER_PROCESS 3 +#define P3D_ENABLE_CONTROL 1 +#define P3D_SPACIOUSNESS_CONTROL 2 +#define REVERBATION_PROCESS 4 +#define RV_ENABLE_CONTROL 1 +#define RV_LEVEL_CONTROL 2 +#define RV_TIME_CONTROL 3 +#define RV_FEEDBACK_CONTROL 4 +#define CHORUS_PROCESS 5 +#define CH_ENABLE_CONTROL 1 +#define CH_LEVEL_CONTROL 2 +#define CH_RATE_CONTROL 3 +#define CH_DEPTH_CONTROL 4 +#define DYN_RANGE_COMP_PROCESS 6 +#define DR_ENABLE_CONTROL 1 +#define DR_COMPRESSION_RATE_CONTROL 2 +#define DR_MAXAMPL_CONTROL 3 +#define DR_THRESHOLD_CONTROL 4 +#define DR_ATTACK_TIME_CONTROL 5 +#define DR_RELEASE_TIME_CONTROL 6 diff --git a/sys/dev/usb2/sound/usb2_sound.c b/sys/dev/usb2/sound/usb2_sound.c new file mode 100644 index 000000000000..0e6994baaa21 --- /dev/null +++ b/sys/dev/usb2/sound/usb2_sound.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_sound, 1); +MODULE_DEPEND(usb2_sound, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/sound/usb2_sound.h b/sys/dev/usb2/sound/usb2_sound.h new file mode 100644 index 000000000000..5b6ae152c4b8 --- /dev/null +++ b/sys/dev/usb2/sound/usb2_sound.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_SOUND_H_ +#define _USB2_SOUND_H_ + +#endif /* _USB2_SOUND_H_ */ diff --git a/sys/dev/usb2/storage/ata-usb2.c b/sys/dev/usb2/storage/ata-usb2.c new file mode 100644 index 000000000000..17a63c62135f --- /dev/null +++ b/sys/dev/usb2/storage/ata-usb2.c @@ -0,0 +1,1114 @@ +/*- + * Copyright (c) 2006 - 2008 Søren Schmidt + * All rights reserved. + * + * Copyright (c) 2006 Hans Petter Selasky + * 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. + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define ATAUSB_BULK_SIZE (1<<17) + +/* Command Block Wrapper */ +struct bbb_cbw { + uint8_t signature[4]; +#define CBWSIGNATURE 0x43425355 + + uint8_t tag[4]; + uint8_t transfer_length[4]; + uint8_t flags; +#define CBWFLAGS_OUT 0x00 +#define CBWFLAGS_IN 0x80 + + uint8_t lun; + uint8_t length; +#define CBWCDBLENGTH 16 + + uint8_t cdb[CBWCDBLENGTH]; +} __packed; + +/* Command Status Wrapper */ +struct bbb_csw { + uint8_t signature[4]; +#define CSWSIGNATURE 0x53425355 + + uint8_t tag[4]; + uint8_t residue[4]; + uint8_t status; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} __packed; + +/* USB-ATA 'controller' softc */ +struct atausb2_softc { + struct bbb_cbw cbw; + struct bbb_csw csw; + struct mtx locked_mtx; + + struct ata_channel *locked_ch; + struct ata_channel *restart_ch; + struct ata_request *ata_request; + +#define ATAUSB_T_BBB_RESET1 0 +#define ATAUSB_T_BBB_RESET2 1 +#define ATAUSB_T_BBB_RESET3 2 +#define ATAUSB_T_BBB_COMMAND 3 +#define ATAUSB_T_BBB_DATA_READ 4 +#define ATAUSB_T_BBB_DATA_RD_CS 5 +#define ATAUSB_T_BBB_DATA_WRITE 6 +#define ATAUSB_T_BBB_DATA_WR_CS 7 +#define ATAUSB_T_BBB_STATUS 8 +#define ATAUSB_T_BBB_MAX 9 + +#define ATAUSB_T_MAX ATAUSB_T_BBB_MAX + + struct usb2_xfer *xfer[ATAUSB_T_MAX]; + caddr_t ata_data; + device_t dev; + + uint32_t timeout; + uint32_t ata_donecount; + uint32_t ata_bytecount; + + uint8_t last_xfer_no; + uint8_t usb2_speed; + uint8_t intr_stalled; + uint8_t maxlun; + uint8_t iface_no; + uint8_t status_try; +}; + +static const int atausbdebug = 0; + +/* prototypes */ + +static device_probe_t atausb2_probe; +static device_attach_t atausb2_attach; +static device_detach_t atausb2_detach; + +static usb2_callback_t atausb2_t_bbb_reset1_callback; +static usb2_callback_t atausb2_t_bbb_reset2_callback; +static usb2_callback_t atausb2_t_bbb_reset3_callback; +static usb2_callback_t atausb2_t_bbb_command_callback; +static usb2_callback_t atausb2_t_bbb_data_read_callback; +static usb2_callback_t atausb2_t_bbb_data_rd_cs_callback; +static usb2_callback_t atausb2_t_bbb_data_write_callback; +static usb2_callback_t atausb2_t_bbb_data_wr_cs_callback; +static usb2_callback_t atausb2_t_bbb_status_callback; +static usb2_callback_t atausb2_tr_error; + +static void atausb2_cancel_request(struct atausb2_softc *sc); +static void atausb2_transfer_start(struct atausb2_softc *sc, uint8_t xfer_no); +static void atausb2_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer); +static int ata_usbchannel_begin_transaction(struct ata_request *request); +static int ata_usbchannel_end_transaction(struct ata_request *request); + +static device_probe_t ata_usbchannel_probe; +static device_attach_t ata_usbchannel_attach; +static device_detach_t ata_usbchannel_detach; + +static ata_setmode_t ata_usbchannel_setmode; +static ata_locking_t ata_usbchannel_locking; + +/* + * USB frontend part + */ + +struct usb2_config atausb2_config[ATAUSB_T_BBB_MAX] = { + + [ATAUSB_T_BBB_RESET1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_reset1_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 500, /* 500 milliseconds */ + }, + + [ATAUSB_T_BBB_RESET2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_reset2_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [ATAUSB_T_BBB_RESET3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_reset3_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [ATAUSB_T_BBB_COMMAND] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = sizeof(struct bbb_cbw), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_command_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [ATAUSB_T_BBB_DATA_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = ATAUSB_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &atausb2_t_bbb_data_read_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [ATAUSB_T_BBB_DATA_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_data_rd_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [ATAUSB_T_BBB_DATA_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = ATAUSB_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .mh.callback = &atausb2_t_bbb_data_write_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [ATAUSB_T_BBB_DATA_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &atausb2_t_bbb_data_wr_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [ATAUSB_T_BBB_STATUS] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = sizeof(struct bbb_csw), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &atausb2_t_bbb_status_callback, + .mh.timeout = 5000, /* ms */ + }, +}; + +static devclass_t atausb2_devclass; + +static device_method_t atausb2_methods[] = { + DEVMETHOD(device_probe, atausb2_probe), + DEVMETHOD(device_attach, atausb2_attach), + DEVMETHOD(device_detach, atausb2_detach), + {0, 0} +}; + +static driver_t atausb2_driver = { + .name = "atausb", + .methods = atausb2_methods, + .size = sizeof(struct atausb2_softc), +}; + +DRIVER_MODULE(atausb, ushub, atausb2_driver, atausb2_devclass, 0, 0); +MODULE_DEPEND(atausb, usb2_storage, 1, 1, 1); +MODULE_DEPEND(atausb, usb2_core, 1, 1, 1); +MODULE_VERSION(atausb, 1); + +static int +atausb2_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->use_generic == 0) { + /* give other drivers a try first */ + return (ENXIO); + } + id = usb2_get_interface_descriptor(uaa->iface); + if ((!id) || (id->bInterfaceClass != UICLASS_MASS)) { + return (ENXIO); + } + switch (id->bInterfaceSubClass) { + case UISUBCLASS_QIC157: + case UISUBCLASS_RBC: + case UISUBCLASS_SCSI: + case UISUBCLASS_SFF8020I: + case UISUBCLASS_SFF8070I: + case UISUBCLASS_UFI: + switch (id->bInterfaceProtocol) { + case UIPROTO_MASS_CBI: + case UIPROTO_MASS_CBI_I: + case UIPROTO_MASS_BBB: + case UIPROTO_MASS_BBB_OLD: + return (0); + default: + return (0); + } + break; + default: + return (0); + } +} + +static int +atausb2_attach(device_t dev) +{ + struct atausb2_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + const char *proto, *subclass; + struct usb2_device_request request; + uint16_t i; + uint8_t maxlun; + uint8_t has_intr; + int err; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->dev = dev; + sc->maxlun = 0; + sc->locked_ch = NULL; + sc->restart_ch = NULL; + sc->usb2_speed = usb2_get_speed(uaa->device); + mtx_init(&sc->locked_mtx, "ATAUSB lock", NULL, (MTX_DEF | MTX_RECURSE)); + + id = usb2_get_interface_descriptor(uaa->iface); + switch (id->bInterfaceProtocol) { + case UIPROTO_MASS_BBB: + case UIPROTO_MASS_BBB_OLD: + proto = "Bulk-Only"; + break; + case UIPROTO_MASS_CBI: + proto = "CBI"; + break; + case UIPROTO_MASS_CBI_I: + proto = "CBI with CCI"; + break; + default: + proto = "Unknown"; + } + + switch (id->bInterfaceSubClass) { + case UISUBCLASS_RBC: + subclass = "RBC"; + break; + case UISUBCLASS_QIC157: + case UISUBCLASS_SFF8020I: + case UISUBCLASS_SFF8070I: + subclass = "ATAPI"; + break; + case UISUBCLASS_SCSI: + subclass = "SCSI"; + break; + case UISUBCLASS_UFI: + subclass = "UFI"; + break; + default: + subclass = "Unknown"; + } + + has_intr = (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I); + sc->iface_no = id->bInterfaceNumber; + + device_printf(dev, "using %s over %s\n", subclass, proto); + if (strcmp(proto, "Bulk-Only") || + (strcmp(subclass, "ATAPI") && strcmp(subclass, "SCSI"))) { + goto detach; + } + err = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, + sc->xfer, atausb2_config, ATAUSB_T_BBB_MAX, sc, + &sc->locked_mtx); + + /* skip reset first time */ + sc->last_xfer_no = ATAUSB_T_BBB_COMMAND; + + if (err) { + device_printf(sc->dev, "could not setup required " + "transfers, %s\n", usb2_errstr(err)); + goto detach; + } + /* get number of devices so we can add matching channels */ + request.bmRequestType = UT_READ_CLASS_INTERFACE; + request.bRequest = 0xfe; /* GET_MAX_LUN; */ + USETW(request.wValue, 0); + USETW(request.wIndex, sc->iface_no); + USETW(request.wLength, sizeof(maxlun)); + err = usb2_do_request(uaa->device, &Giant, &request, &maxlun); + + if (err) { + if (bootverbose) { + device_printf(sc->dev, "get maxlun not supported %s\n", + usb2_errstr(err)); + } + } else { + sc->maxlun = maxlun; + if (bootverbose) { + device_printf(sc->dev, "maxlun=%d\n", sc->maxlun); + } + } + + /* ata channels are children to this USB control device */ + for (i = 0; i <= sc->maxlun; i++) { + if (!device_add_child(sc->dev, "ata", + devclass_find_free_unit(ata_devclass, 2))) { + device_printf(sc->dev, "failed to attach ata child device\n"); + goto detach; + } + } + bus_generic_attach(sc->dev); + + return (0); + +detach: + atausb2_detach(dev); + return (ENXIO); +} + +static int +atausb2_detach(device_t dev) +{ + struct atausb2_softc *sc = device_get_softc(dev); + device_t *children; + int nchildren, i; + + /* teardown our statemachine */ + + usb2_transfer_unsetup(sc->xfer, ATAUSB_T_MAX); + + /* detach & delete all children, if any */ + + if (!device_get_children(dev, &children, &nchildren)) { + for (i = 0; i < nchildren; i++) { + device_delete_child(dev, children[i]); + } + free(children, M_TEMP); + } + mtx_destroy(&sc->locked_mtx); + return (0); +} + +static void +atausb2_transfer_start(struct atausb2_softc *sc, uint8_t xfer_no) +{ + if (atausbdebug) { + device_printf(sc->dev, "BBB transfer %d\n", xfer_no); + } + if (sc->xfer[xfer_no]) { + sc->last_xfer_no = xfer_no; + usb2_transfer_start(sc->xfer[xfer_no]); + } else { + atausb2_cancel_request(sc); + } + return; +} + +static void +atausb2_t_bbb_reset1_callback(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + atausb2_transfer_start(sc, ATAUSB_T_BBB_RESET2); + return; + + case USB_ST_SETUP: + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = 0xff; /* bulk-only reset */ + USETW(req.wValue, 0); + req.wIndex[0] = sc->iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->nframes = 1; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + atausb2_tr_error(xfer); + return; + + } +} + +static void +atausb2_t_bbb_reset2_callback(struct usb2_xfer *xfer) +{ + atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_RESET3, + ATAUSB_T_BBB_DATA_READ); + return; +} + +static void +atausb2_t_bbb_reset3_callback(struct usb2_xfer *xfer) +{ + atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_COMMAND, + ATAUSB_T_BBB_DATA_WRITE); + return; +} + +static void +atausb2_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer, + uint8_t next_xfer, + uint8_t stall_xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + atausb2_transfer_start(sc, next_xfer); + return; + + case USB_ST_SETUP: + if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { + goto tr_transferred; + } + return; + + default: /* Error */ + atausb2_tr_error(xfer); + return; + + } +} + +static void +atausb2_t_bbb_command_callback(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + struct ata_request *request = sc->ata_request; + struct ata_channel *ch; + uint32_t tag; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + atausb2_transfer_start + (sc, ((request->flags & ATA_R_READ) ? ATAUSB_T_BBB_DATA_READ : + (request->flags & ATA_R_WRITE) ? ATAUSB_T_BBB_DATA_WRITE : + ATAUSB_T_BBB_STATUS)); + return; + + case USB_ST_SETUP: + + sc->status_try = 0; + + if (request) { + ch = device_get_softc(request->parent); + + sc->timeout = (request->timeout * 1000) + 5000; + + tag = UGETDW(sc->cbw.tag) + 1; + + USETDW(sc->cbw.signature, CBWSIGNATURE); + USETDW(sc->cbw.tag, tag); + USETDW(sc->cbw.transfer_length, request->bytecount); + sc->cbw.flags = (request->flags & ATA_R_READ) ? CBWFLAGS_IN : CBWFLAGS_OUT; + sc->cbw.lun = ch->unit; + sc->cbw.length = 16; + bzero(sc->cbw.cdb, 16); + bcopy(request->u.atapi.ccb, sc->cbw.cdb, 12); /* XXX SOS */ + + usb2_copy_in(xfer->frbuffers, 0, &sc->cbw, sizeof(sc->cbw)); + + xfer->frlengths[0] = sizeof(sc->cbw); + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + atausb2_tr_error(xfer); + return; + + } +} + +static void +atausb2_t_bbb_data_read_callback(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usb2_copy_out(xfer->frbuffers, 0, + sc->ata_data, xfer->actlen); + + sc->ata_bytecount -= xfer->actlen; + sc->ata_data += xfer->actlen; + sc->ata_donecount += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->ata_bytecount = 0; + } + case USB_ST_SETUP: + + if (atausbdebug > 1) { + device_printf(sc->dev, "%s: max_bulk=%d, ata_bytecount=%d\n", + __FUNCTION__, max_bulk, sc->ata_bytecount); + } + if (sc->ata_bytecount == 0) { + atausb2_transfer_start(sc, ATAUSB_T_BBB_STATUS); + return; + } + if (max_bulk > sc->ata_bytecount) { + max_bulk = sc->ata_bytecount; + } + xfer->timeout = sc->timeout; + xfer->frlengths[0] = max_bulk; + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + atausb2_tr_error(xfer); + } else { + atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_RD_CS); + } + return; + + } +} + +static void +atausb2_t_bbb_data_rd_cs_callback(struct usb2_xfer *xfer) +{ + atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_STATUS, + ATAUSB_T_BBB_DATA_READ); + return; +} + +static void +atausb2_t_bbb_data_write_callback(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + sc->ata_bytecount -= xfer->actlen; + sc->ata_data += xfer->actlen; + sc->ata_donecount += xfer->actlen; + + case USB_ST_SETUP: + + if (atausbdebug > 1) { + device_printf(sc->dev, "%s: max_bulk=%d, ata_bytecount=%d\n", + __FUNCTION__, max_bulk, sc->ata_bytecount); + } + if (sc->ata_bytecount == 0) { + atausb2_transfer_start(sc, ATAUSB_T_BBB_STATUS); + return; + } + if (max_bulk > sc->ata_bytecount) { + max_bulk = sc->ata_bytecount; + } + xfer->timeout = sc->timeout; + xfer->frlengths[0] = max_bulk; + + usb2_copy_in(xfer->frbuffers, 0, + sc->ata_data, max_bulk); + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + atausb2_tr_error(xfer); + } else { + atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_WR_CS); + } + return; + + } +} + +static void +atausb2_t_bbb_data_wr_cs_callback(struct usb2_xfer *xfer) +{ + atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_STATUS, + ATAUSB_T_BBB_DATA_WRITE); + return; +} + +static void +atausb2_t_bbb_status_callback(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + struct ata_request *request = sc->ata_request; + uint32_t residue; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < sizeof(sc->csw)) { + bzero(&sc->csw, sizeof(sc->csw)); + } + usb2_copy_out(xfer->frbuffers, 0, &sc->csw, xfer->actlen); + + if (request->flags & (ATA_R_READ | ATA_R_WRITE)) { + request->donecount = sc->ata_donecount; + } + residue = UGETDW(sc->csw.residue); + + if (!residue) { + residue = (request->bytecount - request->donecount); + } + if (residue > request->bytecount) { + if (atausbdebug) { + device_printf(sc->dev, "truncating residue from %d " + "to %d bytes\n", residue, + request->bytecount); + } + residue = request->bytecount; + } + /* check CSW and handle eventual error */ + if (UGETDW(sc->csw.signature) != CSWSIGNATURE) { + if (atausbdebug) { + device_printf(sc->dev, "bad CSW signature 0x%08x != 0x%08x\n", + UGETDW(sc->csw.signature), CSWSIGNATURE); + } + goto tr_error; + } else if (UGETDW(sc->csw.tag) != UGETDW(sc->cbw.tag)) { + if (atausbdebug) { + device_printf(sc->dev, "bad CSW tag %d != %d\n", + UGETDW(sc->csw.tag), UGETDW(sc->cbw.tag)); + } + goto tr_error; + } else if (sc->csw.status > CSWSTATUS_PHASE) { + if (atausbdebug) { + device_printf(sc->dev, "bad CSW status %d > %d\n", + sc->csw.status, CSWSTATUS_PHASE); + } + goto tr_error; + } else if (sc->csw.status == CSWSTATUS_PHASE) { + if (atausbdebug) { + device_printf(sc->dev, "phase error residue = %d\n", residue); + } + goto tr_error; + } else if (request->donecount > request->bytecount) { + if (atausbdebug) { + device_printf(sc->dev, "buffer overrun %d > %d\n", + request->donecount, request->bytecount); + } + goto tr_error; + } else if (sc->csw.status == CSWSTATUS_FAILED) { + if (atausbdebug) { + device_printf(sc->dev, "CSWSTATUS_FAILED\n"); + } + request->error = ATA_E_ATAPI_SENSE_MASK; + } + sc->last_xfer_no = ATAUSB_T_BBB_COMMAND; + + sc->ata_request = NULL; + + mtx_unlock(xfer->priv_mtx); + + ata_interrupt(device_get_softc(request->parent)); + + mtx_lock(xfer->priv_mtx); + return; + + case USB_ST_SETUP: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: +tr_error: + if ((xfer->error == USB_ERR_CANCELLED) || + (sc->status_try)) { + atausb2_tr_error(xfer); + } else { + sc->status_try = 1; + atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_RD_CS); + } + return; + + } +} + +static void +atausb2_cancel_request(struct atausb2_softc *sc) +{ + struct ata_request *request; + + mtx_assert(&sc->locked_mtx, MA_OWNED); + + request = sc->ata_request; + sc->ata_request = NULL; + sc->last_xfer_no = ATAUSB_T_BBB_RESET1; + + if (request) { + request->error = ATA_E_ATAPI_SENSE_MASK; + + mtx_unlock(&sc->locked_mtx); + + ata_interrupt(device_get_softc(request->parent)); + + mtx_lock(&sc->locked_mtx); + } + return; +} + +static void +atausb2_tr_error(struct usb2_xfer *xfer) +{ + struct atausb2_softc *sc = xfer->priv_sc; + + if (xfer->error != USB_ERR_CANCELLED) { + + if (atausbdebug) { + device_printf(sc->dev, "transfer failed, %s, in state %d " + "-> BULK reset\n", usb2_errstr(xfer->error), + sc->last_xfer_no); + } + } + atausb2_cancel_request(sc); + + return; +} + +/* + * ATA backend part + */ +struct atapi_inquiry { + uint8_t device_type; + uint8_t device_modifier; + uint8_t version; + uint8_t response_format; + uint8_t length; + uint8_t reserved[2]; + uint8_t flags; + uint8_t vendor[8]; + uint8_t product[16]; + uint8_t revision[4]; + /* uint8_t crap[60]; */ +} __packed; + +static int +ata_usbchannel_begin_transaction(struct ata_request *request) +{ + struct atausb2_softc *sc = + device_get_softc(device_get_parent(request->parent)); + int error; + + if (atausbdebug > 1) { + device_printf(request->dev, "begin_transaction %s\n", + ata_cmd2str(request)); + } + mtx_lock(&sc->locked_mtx); + + /* sanity, just in case */ + if (sc->ata_request) { + device_printf(request->dev, "begin is busy, " + "state = %d\n", sc->last_xfer_no); + request->result = EBUSY; + error = ATA_OP_FINISHED; + goto done; + } + /* + * XXX SOS convert the request into the format used, only BBB for + * now + */ + + /* ATA/ATAPI IDENTIFY needs special treatment */ + if (!(request->flags & ATA_R_ATAPI)) { + if (request->u.ata.command != ATA_ATAPI_IDENTIFY) { + device_printf(request->dev, "%s unsupported\n", + ata_cmd2str(request)); + request->result = EIO; + error = ATA_OP_FINISHED; + goto done; + } + request->flags |= ATA_R_ATAPI; + bzero(request->u.atapi.ccb, 16); + request->u.atapi.ccb[0] = ATAPI_INQUIRY; + request->u.atapi.ccb[4] = 255; /* sizeof(struct + * atapi_inquiry); */ + request->data += 256; /* arbitrary offset into ata_param */ + request->bytecount = 255; /* sizeof(struct + * atapi_inquiry); */ + } + if (sc->xfer[sc->last_xfer_no]) { + + sc->ata_request = request; + sc->ata_bytecount = request->bytecount; + sc->ata_data = request->data; + sc->ata_donecount = 0; + + usb2_transfer_start(sc->xfer[sc->last_xfer_no]); + error = ATA_OP_CONTINUES; + } else { + request->result = EIO; + error = ATA_OP_FINISHED; + } + +done: + mtx_unlock(&sc->locked_mtx); + return (error); +} + +static int +ata_usbchannel_end_transaction(struct ata_request *request) +{ + if (atausbdebug > 1) { + device_printf(request->dev, "end_transaction %s\n", + ata_cmd2str(request)); + } + /* + * XXX SOS convert the request from the format used, only BBB for + * now + */ + + /* ATA/ATAPI IDENTIFY needs special treatment */ + if ((request->flags & ATA_R_ATAPI) && + (request->u.atapi.ccb[0] == ATAPI_INQUIRY)) { + struct ata_device *atadev = device_get_softc(request->dev); + struct atapi_inquiry *inquiry = (struct atapi_inquiry *)request->data; + uint16_t *ptr; + + /* convert inquiry data into simple ata_param like format */ + atadev->param.config = ATA_PROTO_ATAPI | ATA_PROTO_ATAPI_12; + atadev->param.config |= (inquiry->device_type & 0x1f) << 8; + bzero(atadev->param.model, sizeof(atadev->param.model)); + strncpy(atadev->param.model, inquiry->vendor, 8); + strcpy(atadev->param.model, " "); + strncpy(atadev->param.model, inquiry->product, 16); + ptr = (uint16_t *)(atadev->param.model + sizeof(atadev->param.model)); + while (--ptr >= (uint16_t *)atadev->param.model) { + *ptr = ntohs(*ptr); + } + strncpy(atadev->param.revision, inquiry->revision, 4); + ptr = (uint16_t *)(atadev->param.revision + sizeof(atadev->param.revision)); + while (--ptr >= (uint16_t *)atadev->param.revision) { + *ptr = ntohs(*ptr); + } + request->result = 0; + } + return (ATA_OP_FINISHED); +} + +static int +ata_usbchannel_probe(device_t dev) +{ + struct ata_channel *ch = device_get_softc(dev); + device_t *children; + int count, i; + char buffer[32]; + + /* take care of green memory */ + bzero(ch, sizeof(struct ata_channel)); + + /* find channel number on this controller */ + if (!device_get_children(device_get_parent(dev), &children, &count)) { + for (i = 0; i < count; i++) { + if (children[i] == dev) + ch->unit = i; + } + free(children, M_TEMP); + } + snprintf(buffer, sizeof(buffer), "USB lun %d", ch->unit); + device_set_desc_copy(dev, buffer); + + return (0); +} + +static int +ata_usbchannel_attach(device_t dev) +{ + struct ata_channel *ch = device_get_softc(dev); + + /* initialize the softc basics */ + ch->dev = dev; + ch->state = ATA_IDLE; + ch->hw.begin_transaction = ata_usbchannel_begin_transaction; + ch->hw.end_transaction = ata_usbchannel_end_transaction; + ch->hw.status = NULL; + ch->hw.command = NULL; + bzero(&ch->state_mtx, sizeof(struct mtx)); + mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); + bzero(&ch->queue_mtx, sizeof(struct mtx)); + mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); + TAILQ_INIT(&ch->ata_queue); + + /* XXX SOS reset the controller HW, the channel and device(s) */ + /* ATA_RESET(dev); */ + + /* probe and attach device on this channel */ + ch->devices = ATA_ATAPI_MASTER; + if (!ata_delayed_attach) { + ata_identify(dev); + } + return (0); +} + +static int +ata_usbchannel_detach(device_t dev) +{ + struct ata_channel *ch = device_get_softc(dev); + device_t *children; + int nchildren, i; + + /* detach & delete all children */ + if (!device_get_children(dev, &children, &nchildren)) { + for (i = 0; i < nchildren; i++) + if (children[i]) + device_delete_child(dev, children[i]); + free(children, M_TEMP); + } + mtx_destroy(&ch->state_mtx); + mtx_destroy(&ch->queue_mtx); + return (0); +} + +static void +ata_usbchannel_setmode(device_t parent, device_t dev) +{ + struct atausb2_softc *sc = device_get_softc(GRANDPARENT(dev)); + struct ata_device *atadev = device_get_softc(dev); + + if (sc->usb2_speed == USB_SPEED_HIGH) + atadev->mode = ATA_USB2; + else + atadev->mode = ATA_USB1; + return; +} + +static int +ata_usbchannel_locking(device_t dev, int flags) +{ + struct atausb2_softc *sc = device_get_softc(device_get_parent(dev)); + struct ata_channel *ch = device_get_softc(dev); + int res = -1; + + mtx_lock(&sc->locked_mtx); + switch (flags) { + case ATA_LF_LOCK: + if (sc->locked_ch == NULL) + sc->locked_ch = ch; + if (sc->locked_ch != ch) + sc->restart_ch = ch; + break; + + case ATA_LF_UNLOCK: + if (sc->locked_ch == ch) { + sc->locked_ch = NULL; + if (sc->restart_ch) { + ch = sc->restart_ch; + sc->restart_ch = NULL; + mtx_unlock(&sc->locked_mtx); + ata_start(ch->dev); + return (res); + } + } + break; + + case ATA_LF_WHICH: + break; + } + if (sc->locked_ch) { + res = sc->locked_ch->unit; + } + mtx_unlock(&sc->locked_mtx); + return (res); +} + +static device_method_t ata_usbchannel_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ata_usbchannel_probe), + DEVMETHOD(device_attach, ata_usbchannel_attach), + DEVMETHOD(device_detach, ata_usbchannel_detach), + + /* ATA methods */ + DEVMETHOD(ata_setmode, ata_usbchannel_setmode), + DEVMETHOD(ata_locking, ata_usbchannel_locking), + /* DEVMETHOD(ata_reset, ata_usbchannel_reset), */ + + {0, 0} +}; + +static driver_t ata_usbchannel_driver = { + "ata", + ata_usbchannel_methods, + sizeof(struct ata_channel), +}; + +DRIVER_MODULE(ata, atausb, ata_usbchannel_driver, ata_devclass, 0, 0); +MODULE_DEPEND(atausb, ata, 1, 1, 1); diff --git a/sys/dev/usb2/storage/umass2.c b/sys/dev/usb2/storage/umass2.c new file mode 100644 index 000000000000..f998cf1cb2cd --- /dev/null +++ b/sys/dev/usb2/storage/umass2.c @@ -0,0 +1,3670 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 1999 MAEKAWA Masahide , + * Nick Hibma + * 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 . + * Parts of the code written by Jason R. Thorpe . + */ + +/* + * 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->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_t_*_callback. + * The state machine is started through either umass_command_start() 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#if 1 +/* this enables loading of virtual buffers into DMA */ +#define UMASS_USB_FLAGS .ext_buffer=1, +#else +#define UMASS_USB_FLAGS +#endif + +#if USB_DEBUG +#define DIF(m, x) \ + do { \ + if (umass_debug & (m)) { x ; } \ + } while (0) + +#define DPRINTF(sc, m, fmt, ...) \ + do { \ + if (umass_debug & (m)) { \ + printf("%s:%s: " fmt, \ + (sc) ? (const char *)(sc)->sc_name : \ + (const char *)"umassX", \ + __FUNCTION__ ,## __VA_ARGS__); \ + } \ + } while (0) + +#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 */ +static int umass_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass"); +SYSCTL_INT(_hw_usb2_umass, OID_AUTO, debug, CTLFLAG_RW, + &umass_debug, 0, "umass debug level"); +#else +#define DIF(...) do { } while (0) +#define DPRINTF(...) do { } while (0) +#endif + +#define UMASS_GONE ((struct umass_softc *)1) +#define UMASS_MAXUNIT 64 /* XXX temporary */ + +#define UMASS_BULK_SIZE (1 << 17) +#define UMASS_CBI_DIAGNOSTIC_CMDLEN 12 /* bytes */ +#define UMASS_MAX_CMDLEN MAX(12, CAM_MAX_CDBLEN) /* bytes */ + +/* USB transfer definitions */ + +#define UMASS_T_BBB_RESET1 0 /* Bulk-Only */ +#define UMASS_T_BBB_RESET2 1 +#define UMASS_T_BBB_RESET3 2 +#define UMASS_T_BBB_COMMAND 3 +#define UMASS_T_BBB_DATA_READ 4 +#define UMASS_T_BBB_DATA_RD_CS 5 +#define UMASS_T_BBB_DATA_WRITE 6 +#define UMASS_T_BBB_DATA_WR_CS 7 +#define UMASS_T_BBB_STATUS 8 +#define UMASS_T_BBB_MAX 9 + +#define UMASS_T_CBI_RESET1 0 /* CBI */ +#define UMASS_T_CBI_RESET2 1 +#define UMASS_T_CBI_RESET3 2 +#define UMASS_T_CBI_COMMAND 3 +#define UMASS_T_CBI_DATA_READ 4 +#define UMASS_T_CBI_DATA_RD_CS 5 +#define UMASS_T_CBI_DATA_WRITE 6 +#define UMASS_T_CBI_DATA_WR_CS 7 +#define UMASS_T_CBI_STATUS 8 +#define UMASS_T_CBI_RESET4 9 +#define UMASS_T_CBI_MAX 10 + +#define UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX) + +/* Generic definitions */ + +/* Direction for transfer */ +#define DIR_NONE 0 +#define DIR_IN 1 +#define DIR_OUT 2 + +/* device name */ +#define DEVNAME "umass" +#define DEVNAME_SIM "umass-sim" + +/* 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 /* ms */ + +/* CAM specific definitions */ + +#define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */ +#define UMASS_SCSIID_HOST UMASS_SCSIID_MAX + +/* 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 union { + struct { + uint8_t type; +#define IDB_TYPE_CCI 0x00 + uint8_t 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 + } __packed common; + + struct { + uint8_t asc; + uint8_t ascq; + } __packed ufi; +} __packed umass_cbi_sbl_t; + +struct umass_softc; /* see below */ + +typedef void (umass_callback_t)(struct umass_softc *sc, union ccb *ccb, + uint32_t residue, uint8_t 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 uint8_t (umass_transform_t)(struct umass_softc *sc, uint8_t *cmd_ptr, + uint8_t cmd_len); + +struct umass_devdescr { + uint32_t vid; +#define VID_WILDCARD 0xffffffff +#define VID_EOT 0xfffffffe + uint32_t pid; +#define PID_WILDCARD 0xffffffff +#define PID_EOT 0xfffffffe + uint32_t rid; +#define RID_WILDCARD 0xffffffff +#define RID_EOT 0xfffffffe + + /* wire and command protocol */ + uint16_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 */ + uint16_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 const struct umass_devdescr umass_devdescr[] = { + {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_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_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_DZ_MV100A, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_CBI, + NO_GETMAXLUN + }, + {USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, RID_WILDCARD, + UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, + NO_INQUIRY + }, + {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_USBCABLE, RID_WILDCARD, + UMASS_PROTO_ATAPI | UMASS_PROTO_CBI, + NO_TEST_UNIT_READY | NO_START_STOP | ALT_IFACE_1 + }, + {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_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_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_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_SANDISK, USB_PRODUCT_SANDISK_SDDR31, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + READ_CAPACITY_OFFBY1 + }, + {USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, RID_WILDCARD, + UMASS_PROTO_ATAPI | UMASS_PROTO_BBB, + NO_INQUIRY + }, + {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_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_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_HANDYCAM, 0x0500, + UMASS_PROTO_RBC | UMASS_PROTO_CBI, + RBC_PAD_TO_12 + }, + {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, RID_WILDCARD, + UMASS_PROTO_RBC | UMASS_PROTO_CBI, + NO_QUIRKS + }, + {USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, 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_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} +}; + +struct umass_softc { + + struct scsi_sense cam_scsi_sense; + struct scsi_test_unit_ready cam_scsi_test_unit_ready; + struct mtx sc_mtx; + struct { + uint8_t *data_ptr; + union ccb *ccb; + umass_callback_t *callback; + + uint32_t data_len; /* bytes */ + uint32_t data_rem; /* bytes */ + uint32_t data_timeout; /* ms */ + uint32_t actlen; /* bytes */ + + uint8_t cmd_data[UMASS_MAX_CMDLEN]; + uint8_t cmd_len; /* bytes */ + uint8_t dir; + uint8_t lun; + } sc_transfer; + + /* 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_sbl_t sbl; /* status block */ + + device_t sc_dev; + struct usb2_device *sc_udev; + struct cam_sim *sc_sim; /* SCSI Interface Module */ + struct usb2_xfer *sc_xfer[UMASS_T_MAX]; + + /* + * The command transform function is used to convert the SCSI + * commands into their derivatives, like UFI, ATAPI, and friends. + */ + umass_transform_t *sc_transform; + + uint32_t sc_unit; + + uint16_t sc_proto; /* wire and cmd protocol */ + uint16_t sc_quirks; /* they got it almost right */ + + uint8_t sc_name[16]; + uint8_t sc_iface_no; /* interface number */ + uint8_t sc_maxlun; /* maximum LUN number, inclusive */ + uint8_t sc_last_xfer_index; + uint8_t sc_status_try; +}; + +struct umass_probe_proto { + uint16_t quirks; + uint16_t proto; + + int32_t error; +}; + +/* prototypes */ + +static device_probe_t umass_probe; +static device_attach_t umass_attach; +static device_detach_t umass_detach; + +static usb2_callback_t umass_tr_error; +static usb2_callback_t umass_t_bbb_reset1_callback; +static usb2_callback_t umass_t_bbb_reset2_callback; +static usb2_callback_t umass_t_bbb_reset3_callback; +static usb2_callback_t umass_t_bbb_command_callback; +static usb2_callback_t umass_t_bbb_data_read_callback; +static usb2_callback_t umass_t_bbb_data_rd_cs_callback; +static usb2_callback_t umass_t_bbb_data_write_callback; +static usb2_callback_t umass_t_bbb_data_wr_cs_callback; +static usb2_callback_t umass_t_bbb_status_callback; +static usb2_callback_t umass_t_cbi_reset1_callback; +static usb2_callback_t umass_t_cbi_reset2_callback; +static usb2_callback_t umass_t_cbi_reset3_callback; +static usb2_callback_t umass_t_cbi_reset4_callback; +static usb2_callback_t umass_t_cbi_command_callback; +static usb2_callback_t umass_t_cbi_data_read_callback; +static usb2_callback_t umass_t_cbi_data_rd_cs_callback; +static usb2_callback_t umass_t_cbi_data_write_callback; +static usb2_callback_t umass_t_cbi_data_wr_cs_callback; +static usb2_callback_t umass_t_cbi_status_callback; + +static void umass_cancel_ccb(struct umass_softc *sc); +static void umass_init_shuttle(struct umass_softc *sc); +static void umass_reset(struct umass_softc *sc); +static void umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer); +static void umass_command_start(struct umass_softc *sc, uint8_t dir, void *data_ptr, uint32_t data_len, uint32_t data_timeout, umass_callback_t *callback, union ccb *ccb); +static uint8_t umass_bbb_get_max_lun(struct umass_softc *sc); +static void umass_cbi_start_status(struct umass_softc *sc); +static void umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer); +static int umass_cam_attach_sim(struct umass_softc *sc); +static void umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb); +static void umass_cam_rescan(struct umass_softc *sc); +static void umass_cam_attach(struct umass_softc *sc); +static void umass_cam_detach_sim(struct umass_softc *sc); +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, union ccb *ccb, uint32_t residue, uint8_t status); +static void umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status); +static void umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, uint8_t status); +static uint8_t umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len); +static uint8_t umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len); +static uint8_t umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len); +static uint8_t umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len); +static uint8_t umass_no_transform(struct umass_softc *sc, uint8_t *cmd, uint8_t cmdlen); +static uint8_t umass_std_transform(struct umass_softc *sc, union ccb *ccb, uint8_t *cmd, uint8_t cmdlen); +static int umass_driver_loaded(struct module *mod, int what, void *arg); + +#if USB_DEBUG +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, uint8_t cmdlen); +static void umass_dump_buffer(struct umass_softc *sc, uint8_t *buffer, uint32_t buflen, uint32_t printlen); + +#endif + +struct usb2_config umass_bbb_config[UMASS_T_BBB_MAX] = { + + [UMASS_T_BBB_RESET1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_reset1_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 500, /* 500 milliseconds */ + }, + + [UMASS_T_BBB_RESET2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_reset2_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [UMASS_T_BBB_RESET3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_reset3_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [UMASS_T_BBB_COMMAND] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = sizeof(umass_bbb_cbw_t), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_command_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_BBB_DATA_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMASS_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS}, + .mh.callback = &umass_t_bbb_data_read_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [UMASS_T_BBB_DATA_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_data_rd_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_BBB_DATA_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMASS_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS}, + .mh.callback = &umass_t_bbb_data_write_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [UMASS_T_BBB_DATA_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_bbb_data_wr_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_BBB_STATUS] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = sizeof(umass_bbb_csw_t), + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &umass_t_bbb_status_callback, + .mh.timeout = 5000, /* ms */ + }, +}; + +struct usb2_config umass_cbi_config[UMASS_T_CBI_MAX] = { + + [UMASS_T_CBI_RESET1] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + + UMASS_CBI_DIAGNOSTIC_CMDLEN), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_reset1_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 500, /* 500 milliseconds */ + }, + + [UMASS_T_CBI_RESET2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_reset2_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [UMASS_T_CBI_RESET3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_reset3_callback, + .mh.timeout = 5000, /* 5 seconds */ + .mh.interval = 50, /* 50 milliseconds */ + }, + + [UMASS_T_CBI_COMMAND] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + + UMASS_MAX_CMDLEN), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_command_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_CBI_DATA_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMASS_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS}, + .mh.callback = &umass_t_cbi_data_read_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [UMASS_T_CBI_DATA_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_data_rd_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_CBI_DATA_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMASS_BULK_SIZE, + .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS}, + .mh.callback = &umass_t_cbi_data_write_callback, + .mh.timeout = 0, /* overwritten later */ + }, + + [UMASS_T_CBI_DATA_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_data_wr_cs_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + [UMASS_T_CBI_STATUS] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.bufsize = sizeof(umass_cbi_sbl_t), + .mh.callback = &umass_t_cbi_status_callback, + .mh.timeout = 5000, /* ms */ + }, + + [UMASS_T_CBI_RESET4] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &umass_t_cbi_reset4_callback, + .mh.timeout = 5000, /* ms */ + }, +}; + +/* If device cannot return valid inquiry data, fake it */ +static const uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = { + 0, /* removable */ 0x80, SCSI_REV_2, SCSI_REV_2, + /* additional_length */ 31, 0, 0, 0 +}; + +#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */ +#define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */ + +static struct cam_sim *umass_sim[UMASS_MAXUNIT]; +static struct mtx umass_mtx; + +static devclass_t umass_devclass; + +static device_method_t umass_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, umass_probe), + DEVMETHOD(device_attach, umass_attach), + DEVMETHOD(device_detach, umass_detach), + {0, 0} +}; + +static driver_t umass_driver = { + .name = "umass", + .methods = umass_methods, + .size = sizeof(struct umass_softc), +}; + +DRIVER_MODULE(umass, ushub, umass_driver, umass_devclass, umass_driver_loaded, 0); +MODULE_DEPEND(umass, usb2_storage, 1, 1, 1); +MODULE_DEPEND(umass, usb2_core, 1, 1, 1); +MODULE_DEPEND(umass, cam, 1, 1, 1); + +/* + * USB device probe/attach/detach + */ + +/* + * Match the device we are seeing with the + * devices supported. + */ +static struct umass_probe_proto +umass_probe_proto(device_t dev, struct usb2_attach_arg *uaa) +{ + const struct umass_devdescr *udd = umass_devdescr; + struct usb2_interface_descriptor *id; + struct umass_probe_proto ret; + + bzero(&ret, sizeof(ret)); + + /* + * An entry specifically for Y-E Data devices as they don't fit in + * the device description table. + */ + if ((uaa->info.idVendor == USB_VENDOR_YEDATA) && + (uaa->info.idProduct == USB_PRODUCT_YEDATA_FLASHBUSTERU)) { + + /* + * Revisions < 1.28 do not handle the interrupt endpoint + * very well. + */ + if (uaa->info.bcdDevice < 0x128) { + ret.proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI; + } else { + ret.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 (uaa->info.bcdDevice <= 0x128) { + ret.quirks |= NO_TEST_UNIT_READY; + } + ret.quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED; + ret.error = 0; + goto done; + } + /* + * Check the list of supported devices for a match. While looking, + * check for wildcarded and fully matched. First match wins. + */ + for (; udd->vid != VID_EOT; udd++) { + if ((udd->vid == VID_WILDCARD) && + (udd->pid == PID_WILDCARD) && + (udd->rid == RID_WILDCARD)) { + device_printf(dev, "ignoring invalid " + "wildcard quirk\n"); + continue; + } + if (((udd->vid == uaa->info.idVendor) || + (udd->vid == VID_WILDCARD)) && + ((udd->pid == uaa->info.idProduct) || + (udd->pid == PID_WILDCARD))) { + if (udd->rid == RID_WILDCARD) { + ret.proto = udd->proto; + ret.quirks = udd->quirks; + ret.error = 0; + goto done; + } else if (udd->rid == uaa->info.bcdDevice) { + ret.proto = udd->proto; + ret.quirks = udd->quirks; + ret.error = 0; + goto done; + } /* else RID does not match */ + } + } + + /* Check for a standards compliant device */ + id = usb2_get_interface_descriptor(uaa->iface); + if ((id == NULL) || + (id->bInterfaceClass != UICLASS_MASS)) { + ret.error = ENXIO; + goto done; + } + switch (id->bInterfaceSubClass) { + case UISUBCLASS_SCSI: + ret.proto |= UMASS_PROTO_SCSI; + break; + case UISUBCLASS_UFI: + ret.proto |= UMASS_PROTO_UFI; + break; + case UISUBCLASS_RBC: + ret.proto |= UMASS_PROTO_RBC; + break; + case UISUBCLASS_SFF8020I: + case UISUBCLASS_SFF8070I: + ret.proto |= UMASS_PROTO_ATAPI; + break; + default: + device_printf(dev, "unsupported command " + "protocol %d\n", id->bInterfaceSubClass); + ret.error = ENXIO; + goto done; + } + + switch (id->bInterfaceProtocol) { + case UIPROTO_MASS_CBI: + ret.proto |= UMASS_PROTO_CBI; + break; + case UIPROTO_MASS_CBI_I: + ret.proto |= UMASS_PROTO_CBI_I; + break; + case UIPROTO_MASS_BBB_OLD: + case UIPROTO_MASS_BBB: + ret.proto |= UMASS_PROTO_BBB; + break; + default: + device_printf(dev, "unsupported wire " + "protocol %d\n", id->bInterfaceProtocol); + ret.error = ENXIO; + goto done; + } + + ret.error = 0; +done: + return (ret); +} + +static int +umass_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umass_probe_proto temp; + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->use_generic == 0) { + /* give other drivers a try first */ + return (ENXIO); + } + temp = umass_probe_proto(dev, uaa); + + return (temp.error); +} + +static int +umass_attach(device_t dev) +{ + struct umass_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umass_probe_proto temp = umass_probe_proto(dev, uaa); + struct usb2_interface_descriptor *id; + int32_t err; + + if (sc == NULL) { + return (ENOMEM); + } + if (device_get_unit(dev) >= UMASS_MAXUNIT) { + device_printf(dev, "Maxunit(%u) limit reached!\n", + UMASS_MAXUNIT); + return (ENOMEM); + } + /* + * NOTE: the softc struct is bzero-ed in device_set_driver. + * We can safely call umass_detach without specifically + * initializing the struct. + */ + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + sc->sc_proto = temp.proto; + sc->sc_quirks = temp.quirks; + sc->sc_unit = device_get_unit(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + device_set_usb2_desc(dev); + + /* get interface index */ + + id = usb2_get_interface_descriptor(uaa->iface); + if (id == NULL) { + device_printf(dev, "failed to get " + "interface number\n"); + goto detach; + } + sc->sc_iface_no = id->bInterfaceNumber; + +#if USB_DEBUG + device_printf(dev, " "); + + switch (sc->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->sc_proto & UMASS_PROTO_COMMAND); + break; + } + + printf(" over "); + + switch (sc->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"); + break; + default: + printf("(unknown 0x%02x)", + sc->sc_proto & UMASS_PROTO_WIRE); + } + + printf("; quirks = 0x%04x\n", sc->sc_quirks); +#endif + + if (sc->sc_quirks & ALT_IFACE_1) { + err = usb2_set_alt_interface_index + (uaa->device, uaa->info.bIfaceIndex, 1); + + if (err) { + DPRINTF(sc, UDMASS_USB, "could not switch to " + "Alt Interface 1\n"); + goto detach; + } + } + /* allocate all required USB transfers */ + + if (sc->sc_proto & UMASS_PROTO_BBB) { + + err = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, umass_bbb_config, + UMASS_T_BBB_MAX, sc, &umass_mtx); + + /* skip reset first time */ + sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; + + } else if (sc->sc_proto & (UMASS_PROTO_CBI | UMASS_PROTO_CBI_I)) { + + err = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, umass_cbi_config, + (sc->sc_proto & UMASS_PROTO_CBI_I) ? + UMASS_T_CBI_MAX : (UMASS_T_CBI_MAX - 2), sc, + &umass_mtx); + + /* skip reset first time */ + sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + + } else { + err = USB_ERR_INVAL; + } + + if (err) { + device_printf(dev, "could not setup required " + "transfers, %s\n", usb2_errstr(err)); + goto detach; + } + sc->sc_transform = + (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform : + (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform : + (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform : + (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform : + &umass_no_transform; + + /* from here onwards the device can be used. */ + + if (sc->sc_quirks & SHUTTLE_INIT) { + umass_init_shuttle(sc); + } + /* get the maximum LUN supported by the device */ + + if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) && + !(sc->sc_quirks & NO_GETMAXLUN)) + sc->sc_maxlun = umass_bbb_get_max_lun(sc); + else + sc->sc_maxlun = 0; + + /* Prepare the SCSI command block */ + sc->cam_scsi_sense.opcode = REQUEST_SENSE; + sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY; + + /* + * some devices need a delay after that the configuration value is + * set to function properly: + */ + usb2_pause_mtx(&Giant, USB_MS_HZ); + + /* register the SIM */ + err = umass_cam_attach_sim(sc); + if (err) { + goto detach; + } + /* scan the SIM */ + umass_cam_attach(sc); + + DPRINTF(sc, UDMASS_GEN, "Attach finished\n"); + + return (0); /* success */ + +detach: + umass_detach(dev); + return (ENXIO); /* failure */ +} + +static int +umass_detach(device_t dev) +{ + struct umass_softc *sc = device_get_softc(dev); + + DPRINTF(sc, UDMASS_USB, "\n"); + + /* teardown our statemachine */ + + usb2_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX); + +#if (__FreeBSD_version >= 700037) + mtx_lock(&umass_mtx); +#endif + umass_cam_detach_sim(sc); + +#if (__FreeBSD_version >= 700037) + mtx_unlock(&umass_mtx); +#endif + + return (0); /* success */ +} + +static void +umass_init_shuttle(struct umass_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t status[2] = {0, 0}; + + /* + * 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); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, sizeof(status)); + err = usb2_do_request(sc->sc_udev, &Giant, &req, &status); + + DPRINTF(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n", + status[0], status[1]); + return; +} + +/* + * Generic functions to handle transfers + */ + +static void +umass_transfer_start(struct umass_softc *sc, uint8_t xfer_index) +{ + DPRINTF(sc, UDMASS_GEN, "transfer index = " + "%d\n", xfer_index); + + if (sc->sc_xfer[xfer_index]) { + sc->sc_last_xfer_index = xfer_index; + usb2_transfer_start(sc->sc_xfer[xfer_index]); + } else { + umass_cancel_ccb(sc); + } + return; +} + +static void +umass_reset(struct umass_softc *sc) +{ + DPRINTF(sc, UDMASS_GEN, "resetting device\n"); + + /* + * stop the last transfer, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]); + umass_transfer_start(sc, 0); + return; +} + +static void +umass_cancel_ccb(struct umass_softc *sc) +{ + union ccb *ccb; + + mtx_assert(&umass_mtx, MA_OWNED); + + ccb = sc->sc_transfer.ccb; + sc->sc_transfer.ccb = NULL; + sc->sc_last_xfer_index = 0; + + if (ccb) { + (sc->sc_transfer.callback) + (sc, ccb, (sc->sc_transfer.data_len - + sc->sc_transfer.actlen), STATUS_WIRE_FAILED); + } + return; +} + +static void +umass_tr_error(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + + if (xfer->error != USB_ERR_CANCELLED) { + + DPRINTF(sc, UDMASS_GEN, "transfer error, %s -> " + "reset\n", usb2_errstr(xfer->error)); + } + umass_cancel_ccb(sc); + return; +} + +/* + * BBB protocol specific functions + */ + +static void +umass_t_bbb_reset1_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + umass_transfer_start(sc, UMASS_T_BBB_RESET2); + return; + + case USB_ST_SETUP: + /* + * 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, using 3 transfers: + * UMASS_T_BBB_RESET1 + * UMASS_T_BBB_RESET2 + * UMASS_T_BBB_RESET3 + */ + + DPRINTF(sc, UDMASS_BBB, "BBB reset!\n"); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_BBB_RESET; /* bulk only reset */ + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + + xfer->frlengths[0] = sizeof(req); + xfer->nframes = 1; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_bbb_reset2_callback(struct usb2_xfer *xfer) +{ + umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3, + UMASS_T_BBB_DATA_READ); + return; +} + +static void +umass_t_bbb_reset3_callback(struct usb2_xfer *xfer) +{ + umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND, + UMASS_T_BBB_DATA_WRITE); + return; +} + +static void +umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer, + uint8_t next_xfer, + uint8_t stall_xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + umass_transfer_start(sc, next_xfer); + return; + + case USB_ST_SETUP: + if (usb2_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) { + goto tr_transferred; + } + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_bbb_command_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + union ccb *ccb = sc->sc_transfer.ccb; + uint32_t tag; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + umass_transfer_start + (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ : + (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE : + UMASS_T_BBB_STATUS)); + return; + + case USB_ST_SETUP: + + sc->sc_status_try = 0; + + if (ccb) { + + /* + * the initial value is not important, + * as long as the values are unique: + */ + tag = UGETDW(sc->cbw.dCBWTag) + 1; + + USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); + USETDW(sc->cbw.dCBWTag, tag); + + /* + * dCBWDataTransferLength: + * 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. + */ + USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len); + + /* + * dCBWFlags: + * 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 + */ + sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ? + CBWFLAGS_IN : CBWFLAGS_OUT); + sc->cbw.bCBWLUN = sc->sc_transfer.lun; + + if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) { + sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB); + DPRINTF(sc, UDMASS_BBB, "Truncating long command!\n"); + } + sc->cbw.bCDBLength = sc->sc_transfer.cmd_len; + + bcopy(sc->sc_transfer.cmd_data, sc->cbw.CBWCDB, + sc->sc_transfer.cmd_len); + + bzero(sc->sc_transfer.cmd_data + sc->sc_transfer.cmd_len, + sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len); + + DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw)); + + usb2_copy_in(xfer->frbuffers, 0, &sc->cbw, sizeof(sc->cbw)); + + xfer->frlengths[0] = sizeof(sc->cbw); + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_bbb_data_read_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (!xfer->flags.ext_buffer) { + usb2_copy_out(xfer->frbuffers, 0, + sc->sc_transfer.data_ptr, xfer->actlen); + } + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->sc_transfer.data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", + max_bulk, sc->sc_transfer.data_rem); + + if (sc->sc_transfer.data_rem == 0) { + umass_transfer_start(sc, UMASS_T_BBB_STATUS); + return; + } + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + xfer->timeout = sc->sc_transfer.data_timeout; + xfer->frlengths[0] = max_bulk; + + if (xfer->flags.ext_buffer) { + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + } + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + umass_tr_error(xfer); + } else { + umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); + } + return; + + } +} + +static void +umass_t_bbb_data_rd_cs_callback(struct usb2_xfer *xfer) +{ + umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, + UMASS_T_BBB_DATA_READ); + return; +} + +static void +umass_t_bbb_data_write_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->sc_transfer.data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n", + max_bulk, sc->sc_transfer.data_rem); + + if (sc->sc_transfer.data_rem == 0) { + umass_transfer_start(sc, UMASS_T_BBB_STATUS); + return; + } + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + xfer->timeout = sc->sc_transfer.data_timeout; + xfer->frlengths[0] = max_bulk; + + if (xfer->flags.ext_buffer) { + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + } else { + usb2_copy_in(xfer->frbuffers, 0, + sc->sc_transfer.data_ptr, max_bulk); + } + + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + umass_tr_error(xfer); + } else { + umass_transfer_start(sc, UMASS_T_BBB_DATA_WR_CS); + } + return; + + } +} + +static void +umass_t_bbb_data_wr_cs_callback(struct usb2_xfer *xfer) +{ + umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS, + UMASS_T_BBB_DATA_WRITE); + return; +} + +static void +umass_t_bbb_status_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + union ccb *ccb = sc->sc_transfer.ccb; + uint32_t residue; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* + * Do a full reset if there is something wrong with the CSW: + */ + sc->sc_status_try = 1; + + /* Zero missing parts of the CSW: */ + + if (xfer->actlen < sizeof(sc->csw)) { + bzero(&sc->csw, sizeof(sc->csw)); + } + usb2_copy_out(xfer->frbuffers, 0, &sc->csw, xfer->actlen); + + DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw)); + + residue = UGETDW(sc->csw.dCSWDataResidue); + + if (!residue) { + residue = (sc->sc_transfer.data_len - + sc->sc_transfer.actlen); + } + if (residue > sc->sc_transfer.data_len) { + DPRINTF(sc, UDMASS_BBB, "truncating residue from %d " + "to %d bytes\n", residue, sc->sc_transfer.data_len); + residue = sc->sc_transfer.data_len; + } + /* translate weird command-status signatures: */ + if (sc->sc_quirks & WRONG_CSWSIG) { + + uint32_t temp = UGETDW(sc->csw.dCSWSignature); + + if ((temp == CSWSIGNATURE_OLYMPUS_C1) || + (temp == CSWSIGNATURE_IMAGINATION_DBX1)) { + USETDW(sc->csw.dCSWSignature, CSWSIGNATURE); + } + } + /* check CSW and handle eventual error */ + if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) { + DPRINTF(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n", + UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE); + /* + * Invalid CSW: Wrong signature or wrong tag might + * indicate that we lost synchronization. Reset the + * device. + */ + goto tr_error; + } else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) { + DPRINTF(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be " + "0x%08x\n", UGETDW(sc->csw.dCSWTag), + UGETDW(sc->cbw.dCBWTag)); + goto tr_error; + } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) { + DPRINTF(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n", + sc->csw.bCSWStatus, CSWSTATUS_PHASE); + goto tr_error; + } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) { + DPRINTF(sc, UDMASS_BBB, "Phase error, residue = " + "%d\n", residue); + goto tr_error; + } else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) { + DPRINTF(sc, UDMASS_BBB, "Buffer overrun %d > %d\n", + sc->sc_transfer.actlen, sc->sc_transfer.data_len); + goto tr_error; + } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { + DPRINTF(sc, UDMASS_BBB, "Command failed, residue = " + "%d\n", residue); + + sc->sc_transfer.ccb = NULL; + + sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; + + (sc->sc_transfer.callback) + (sc, ccb, residue, STATUS_CMD_FAILED); + } else { + sc->sc_transfer.ccb = NULL; + + sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND; + + (sc->sc_transfer.callback) + (sc, ccb, residue, STATUS_CMD_OK); + } + return; + + case USB_ST_SETUP: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: +tr_error: + DPRINTF(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n", + usb2_errstr(xfer->error), sc->sc_status_try); + + if ((xfer->error == USB_ERR_CANCELLED) || + (sc->sc_status_try)) { + umass_tr_error(xfer); + } else { + sc->sc_status_try = 1; + umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS); + } + return; + + } +} + +static void +umass_command_start(struct umass_softc *sc, uint8_t dir, + void *data_ptr, uint32_t data_len, + uint32_t data_timeout, umass_callback_t *callback, + union ccb *ccb) +{ + sc->sc_transfer.lun = ccb->ccb_h.target_lun; + + /* + * NOTE: assumes that "sc->sc_transfer.cmd_data" and + * "sc->sc_transfer.cmd_len" has been properly + * initialized. + */ + + sc->sc_transfer.dir = data_len ? dir : DIR_NONE; + sc->sc_transfer.data_ptr = data_ptr; + sc->sc_transfer.data_len = data_len; + sc->sc_transfer.data_rem = data_len; + sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT); + + sc->sc_transfer.actlen = 0; + sc->sc_transfer.callback = callback; + sc->sc_transfer.ccb = ccb; + + if (sc->sc_xfer[sc->sc_last_xfer_index]) { + usb2_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]); + } else { + ccb->ccb_h.status = CAM_TID_INVALID; + xpt_done(ccb); + } + return; +} + +static uint8_t +umass_bbb_get_max_lun(struct umass_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t buf = 0; + + /* 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); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + + err = usb2_do_request(sc->sc_udev, &Giant, &req, &buf); + if (err) { + buf = 0; + + /* Device doesn't support Get Max Lun request. */ + printf("%s: Get Max Lun not supported (%s)\n", + sc->sc_name, usb2_errstr(err)); + } + return (buf); +} + +/* + * Command/Bulk/Interrupt (CBI) specific functions + */ + +static void +umass_cbi_start_status(struct umass_softc *sc) +{ + if (sc->sc_xfer[UMASS_T_CBI_STATUS]) { + umass_transfer_start(sc, UMASS_T_CBI_STATUS); + } else { + union ccb *ccb = sc->sc_transfer.ccb; + + sc->sc_transfer.ccb = NULL; + + sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + + (sc->sc_transfer.callback) + (sc, ccb, (sc->sc_transfer.data_len - + sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN); + } + return; +} + +static void +umass_t_cbi_reset1_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + struct usb2_device_request req; + uint8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN]; + + uint8_t i; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + umass_transfer_start(sc, UMASS_T_CBI_RESET2); + return; + + case USB_ST_SETUP: + /* + * 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, using 3 transfers: + * UMASS_T_CBI_RESET1 + * UMASS_T_CBI_RESET2 + * UMASS_T_CBI_RESET3 + * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint) + */ + + DPRINTF(sc, UDMASS_CBI, "CBI reset!\n"); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_CBI_ADSC; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN); + + /* + * 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 + * specification) + */ + buf[0] = 0x1d; /* Command Block Reset */ + buf[1] = 0x04; + + for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) { + buf[i] = 0xff; + } + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf)); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = sizeof(buf); + xfer->nframes = 2; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_cbi_reset2_callback(struct usb2_xfer *xfer) +{ + umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3, + UMASS_T_CBI_DATA_READ); + return; +} + +static void +umass_t_cbi_reset3_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + + umass_t_cbi_data_clear_stall_callback + (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] && + sc->sc_xfer[UMASS_T_CBI_STATUS]) ? + UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND, + UMASS_T_CBI_DATA_WRITE); + + return; +} + +static void +umass_t_cbi_reset4_callback(struct usb2_xfer *xfer) +{ + umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND, + UMASS_T_CBI_STATUS); + return; +} + +static void +umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *xfer, + uint8_t next_xfer, + uint8_t stall_xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: +tr_transferred: + if (next_xfer == UMASS_T_CBI_STATUS) { + umass_cbi_start_status(sc); + } else { + umass_transfer_start(sc, next_xfer); + } + return; + + case USB_ST_SETUP: + if (usb2_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) { + goto tr_transferred; /* should not happen */ + } + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_cbi_command_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + union ccb *ccb = sc->sc_transfer.ccb; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (sc->sc_transfer.dir == DIR_NONE) { + umass_cbi_start_status(sc); + } else { + umass_transfer_start + (sc, (sc->sc_transfer.dir == DIR_IN) ? + UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE); + } + return; + + case USB_ST_SETUP: + + if (ccb) { + + /* + * do a CBI transfer with cmd_len bytes from + * cmd_data, possibly a data phase of data_len + * bytes from/to the device and finally a status + * read phase. + */ + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UR_CBI_ADSC; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + req.wLength[0] = sc->sc_transfer.cmd_len; + req.wLength[1] = 0; + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + usb2_copy_in(xfer->frbuffers + 1, 0, sc->sc_transfer.cmd_data, + sc->sc_transfer.cmd_len); + + xfer->frlengths[0] = sizeof(req); + xfer->frlengths[1] = sc->sc_transfer.cmd_len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; + + DIF(UDMASS_CBI, + umass_cbi_dump_cmd(sc, + sc->sc_transfer.cmd_data, + sc->sc_transfer.cmd_len)); + + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + umass_tr_error(xfer); + return; + + } +} + +static void +umass_t_cbi_data_read_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (!xfer->flags.ext_buffer) { + usb2_copy_out(xfer->frbuffers, 0, + sc->sc_transfer.data_ptr, xfer->actlen); + } + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->sc_transfer.data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", + max_bulk, sc->sc_transfer.data_rem); + + if (sc->sc_transfer.data_rem == 0) { + umass_cbi_start_status(sc); + return; + } + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + xfer->timeout = sc->sc_transfer.data_timeout; + + xfer->frlengths[0] = max_bulk; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if ((xfer->error == USB_ERR_CANCELLED) || + (sc->sc_transfer.callback != &umass_cam_cb)) { + umass_tr_error(xfer); + } else { + umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS); + } + return; + + } +} + +static void +umass_t_cbi_data_rd_cs_callback(struct usb2_xfer *xfer) +{ + umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, + UMASS_T_CBI_DATA_READ); + return; +} + +static void +umass_t_cbi_data_write_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.actlen += xfer->actlen; + + if (xfer->actlen < xfer->sumlen) { + /* short transfer */ + sc->sc_transfer.data_rem = 0; + } + case USB_ST_SETUP: + DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n", + max_bulk, sc->sc_transfer.data_rem); + + if (sc->sc_transfer.data_rem == 0) { + umass_cbi_start_status(sc); + return; + } + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + xfer->timeout = sc->sc_transfer.data_timeout; + + if (xfer->flags.ext_buffer) { + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + } else { + usb2_copy_in(xfer->frbuffers, 0, + sc->sc_transfer.data_ptr, max_bulk); + } + + xfer->frlengths[0] = max_bulk; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if ((xfer->error == USB_ERR_CANCELLED) || + (sc->sc_transfer.callback != &umass_cam_cb)) { + umass_tr_error(xfer); + } else { + umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS); + } + return; + + } +} + +static void +umass_t_cbi_data_wr_cs_callback(struct usb2_xfer *xfer) +{ + umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS, + UMASS_T_CBI_DATA_WRITE); + return; +} + +static void +umass_t_cbi_status_callback(struct usb2_xfer *xfer) +{ + struct umass_softc *sc = xfer->priv_sc; + union ccb *ccb = sc->sc_transfer.ccb; + uint32_t residue; + uint8_t status; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < sizeof(sc->sbl)) { + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, &sc->sbl, sizeof(sc->sbl)); + + residue = (sc->sc_transfer.data_len - + sc->sc_transfer.actlen); + + /* dissect the information in the buffer */ + + if (sc->sc_proto & UMASS_PROTO_UFI) { + + /* + * Section 3.4.3.1.3 specifies that the UFI command + * protocol returns an ASC and ASCQ in the interrupt + * data block. + */ + + DPRINTF(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, " + "ASCQ = 0x%02x\n", sc->sbl.ufi.asc, + sc->sbl.ufi.ascq); + + status = (((sc->sbl.ufi.asc == 0) && + (sc->sbl.ufi.ascq == 0)) ? + STATUS_CMD_OK : STATUS_CMD_FAILED); + + sc->sc_transfer.ccb = NULL; + + sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + + (sc->sc_transfer.callback) + (sc, ccb, residue, status); + + return; + + } else { + + /* Command Interrupt Data Block */ + + DPRINTF(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n", + sc->sbl.common.type, sc->sbl.common.value); + + if (sc->sbl.common.type == IDB_TYPE_CCI) { + + status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK); + + status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK : + (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED : + (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED : + STATUS_WIRE_FAILED); + + sc->sc_transfer.ccb = NULL; + + sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + + (sc->sc_transfer.callback) + (sc, ccb, residue, status); + + return; + } + } + + /* fallthrough */ + + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + DPRINTF(sc, UDMASS_CBI, "Failed to read CSW: %s\n", + usb2_errstr(xfer->error)); + umass_tr_error(xfer); + return; + + } +} + +/* + * 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 */ + + if (umass_sim[sc->sc_unit] != NULL) { + sc->sc_sim = umass_sim[sc->sc_unit]; + goto register_only; + } + /* + * 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->sc_sim = cam_sim_alloc + (&umass_cam_action, &umass_cam_poll, + DEVNAME_SIM, + sc /* priv */ , + sc->sc_unit /* unit number */ , +#if (__FreeBSD_version >= 700037) + &umass_mtx /* mutex */ , +#endif + 1 /* maximum device openings */ , + 0 /* maximum tagged device openings */ , + devq); + + if (sc->sc_sim == NULL) { + cam_simq_free(devq); + return (ENOMEM); + } + umass_sim[sc->sc_unit] = sc->sc_sim; + +register_only: + + /* update the softc pointer */ + sc->sc_sim->softc = sc; + +#if (__FreeBSD_version >= 700037) + mtx_lock(&umass_mtx); +#endif + +#if (__FreeBSD_version >= 700048) + if (xpt_bus_register(sc->sc_sim, sc->sc_dev, sc->sc_unit) != CAM_SUCCESS) { + mtx_unlock(&umass_mtx); + return (ENOMEM); + } +#else + if (xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) { +#if (__FreeBSD_version >= 700037) + mtx_unlock(&umass_mtx); +#endif + return (ENOMEM); + } +#endif + +#if (__FreeBSD_version >= 700037) + mtx_unlock(&umass_mtx); +#endif + return (0); +} + +static void +umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) +{ +#if USB_DEBUG + struct umass_softc *sc = NULL; + + if (ccb->ccb_h.status != CAM_REQ_CMP) { + DPRINTF(sc, UDMASS_SCSI, "%s:%d Rescan failed, 0x%04x\n", + periph->periph_name, periph->unit_number, + ccb->ccb_h.status); + } else { + DPRINTF(sc, 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); + return; +} + +static void +umass_cam_rescan(struct umass_softc *sc) +{ + struct cam_path *path; + union ccb *ccb; + + DPRINTF(sc, UDMASS_SCSI, "scbus%d: scanning for %d:%d:%d\n", + cam_sim_path(sc->sc_sim), + cam_sim_path(sc->sc_sim), + sc->sc_unit, CAM_LUN_WILDCARD); + + ccb = malloc(sizeof(*ccb), M_USBDEV, M_WAITOK | M_ZERO); + + if (ccb == NULL) { + return; + } +#if (__FreeBSD_version >= 700037) + mtx_lock(&umass_mtx); +#endif + + if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->sc_sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) + != CAM_REQ_CMP) { +#if (__FreeBSD_version >= 700037) + mtx_unlock(&umass_mtx); +#endif + 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); + +#if (__FreeBSD_version >= 700037) + mtx_unlock(&umass_mtx); +#endif + + /* The scan is in progress now. */ + + return; +} + +static void +umass_cam_attach(struct umass_softc *sc) +{ +#ifndef USB_DEBUG + if (bootverbose) +#endif + printf("%s:%d:%d:%d: Attached to scbus%d\n", + sc->sc_name, cam_sim_path(sc->sc_sim), + sc->sc_unit, CAM_LUN_WILDCARD, + cam_sim_path(sc->sc_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 ). Only do this if we are not + * booting, because CAM does a scan after booting has + * completed, when interrupts have been enabled. + */ + + /* scan the new sim */ + umass_cam_rescan(sc); + } + return; +} + +/* umass_cam_detach + * detach from the CAM layer + */ + +static void +umass_cam_detach_sim(struct umass_softc *sc) +{ + if (sc->sc_sim) { + if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) { +#if 0 /* NOTYET */ + cam_sim_free(sc->sc_sim, /* free_devq */ TRUE); +#else + /* accessing the softc is not possible after this */ + sc->sc_sim->softc = UMASS_GONE; +#endif + } else { + panic("%s: CAM layer is busy!\n", + sc->sc_name); + } + sc->sc_sim = NULL; + } + return; +} + +/* 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; + + if (sc == UMASS_GONE) { + ccb->ccb_h.status = CAM_TID_INVALID; + xpt_done(ccb); + return; + } + if (sc) { +#if (__FreeBSD_version < 700037) + mtx_lock(&umass_mtx); +#endif + } + /* + * 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) { + DPRINTF(sc, UDMASS_GEN, "%s:%d:%d:%d:func_code 0x%04x: " + "Invalid target (target needed)\n", + DEVNAME_SIM, cam_sim_path(sc->sc_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); + goto done; + } + 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(sc, UDMASS_SCSI, "%s:%d:%d:%d:func_code 0x%04x: " + "Invalid target (no wildcard)\n", + DEVNAME_SIM, cam_sim_path(sc->sc_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); + goto done; + } + 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: + { + uint8_t *cmd; + uint8_t dir; + + if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { + cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr); + } else { + cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes); + } + + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: " + "cmd: 0x%02x, flags: 0x%02x, " + "%db cmd/%db data/%db sense\n", + cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, + ccb->ccb_h.target_lun, cmd[0], + ccb->ccb_h.flags & CAM_DIR_MASK, ccb->csio.cdb_len, + ccb->csio.dxfer_len, ccb->csio.sense_len); + + if (sc->sc_transfer.ccb) { + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: " + "I/O in progress, deferring\n", + cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, + ccb->ccb_h.target_lun); + ccb->ccb_h.status = CAM_SCSI_BUSY; + xpt_done(ccb); + goto done; + } + switch (ccb->ccb_h.flags & CAM_DIR_MASK) { + case CAM_DIR_IN: + dir = DIR_IN; + break; + case CAM_DIR_OUT: + dir = DIR_OUT; + DIF(UDMASS_SCSI, + umass_dump_buffer(sc, ccb->csio.data_ptr, + ccb->csio.dxfer_len, 48)); + break; + default: + dir = DIR_NONE; + } + + ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED; + + /* + * sc->sc_transform will convert the command to the + * command format needed by the specific command set + * and return the converted command in + * "sc->sc_transfer.cmd_data" + */ + if (umass_std_transform(sc, ccb, cmd, ccb->csio.cdb_len)) { + + if (sc->sc_transfer.cmd_data[0] == INQUIRY) { + + /* + * Handle EVPD inquiry for broken devices first + * NO_INQUIRY also implies NO_INQUIRY_EVPD + */ + if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) && + (sc->sc_transfer.cmd_data[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); + goto done; + } + /* + * Return fake inquiry data for + * broken devices + */ + if (sc->sc_quirks & NO_INQUIRY) { + memcpy(ccb->csio.data_ptr, &fake_inq_data, + sizeof(fake_inq_data)); + ccb->csio.scsi_status = SCSI_STATUS_OK; + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + goto done; + } + if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { + ccb->csio.dxfer_len = SHORT_INQUIRY_LENGTH; + } + } else if (sc->sc_transfer.cmd_data[0] == SYNCHRONIZE_CACHE) { + if (sc->sc_quirks & NO_SYNCHRONIZE_CACHE) { + ccb->csio.scsi_status = SCSI_STATUS_OK; + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + goto done; + } + } + umass_command_start(sc, dir, ccb->csio.data_ptr, + ccb->csio.dxfer_len, + ccb->ccb_h.timeout, + &umass_cam_cb, ccb); + } + break; + } + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_PATH_INQ:.\n", + sc ? cam_sim_path(sc->sc_sim) : -1, 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; + strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strlcpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN); + strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->bus_id = sc->sc_unit; +#if (__FreeBSD_version >= 700025) + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_2; + cpi->transport = XPORT_USB; + cpi->transport_version = 0; +#endif + if (sc == NULL) { + cpi->base_transfer_speed = 0; + cpi->max_lun = 0; + } else { + if (sc->sc_quirks & FLOPPY_SPEED) { + cpi->base_transfer_speed = + UMASS_FLOPPY_TRANSFER_SPEED; + } else if (usb2_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->sc_maxlun; + } + + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_RESET_DEV: + { + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_RESET_DEV:.\n", + cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, + ccb->ccb_h.target_lun); + + umass_reset(sc); + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; + + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_GET_TRAN_SETTINGS:.\n", + cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id, + ccb->ccb_h.target_lun); + +#if (__FreeBSD_version >= 700025) + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_2; + cts->transport = XPORT_USB; + cts->transport_version = 0; + cts->xport_specific.valid = 0; +#else + cts->valid = 0; + cts->flags = 0; /* no disconnection, tagging */ +#endif + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n", + cam_sim_path(sc->sc_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(sc, UDMASS_SCSI, "%d:%d:%d:XPT_NOOP:.\n", + sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id, + ccb->ccb_h.target_lun); + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + default: + DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:func_code 0x%04x: " + "Not implemented\n", + sc ? cam_sim_path(sc->sc_sim) : -1, 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; + } + +done: +#if (__FreeBSD_version < 700037) + if (sc) { + mtx_unlock(&umass_mtx); + } +#endif + return; +} + +static void +umass_cam_poll(struct cam_sim *sim) +{ + struct umass_softc *sc = (struct umass_softc *)sim->softc; + + if (sc == UMASS_GONE) + return; + + DPRINTF(sc, UDMASS_SCSI, "CAM poll\n"); + + usb2_do_poll(sc->sc_xfer, UMASS_T_MAX); + return; +} + + +/* umass_cam_cb + * finalise a completed CAM command + */ + +static void +umass_cam_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, + uint8_t status) +{ + ccb->csio.resid = residue; + + switch (status) { + case STATUS_CMD_OK: + ccb->ccb_h.status = CAM_REQ_CMP; + if ((sc->sc_quirks & READ_CAPACITY_OFFBY1) && + (ccb->ccb_h.func_code == XPT_SCSI_IO) && + (ccb->csio.cdb_io.cdb_bytes[0] == READ_CAPACITY)) { + struct scsi_read_capacity_data *rcap; + uint32_t maxsector; + + rcap = (void *)(ccb->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: + + /* fetch sense data */ + + /* the rest of the command was filled in at attach */ + sc->cam_scsi_sense.length = ccb->csio.sense_len; + + DPRINTF(sc, UDMASS_SCSI, "Fetching %d bytes of " + "sense data\n", ccb->csio.sense_len); + + if (umass_std_transform(sc, ccb, &sc->cam_scsi_sense.opcode, + sizeof(sc->cam_scsi_sense))) { + + if ((sc->sc_quirks & FORCE_SHORT_INQUIRY) && + (sc->sc_transfer.cmd_data[0] == INQUIRY)) { + ccb->csio.sense_len = SHORT_INQUIRY_LENGTH; + } + umass_command_start(sc, DIR_IN, &ccb->csio.sense_data.error_code, + ccb->csio.sense_len, ccb->ccb_h.timeout, + &umass_cam_sense_cb, ccb); + } + break; + + default: + /* + * 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; + } +} + +/* + * Finalise a completed autosense operation + */ +static void +umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, + uint8_t status) +{ + uint8_t *cmd; + uint8_t key; + + switch (status) { + case STATUS_CMD_OK: + case STATUS_CMD_UNKNOWN: + case STATUS_CMD_FAILED: + + if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) { + cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr); + } else { + cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes); + } + + key = (ccb->csio.sense_data.flags & SSD_KEY); + + /* + * Getting sense data always succeeds (apart from wire + * failures): + */ + if ((sc->sc_quirks & RS_NO_CLEAR_UA) && + (cmd[0] == INQUIRY) && + (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 (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->sc_quirks & RS_NO_CLEAR_UA) && + (cmd[0] == READ_CAPACITY) && + (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; + ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; + +#if 0 + DELAY(300000); +#endif + DPRINTF(sc, UDMASS_SCSI, "Doing a sneaky" + "TEST_UNIT_READY\n"); + + /* the rest of the command was filled in at attach */ + + if (umass_std_transform(sc, ccb, + &sc->cam_scsi_test_unit_ready.opcode, + sizeof(sc->cam_scsi_test_unit_ready))) { + umass_command_start(sc, DIR_NONE, NULL, 0, + ccb->ccb_h.timeout, + &umass_cam_quirk_cb, ccb); + } + break; + } else { + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR + | CAM_AUTOSNS_VALID; + ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; + } + xpt_done(ccb); + break; + + default: + DPRINTF(sc, UDMASS_SCSI, "Autosense failed, " + "status %d\n", status); + ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; + xpt_done(ccb); + } + return; +} + +/* + * 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, union ccb *ccb, uint32_t residue, + uint8_t status) +{ + DPRINTF(sc, UDMASS_SCSI, "Test unit ready " + "returned status %d\n", status); + + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR + | CAM_AUTOSNS_VALID; + ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; + xpt_done(ccb); + return; +} + +/* + * SCSI specific functions + */ + +static uint8_t +umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, + uint8_t cmd_len) +{ + if ((cmd_len == 0) || + (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { + DPRINTF(sc, UDMASS_SCSI, "Invalid command " + "length: %d bytes\n", cmd_len); + return (0); /* failure */ + } + sc->sc_transfer.cmd_len = cmd_len; + + switch (cmd_ptr[0]) { + case TEST_UNIT_READY: + if (sc->sc_quirks & NO_TEST_UNIT_READY) { + DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " + "to START_UNIT\n"); + bzero(sc->sc_transfer.cmd_data, cmd_len); + sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; + sc->sc_transfer.cmd_data[4] = SSS_START; + return (1); + } + break; + + case INQUIRY: + /* + * some drives wedge when asked for full inquiry + * information. + */ + if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; + return (1); + } + break; + } + + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + return (1); +} + +static uint8_t +umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) +{ + if ((cmd_len == 0) || + (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { + DPRINTF(sc, UDMASS_SCSI, "Invalid command " + "length: %d bytes\n", cmd_len); + return (0); /* failure */ + } + switch (cmd_ptr[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: + + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + + if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) { + bzero(sc->sc_transfer.cmd_data + cmd_len, 12 - cmd_len); + cmd_len = 12; + } + sc->sc_transfer.cmd_len = cmd_len; + return (1); /* sucess */ + + /* All other commands are not legal in RBC */ + default: + DPRINTF(sc, UDMASS_SCSI, "Unsupported RBC " + "command 0x%02x\n", cmd_ptr[0]); + return (0); /* failure */ + } +} + +static uint8_t +umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, + uint8_t cmd_len) +{ + if ((cmd_len == 0) || + (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { + DPRINTF(sc, UDMASS_SCSI, "Invalid command " + "length: %d bytes\n", cmd_len); + return (0); /* failure */ + } + /* An UFI command is always 12 bytes in length */ + sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH; + + /* Zero the command data */ + bzero(sc->sc_transfer.cmd_data, UFI_COMMAND_LENGTH); + + switch (cmd_ptr[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->sc_quirks & NO_TEST_UNIT_READY) { + /* + * Some devices do not support this command. Start + * Stop Unit should give the same results + */ + DPRINTF(sc, UDMASS_UFI, "Converted TEST_UNIT_READY " + "to START_UNIT\n"); + + sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; + sc->sc_transfer.cmd_data[4] = SSS_START; + return (1); + } + break; + + 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: + break; + + /* + * 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: + DPRINTF(sc, UDMASS_SCSI, "Unsupported UFI " + "command 0x%02x\n", cmd_ptr[0]); + return (0); /* failure */ + } + + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + return (1); /* success */ +} + +/* + * 8070i (ATAPI) specific functions + */ +static uint8_t +umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, + uint8_t cmd_len) +{ + if ((cmd_len == 0) || + (cmd_len > sizeof(sc->sc_transfer.cmd_data))) { + DPRINTF(sc, UDMASS_SCSI, "Invalid command " + "length: %d bytes\n", cmd_len); + return (0); /* failure */ + } + /* An ATAPI command is always 12 bytes in length. */ + sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH; + + /* Zero the command data */ + bzero(sc->sc_transfer.cmd_data, ATAPI_COMMAND_LENGTH); + + switch (cmd_ptr[0]) { + /* + * Commands of which the format has been verified. They + * should work. Copy the command into the destination + * buffer. + */ + case INQUIRY: + /* + * some drives wedge when asked for full inquiry + * information. + */ + if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + + sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; + return (1); + } + break; + + case TEST_UNIT_READY: + if (sc->sc_quirks & NO_TEST_UNIT_READY) { + DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " + "to START_UNIT\n"); + sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; + sc->sc_transfer.cmd_data[4] = SSS_START; + return (1); + } + break; + + 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 */ + break;; + + case READ_12: + case WRITE_12: + default: + DPRINTF(sc, UDMASS_SCSI, "Unsupported ATAPI " + "command 0x%02x - trying anyway\n", + cmd_ptr[0]); + break;; + } + + bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + return (1); /* success */ +} + +static uint8_t +umass_no_transform(struct umass_softc *sc, uint8_t *cmd, + uint8_t cmdlen) +{ + return (0); /* failure */ +} + +static uint8_t +umass_std_transform(struct umass_softc *sc, union ccb *ccb, + uint8_t *cmd, uint8_t cmdlen) +{ + uint8_t retval; + + retval = (sc->sc_transform) (sc, cmd, cmdlen); + + if (retval == 2) { + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return (0); + } else if (retval == 0) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return (0); + } + /* Command should be executed */ + return (1); +} + +#if USB_DEBUG +static void +umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw) +{ + uint8_t *c = cbw->CBWCDB; + + uint32_t dlen = UGETDW(cbw->dCBWDataTransferLength); + uint32_t tag = UGETDW(cbw->dCBWTag); + + uint8_t clen = cbw->bCDBLength; + uint8_t flags = cbw->bCBWFlags; + uint8_t lun = cbw->bCBWLUN; + + DPRINTF(sc, UDMASS_BBB, "CBW %d: cmd = %db " + "(0x%02x%02x%02x%02x%02x%02x%s), " + "data = %db, lun = %d, dir = %s\n", + tag, clen, + c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6 ? "..." : ""), + dlen, lun, (flags == CBWFLAGS_IN ? "in" : + (flags == CBWFLAGS_OUT ? "out" : ""))); + return; +} + +static void +umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw) +{ + uint32_t sig = UGETDW(csw->dCSWSignature); + uint32_t tag = UGETDW(csw->dCSWTag); + uint32_t res = UGETDW(csw->dCSWDataResidue); + uint8_t status = csw->bCSWStatus; + + DPRINTF(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = 0x%08x, " + "res = %d, status = 0x%02x (%s)\n", + tag, sig, (sig == CSWSIGNATURE ? "valid" : "invalid"), + tag, res, + status, (status == CSWSTATUS_GOOD ? "good" : + (status == CSWSTATUS_FAILED ? "failed" : + (status == CSWSTATUS_PHASE ? "phase" : "")))); + return; +} + +static void +umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, uint8_t cmdlen) +{ + uint8_t *c = cmd; + uint8_t dir = sc->sc_transfer.dir; + + DPRINTF(sc, UDMASS_BBB, "cmd = %db " + "(0x%02x%02x%02x%02x%02x%02x%s), " + "data = %db, dir = %s\n", + cmdlen, + c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6 ? "..." : ""), + sc->sc_transfer.data_len, + (dir == DIR_IN ? "in" : + (dir == DIR_OUT ? "out" : + (dir == DIR_NONE ? "no data phase" : "")))); + return; +} + +static void +umass_dump_buffer(struct umass_softc *sc, uint8_t *buffer, uint32_t buflen, + uint32_t printlen) +{ + uint32_t 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(sc, UDMASS_GEN, "0x %s%s\n", + s1, s2); + s2[0] = '\0'; + } + sprintf(&s1[j * 2], "%02x", buffer[i] & 0xff); + } + if (buflen > printlen) + sprintf(s3, " ..."); + DPRINTF(sc, UDMASS_GEN, "0x %s%s%s\n", + s1, s2, s3); + return; +} + +#endif + +static int +umass_driver_loaded(struct module *mod, int what, void *arg) +{ + uint16_t x; + + switch (what) { + case MOD_LOAD: + mtx_init(&umass_mtx, "UMASS lock", NULL, (MTX_DEF | MTX_RECURSE)); + break; + + case MOD_UNLOAD: + for (x = 0; x != UMASS_MAXUNIT; x++) { + /* cleanup */ + if (umass_sim[x]) + cam_sim_free(umass_sim[x], /* free_devq */ TRUE); + } + mtx_destroy(&umass_mtx); + break; + default: + return (EOPNOTSUPP); + } + + return (0); +} diff --git a/sys/dev/usb2/storage/urio2.c b/sys/dev/usb2/storage/urio2.c new file mode 100644 index 000000000000..775c3cb4d8b3 --- /dev/null +++ b/sys/dev/usb2/storage/urio2.c @@ -0,0 +1,491 @@ +/*- + * 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 +__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 +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR urio_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USB_DEBUG +static int urio_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio"); +SYSCTL_INT(_hw_usb2_urio, OID_AUTO, debug, CTLFLAG_RW, + &urio_debug, 0, "urio debug level"); +#endif + +#define URIO_T_WR 0 +#define URIO_T_RD 1 +#define URIO_T_WR_CS 2 +#define URIO_T_RD_CS 3 +#define URIO_T_MAX 4 + +#define URIO_BSIZE (1<<12) /* bytes */ +#define URIO_IFQ_MAXLEN 2 /* units */ + +struct urio_softc { + struct usb2_fifo_sc sc_fifo; + struct mtx sc_mtx; + + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[URIO_T_MAX]; + + uint8_t sc_flags; +#define URIO_FLAG_READ_STALL 0x01 /* read transfer stalled */ +#define URIO_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ + + uint8_t sc_name[16]; +}; + +/* prototypes */ + +static device_probe_t urio_probe; +static device_attach_t urio_attach; +static device_detach_t urio_detach; + +static usb2_callback_t urio_write_callback; +static usb2_callback_t urio_write_clear_stall_callback; +static usb2_callback_t urio_read_callback; +static usb2_callback_t urio_read_clear_stall_callback; + +static usb2_fifo_close_t urio_close; +static usb2_fifo_cmd_t urio_start_read; +static usb2_fifo_cmd_t urio_start_write; +static usb2_fifo_cmd_t urio_stop_read; +static usb2_fifo_cmd_t urio_stop_write; +static usb2_fifo_ioctl_t urio_ioctl; +static usb2_fifo_open_t urio_open; + +static struct usb2_fifo_methods urio_fifo_methods = { + .f_close = &urio_close, + .f_ioctl = &urio_ioctl, + .f_open = &urio_open, + .f_start_read = &urio_start_read, + .f_start_write = &urio_start_write, + .f_stop_read = &urio_stop_read, + .f_stop_write = &urio_stop_write, + .basename[0] = "urio", +}; + +static const struct usb2_config urio_config[URIO_T_MAX] = { + [URIO_T_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = URIO_BSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,}, + .mh.callback = &urio_write_callback, + }, + + [URIO_T_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = URIO_BSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, + .mh.callback = &urio_read_callback, + }, + + [URIO_T_WR_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &urio_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [URIO_T_RD_CS] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &urio_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t urio_devclass; + +static device_method_t urio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, urio_probe), + DEVMETHOD(device_attach, urio_attach), + DEVMETHOD(device_detach, urio_detach), + {0, 0} +}; + +static driver_t urio_driver = { + .name = "urio", + .methods = urio_methods, + .size = sizeof(struct urio_softc), +}; + +DRIVER_MODULE(urio, ushub, urio_driver, urio_devclass, NULL, 0); +MODULE_DEPEND(urio, usb2_storage, 1, 1, 1); +MODULE_DEPEND(urio, usb2_core, 1, 1, 1); + +static int +urio_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if ((((uaa->info.idVendor == USB_VENDOR_DIAMOND) && + (uaa->info.idProduct == USB_PRODUCT_DIAMOND_RIO500USB)) || + ((uaa->info.idVendor == USB_VENDOR_DIAMOND2) && + ((uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO600USB) || + (uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO800USB))))) + return (0); + else + return (ENXIO); +} + +static int +urio_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct urio_softc *sc = device_get_softc(dev); + int error; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + mtx_init(&sc->sc_mtx, "urio lock", NULL, MTX_DEF | MTX_RECURSE); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + error = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, + urio_config, URIO_T_MAX, sc, &sc->sc_mtx); + + if (error) { + DPRINTF("error=%s\n", usb2_errstr(error)); + goto detach; + } + /* set interface permissions */ + usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644); + + error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, + &urio_fifo_methods, &sc->sc_fifo, + device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); + if (error) { + goto detach; + } + return (0); /* success */ + +detach: + urio_detach(dev); + return (ENOMEM); /* failure */ +} + +static void +urio_write_callback(struct usb2_xfer *xfer) +{ + struct urio_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX]; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: + if (sc->sc_flags & URIO_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); + return; + } + if (usb2_fifo_get_data(f, xfer->frbuffers, 0, + xfer->max_data_length, &actlen, 0)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= URIO_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); + } + return; + } +} + +static void +urio_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct urio_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_WR]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~URIO_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +urio_read_callback(struct usb2_xfer *xfer) +{ + struct urio_softc *sc = xfer->priv_sc; + struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_fifo_put_data(f, xfer->frbuffers, 0, + xfer->actlen, 1); + + case USB_ST_SETUP: + if (sc->sc_flags & URIO_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); + return; + } + if (usb2_fifo_put_bytes_max(f) != 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= URIO_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); + } + return; + } +} + +static void +urio_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct urio_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_RD]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~URIO_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +urio_start_read(struct usb2_fifo *fifo) +{ + struct urio_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[URIO_T_RD]); + return; +} + +static void +urio_stop_read(struct usb2_fifo *fifo) +{ + struct urio_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[URIO_T_RD_CS]); + usb2_transfer_stop(sc->sc_xfer[URIO_T_RD]); + return; +} + +static void +urio_start_write(struct usb2_fifo *fifo) +{ + struct urio_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[URIO_T_WR]); + return; +} + +static void +urio_stop_write(struct usb2_fifo *fifo) +{ + struct urio_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[URIO_T_WR_CS]); + usb2_transfer_stop(sc->sc_xfer[URIO_T_WR]); + return; +} + +static int +urio_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct urio_softc *sc = fifo->priv_sc0; + + if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) { + return (EACCES); + } + if (fflags & FREAD) { + /* clear stall first */ + mtx_lock(&sc->sc_mtx); + sc->sc_flags |= URIO_FLAG_READ_STALL; + mtx_unlock(&sc->sc_mtx); + + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[URIO_T_RD]->max_data_length, + URIO_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + if (fflags & FWRITE) { + /* clear stall first */ + sc->sc_flags |= URIO_FLAG_WRITE_STALL; + + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[URIO_T_WR]->max_data_length, + URIO_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + return (0); /* success */ +} + +static void +urio_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & (FREAD | FWRITE)) { + usb2_fifo_free_buffer(fifo); + } + return; +} + +static int +urio_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, + int fflags, struct thread *td) +{ + struct usb2_ctl_request ur; + struct RioCommand *rio_cmd; + int error; + + switch (cmd) { + case RIO_RECV_COMMAND: + if (!(fflags & FWRITE)) { + error = EPERM; + goto done; + } + bzero(&ur, sizeof(ur)); + rio_cmd = addr; + ur.ucr_request.bmRequestType = + rio_cmd->requesttype | UT_READ_VENDOR_DEVICE; + break; + + case RIO_SEND_COMMAND: + if (!(fflags & FWRITE)) { + error = EPERM; + goto done; + } + bzero(&ur, sizeof(ur)); + rio_cmd = addr; + ur.ucr_request.bmRequestType = + rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE; + break; + + default: + error = EINVAL; + goto done; + } + + DPRINTFN(2, "Sending command\n"); + + /* Send rio control message */ + ur.ucr_request.bRequest = rio_cmd->request; + USETW(ur.ucr_request.wValue, rio_cmd->value); + USETW(ur.ucr_request.wIndex, rio_cmd->index); + USETW(ur.ucr_request.wLength, rio_cmd->length); + ur.ucr_data = rio_cmd->buffer; + + /* reuse generic USB code */ + error = ugen_do_request(fifo, &ur); + +done: + return (error); +} + +static int +urio_detach(device_t dev) +{ + struct urio_softc *sc = device_get_softc(dev); + + DPRINTF("\n"); + + usb2_fifo_detach(&sc->sc_fifo); + + usb2_transfer_unsetup(sc->sc_xfer, URIO_T_MAX); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} diff --git a/sys/dev/usb2/storage/usb2_storage.c b/sys/dev/usb2/storage/usb2_storage.c new file mode 100644 index 000000000000..c8233efb4581 --- /dev/null +++ b/sys/dev/usb2/storage/usb2_storage.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_storage, 1); +MODULE_DEPEND(usb2_storage, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/storage/usb2_storage.h b/sys/dev/usb2/storage/usb2_storage.h new file mode 100644 index 000000000000..f40828a9b3b2 --- /dev/null +++ b/sys/dev/usb2/storage/usb2_storage.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_STORAGE_H_ +#define _USB2_STORAGE_H_ + +#endif /* _USB2_STORAGE_H_ */ diff --git a/sys/dev/usb2/storage/ustorage2_fs.c b/sys/dev/usb2/storage/ustorage2_fs.c new file mode 100644 index 000000000000..b7977de228bb --- /dev/null +++ b/sys/dev/usb2/storage/ustorage2_fs.c @@ -0,0 +1,1906 @@ +/* $FreeBSD$ */ +/*- + * Copyright (C) 2003-2005 Alan Stern + * Copyright (C) 2008 Hans Petter Selasky + * 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. + * 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 names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +/* + * NOTE: Much of the SCSI statemachine handling code derives from the + * Linux USB gadget stack. + */ +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR ustorage_fs_debug + +#include +#include +#include +#include +#include +#include + +#if USB_DEBUG +static int ustorage_fs_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ustorage_fs, CTLFLAG_RW, 0, "USB ustorage_fs"); +SYSCTL_INT(_hw_usb2_ustorage_fs, OID_AUTO, debug, CTLFLAG_RW, + &ustorage_fs_debug, 0, "ustorage_fs debug level"); +#endif + +/* Define some limits */ + +#define USTORAGE_FS_BULK_SIZE (1 << 17) +#define USTORAGE_FS_MAX_LUN 8 +#define USTORAGE_FS_RELEASE 0x0101 +#define USTORAGE_FS_RAM_SECT (1 << 13) + +static uint8_t *ustorage_fs_ramdisk; + +/* USB transfer definitions */ + +#define USTORAGE_FS_T_BBB_COMMAND 0 +#define USTORAGE_FS_T_BBB_DATA_DUMP 1 +#define USTORAGE_FS_T_BBB_DATA_READ 2 +#define USTORAGE_FS_T_BBB_DATA_WRITE 3 +#define USTORAGE_FS_T_BBB_STATUS 4 +#define USTORAGE_FS_T_BBB_MAX 5 + +/* USB data stage direction */ + +#define DIR_NONE 0 +#define DIR_READ 1 +#define DIR_WRITE 2 + +/* USB interface specific control request */ + +#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 ustorage_fs_bbb_cbw_t; + +#define USTORAGE_FS_BBB_CBW_SIZE 31 + +/* Command Status Wrapper */ +typedef struct { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} __packed ustorage_fs_bbb_csw_t; + +#define USTORAGE_FS_BBB_CSW_SIZE 13 + +struct ustorage_fs_lun { + + void *memory_image; + + uint32_t num_sectors; + uint32_t sense_data; + uint32_t sense_data_info; + uint32_t unit_attention_data; + + uint8_t read_only:1; + uint8_t prevent_medium_removal:1; + uint8_t info_valid:1; + uint8_t removable:1; +}; + +struct ustorage_fs_softc { + + ustorage_fs_bbb_cbw_t sc_cbw; /* Command Wrapper Block */ + ustorage_fs_bbb_csw_t sc_csw; /* Command Status Block */ + + struct mtx sc_mtx; + + struct ustorage_fs_lun sc_lun[USTORAGE_FS_MAX_LUN]; + + struct { + uint8_t *data_ptr; + struct ustorage_fs_lun *currlun; + + uint32_t data_rem; /* bytes, as reported by the command + * block wrapper */ + uint32_t offset; /* bytes */ + + uint8_t cbw_dir; + uint8_t cmd_dir; + uint8_t lun; + uint8_t cmd_data[CBWCDBLENGTH]; + uint8_t cmd_len; + uint8_t data_short:1; + uint8_t data_error:1; + } sc_transfer; + + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[USTORAGE_FS_T_BBB_MAX]; + + uint32_t sc_unit; + + uint8_t sc_name[16]; + uint8_t sc_iface_no; /* interface number */ + uint8_t sc_last_lun; + uint8_t sc_last_xfer_index; + uint8_t sc_qdata[1024]; +}; + +/* prototypes */ + +static device_probe_t ustorage_fs_probe; +static device_attach_t ustorage_fs_attach; +static device_detach_t ustorage_fs_detach; +static device_suspend_t ustorage_fs_suspend; +static device_resume_t ustorage_fs_resume; +static device_shutdown_t ustorage_fs_shutdown; +static usb2_handle_request_t ustorage_fs_handle_request; + +static usb2_callback_t ustorage_fs_t_bbb_command_callback; +static usb2_callback_t ustorage_fs_t_bbb_data_dump_callback; +static usb2_callback_t ustorage_fs_t_bbb_data_read_callback; +static usb2_callback_t ustorage_fs_t_bbb_data_write_callback; +static usb2_callback_t ustorage_fs_t_bbb_status_callback; + +static void ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index); +static void ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc); + +static uint8_t ustorage_fs_verify(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_inquiry(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_request_sense(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_read_capacity(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_mode_sense(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_start_stop(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_mode_select(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask); +static uint8_t ustorage_fs_read(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_write(struct ustorage_fs_softc *sc); +static uint8_t ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t cmd_size, uint16_t mask, uint8_t needs_medium); +static uint8_t ustorage_fs_do_cmd(struct ustorage_fs_softc *sc); + +static device_method_t ustorage_fs_methods[] = { + /* USB interface */ + DEVMETHOD(usb2_handle_request, ustorage_fs_handle_request), + + /* Device interface */ + DEVMETHOD(device_probe, ustorage_fs_probe), + DEVMETHOD(device_attach, ustorage_fs_attach), + DEVMETHOD(device_detach, ustorage_fs_detach), + DEVMETHOD(device_suspend, ustorage_fs_suspend), + DEVMETHOD(device_resume, ustorage_fs_resume), + DEVMETHOD(device_shutdown, ustorage_fs_shutdown), + + {0, 0} +}; + +static driver_t ustorage_fs_driver = { + .name = "ustorage_fs", + .methods = ustorage_fs_methods, + .size = sizeof(struct ustorage_fs_softc), +}; + +static devclass_t ustorage_fs_devclass; + +DRIVER_MODULE(ustorage_fs, ushub, ustorage_fs_driver, ustorage_fs_devclass, NULL, 0); +MODULE_VERSION(ustorage_fs, 0); +MODULE_DEPEND(ustorage_fs, usb2_storage, 1, 1, 1); +MODULE_DEPEND(ustorage_fs, usb2_core, 1, 1, 1); + +struct usb2_config ustorage_fs_bbb_config[USTORAGE_FS_T_BBB_MAX] = { + + [USTORAGE_FS_T_BBB_COMMAND] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .md.bufsize = sizeof(ustorage_fs_bbb_cbw_t), + .md.flags = {.ext_buffer = 1,}, + .md.callback = &ustorage_fs_t_bbb_command_callback, + }, + + [USTORAGE_FS_T_BBB_DATA_DUMP] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .md.bufsize = 0, /* use wMaxPacketSize */ + .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .md.callback = &ustorage_fs_t_bbb_data_dump_callback, + }, + + [USTORAGE_FS_T_BBB_DATA_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .md.bufsize = USTORAGE_FS_BULK_SIZE, + .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, + .md.callback = &ustorage_fs_t_bbb_data_read_callback, + }, + + [USTORAGE_FS_T_BBB_DATA_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .md.bufsize = USTORAGE_FS_BULK_SIZE, + .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, + .md.callback = &ustorage_fs_t_bbb_data_write_callback, + }, + + [USTORAGE_FS_T_BBB_STATUS] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .md.bufsize = sizeof(ustorage_fs_bbb_csw_t), + .md.flags = {.short_xfer_ok = 1,.ext_buffer = 1,}, + .md.callback = &ustorage_fs_t_bbb_status_callback, + }, +}; + +/* + * USB device probe/attach/detach + */ + +static int +ustorage_fs_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + + if (uaa->usb2_mode != USB_MODE_DEVICE) { + return (ENXIO); + } + if (uaa->use_generic == 0) { + /* give other drivers a try first */ + return (ENXIO); + } + /* Check for a standards compliant device */ + id = usb2_get_interface_descriptor(uaa->iface); + if ((id == NULL) || + (id->bInterfaceClass != UICLASS_MASS) || + (id->bInterfaceSubClass != UISUBCLASS_SCSI) || + (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) { + return (ENXIO); + } + return (0); +} + +static int +ustorage_fs_attach(device_t dev) +{ + struct ustorage_fs_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface_descriptor *id; + int err; + + if (sc == NULL) { + return (ENOMEM); + } + /* + * NOTE: the softc struct is bzero-ed in device_set_driver. + * We can safely call ustorage_fs_detach without specifically + * initializing the struct. + */ + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + sc->sc_unit = device_get_unit(dev); + + if (sc->sc_unit == 0) { + if (ustorage_fs_ramdisk == NULL) { + /* + * allocate a memory image for our ramdisk until + * further + */ + ustorage_fs_ramdisk = + malloc(USTORAGE_FS_RAM_SECT << 9, M_USB, M_ZERO | M_WAITOK); + if (ustorage_fs_ramdisk == NULL) { + return (ENOMEM); + } + } + sc->sc_lun[0].memory_image = ustorage_fs_ramdisk; + sc->sc_lun[0].num_sectors = USTORAGE_FS_RAM_SECT; + sc->sc_lun[0].removable = 1; + } + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "USTORAGE_FS lock", + NULL, (MTX_DEF | MTX_RECURSE)); + + /* get interface index */ + + id = usb2_get_interface_descriptor(uaa->iface); + if (id == NULL) { + device_printf(dev, "failed to get " + "interface number\n"); + goto detach; + } + sc->sc_iface_no = id->bInterfaceNumber; + + err = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, ustorage_fs_bbb_config, + USTORAGE_FS_T_BBB_MAX, sc, &sc->sc_mtx); + if (err) { + device_printf(dev, "could not setup required " + "transfers, %s\n", usb2_errstr(err)); + goto detach; + } + /* start Mass Storage State Machine */ + + mtx_lock(&sc->sc_mtx); + ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND); + mtx_unlock(&sc->sc_mtx); + + return (0); /* success */ + +detach: + ustorage_fs_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ustorage_fs_detach(device_t dev) +{ + struct ustorage_fs_softc *sc = device_get_softc(dev); + + /* teardown our statemachine */ + + usb2_transfer_unsetup(sc->sc_xfer, USTORAGE_FS_T_BBB_MAX); + + mtx_destroy(&sc->sc_mtx); + + return (0); /* success */ +} + +static int +ustorage_fs_suspend(device_t dev) +{ + device_printf(dev, "suspending\n"); + return (0); /* success */ +} + +static int +ustorage_fs_resume(device_t dev) +{ + device_printf(dev, "resuming\n"); + return (0); /* success */ +} + +static int +ustorage_fs_shutdown(device_t dev) +{ + return (0); /* success */ +} + +/* + * Generic functions to handle transfers + */ + +static void +ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index) +{ + if (sc->sc_xfer[xfer_index]) { + sc->sc_last_xfer_index = xfer_index; + usb2_transfer_start(sc->sc_xfer[xfer_index]); + } + return; +} + +static void +ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc) +{ + usb2_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]); + mtx_unlock(&sc->sc_mtx); + usb2_transfer_drain(sc->sc_xfer[sc->sc_last_xfer_index]); + mtx_lock(&sc->sc_mtx); + return; +} + +static int +ustorage_fs_handle_request(device_t dev, + const void *preq, void **pptr, uint16_t *plen, + uint16_t offset, uint8_t is_complete) +{ + struct ustorage_fs_softc *sc = device_get_softc(dev); + const struct usb2_device_request *req = preq; + + if (!is_complete) { + if (req->bRequest == UR_BBB_RESET) { + *plen = 0; + mtx_lock(&sc->sc_mtx); + ustorage_fs_transfer_stop(sc); + sc->sc_transfer.data_error = 1; + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_COMMAND); + mtx_unlock(&sc->sc_mtx); + return (0); + } else if (req->bRequest == UR_BBB_GET_MAX_LUN) { + if (offset == 0) { + *plen = 1; + *pptr = &sc->sc_last_lun; + } else { + *plen = 0; + } + return (0); + } + } + return (ENXIO); /* use builtin handler */ +} + +static void +ustorage_fs_t_bbb_command_callback(struct usb2_xfer *xfer) +{ + struct ustorage_fs_softc *sc = xfer->priv_sc; + uint32_t tag; + uint8_t error = 0; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + tag = UGETDW(sc->sc_cbw.dCBWSignature); + + if (tag != CBWSIGNATURE) { + /* do nothing */ + DPRINTF("invalid signature 0x%08x\n", tag); + break; + } + tag = UGETDW(sc->sc_cbw.dCBWTag); + + /* echo back tag */ + USETDW(sc->sc_csw.dCSWTag, tag); + + /* reset status */ + sc->sc_csw.bCSWStatus = 0; + + /* reset data offset, data length and data remainder */ + sc->sc_transfer.offset = 0; + sc->sc_transfer.data_rem = + UGETDW(sc->sc_cbw.dCBWDataTransferLength); + + /* reset data flags */ + sc->sc_transfer.data_short = 0; + + /* extract LUN */ + sc->sc_transfer.lun = sc->sc_cbw.bCBWLUN; + + if (sc->sc_transfer.data_rem == 0) { + sc->sc_transfer.cbw_dir = DIR_NONE; + } else { + if (sc->sc_cbw.bCBWFlags & CBWFLAGS_IN) { + sc->sc_transfer.cbw_dir = DIR_WRITE; + } else { + sc->sc_transfer.cbw_dir = DIR_READ; + } + } + + sc->sc_transfer.cmd_len = sc->sc_cbw.bCDBLength; + if ((sc->sc_transfer.cmd_len > sizeof(sc->sc_cbw.CBWCDB)) || + (sc->sc_transfer.cmd_len == 0)) { + /* just halt - this is invalid */ + DPRINTF("invalid command length %d bytes\n", + sc->sc_transfer.cmd_len); + break; + } + bcopy(sc->sc_cbw.CBWCDB, sc->sc_transfer.cmd_data, + sc->sc_transfer.cmd_len); + + bzero(sc->sc_cbw.CBWCDB + sc->sc_transfer.cmd_len, + sizeof(sc->sc_cbw.CBWCDB) - sc->sc_transfer.cmd_len); + + error = ustorage_fs_do_cmd(sc); + if (error) { + /* got an error */ + DPRINTF("command failed\n"); + break; + } + if ((sc->sc_transfer.data_rem > 0) && + (sc->sc_transfer.cbw_dir != sc->sc_transfer.cmd_dir)) { + /* contradicting data transfer direction */ + error = 1; + DPRINTF("data direction mismatch\n"); + break; + } + switch (sc->sc_transfer.cbw_dir) { + case DIR_READ: + ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_READ); + break; + case DIR_WRITE: + ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_WRITE); + break; + default: + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_STATUS); + break; + } + break; + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_transfer.data_error) { + sc->sc_transfer.data_error = 0; + xfer->flags.stall_pipe = 1; + DPRINTF("stall pipe\n"); + } else { + xfer->flags.stall_pipe = 0; + } + + xfer->frlengths[0] = sizeof(sc->sc_cbw); + usb2_set_frame_data(xfer, &sc->sc_cbw, 0); + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTF("error\n"); + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + /* If the pipe is already stalled, don't do another stall */ + if (!xfer->pipe->is_stalled) { + sc->sc_transfer.data_error = 1; + } + /* try again */ + goto tr_setup; + } + if (error) { + if (sc->sc_csw.bCSWStatus == 0) { + /* set some default error code */ + sc->sc_csw.bCSWStatus = CSWSTATUS_FAILED; + } + if (sc->sc_transfer.cbw_dir == DIR_READ) { + /* dump all data */ + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_DATA_DUMP); + return; + } + if (sc->sc_transfer.cbw_dir == DIR_WRITE) { + /* need to stall before status */ + sc->sc_transfer.data_error = 1; + } + ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_STATUS); + } + return; +} + +static void +ustorage_fs_t_bbb_data_dump_callback(struct usb2_xfer *xfer) +{ + struct ustorage_fs_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.offset += xfer->actlen; + + if ((xfer->actlen != xfer->sumlen) || + (sc->sc_transfer.data_rem == 0)) { + /* short transfer or end of data */ + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_STATUS); + break; + } + /* Fallthrough */ + + case USB_ST_SETUP: +tr_setup: + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + if (sc->sc_transfer.data_error) { + sc->sc_transfer.data_error = 0; + xfer->flags.stall_pipe = 1; + } else { + xfer->flags.stall_pipe = 0; + } + xfer->frlengths[0] = max_bulk; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + /* + * If the pipe is already stalled, don't do another stall: + */ + if (!xfer->pipe->is_stalled) { + sc->sc_transfer.data_error = 1; + } + /* try again */ + goto tr_setup; + } + return; +} + +static void +ustorage_fs_t_bbb_data_read_callback(struct usb2_xfer *xfer) +{ + struct ustorage_fs_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.offset += xfer->actlen; + + if ((xfer->actlen != xfer->sumlen) || + (sc->sc_transfer.data_rem == 0)) { + /* short transfer or end of data */ + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_STATUS); + break; + } + /* Fallthrough */ + + case USB_ST_SETUP: +tr_setup: + if (max_bulk > sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + } + if (sc->sc_transfer.data_error) { + sc->sc_transfer.data_error = 0; + xfer->flags.stall_pipe = 1; + } else { + xfer->flags.stall_pipe = 0; + } + + xfer->frlengths[0] = max_bulk; + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + /* If the pipe is already stalled, don't do another stall */ + if (!xfer->pipe->is_stalled) { + sc->sc_transfer.data_error = 1; + } + /* try again */ + goto tr_setup; + } + return; +} + +static void +ustorage_fs_t_bbb_data_write_callback(struct usb2_xfer *xfer) +{ + struct ustorage_fs_softc *sc = xfer->priv_sc; + uint32_t max_bulk = xfer->max_data_length; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + sc->sc_transfer.data_rem -= xfer->actlen; + sc->sc_transfer.data_ptr += xfer->actlen; + sc->sc_transfer.offset += xfer->actlen; + + if ((xfer->actlen != xfer->sumlen) || + (sc->sc_transfer.data_rem == 0)) { + /* short transfer or end of data */ + ustorage_fs_transfer_start(sc, + USTORAGE_FS_T_BBB_STATUS); + break; + } + case USB_ST_SETUP: +tr_setup: + if (max_bulk >= sc->sc_transfer.data_rem) { + max_bulk = sc->sc_transfer.data_rem; + if (sc->sc_transfer.data_short) { + xfer->flags.force_short_xfer = 1; + } else { + xfer->flags.force_short_xfer = 0; + } + } else { + xfer->flags.force_short_xfer = 0; + } + + if (sc->sc_transfer.data_error) { + sc->sc_transfer.data_error = 0; + xfer->flags.stall_pipe = 1; + } else { + xfer->flags.stall_pipe = 0; + } + + xfer->frlengths[0] = max_bulk; + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + /* + * If the pipe is already stalled, don't do another + * stall + */ + if (!xfer->pipe->is_stalled) { + sc->sc_transfer.data_error = 1; + } + /* try again */ + goto tr_setup; + } + return; +} + +static void +ustorage_fs_t_bbb_status_callback(struct usb2_xfer *xfer) +{ + struct ustorage_fs_softc *sc = xfer->priv_sc; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND); + break; + + case USB_ST_SETUP: +tr_setup: + USETDW(sc->sc_csw.dCSWSignature, CSWSIGNATURE); + USETDW(sc->sc_csw.dCSWDataResidue, sc->sc_transfer.data_rem); + + if (sc->sc_transfer.data_error) { + sc->sc_transfer.data_error = 0; + xfer->flags.stall_pipe = 1; + } else { + xfer->flags.stall_pipe = 0; + } + + xfer->frlengths[0] = sizeof(sc->sc_csw); + usb2_set_frame_data(xfer, &sc->sc_csw, 0); + usb2_start_hardware(xfer); + break; + + default: + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + /* If the pipe is already stalled, don't do another stall */ + if (!xfer->pipe->is_stalled) { + sc->sc_transfer.data_error = 1; + } + /* try again */ + goto tr_setup; + } + return; +} + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_START_STOP_UNIT 0x1b +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((uint8_t) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((uint8_t) ((x) >> 8)) +#define ASCQ(x) ((uint8_t) (x)) + +/* Routines for unaligned data access */ + +static uint16_t +get_be16(uint8_t *buf) +{ + return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]); +} + +static uint32_t +get_be32(uint8_t *buf) +{ + return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | + ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]); +} + +static void +put_be16(uint8_t *buf, uint16_t val) +{ + buf[0] = val >> 8; + buf[1] = val; +} + +static void +put_be32(uint8_t *buf, uint32_t val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val & 0xff; +} + +/*------------------------------------------------------------------------* + * ustorage_fs_verify + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_verify(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint32_t lba; + uint32_t vlen; + uint64_t file_offset; + uint64_t amount_left; + + /* + * Get the starting Logical Block Address + */ + lba = get_be32(&sc->sc_transfer.cmd_data[2]); + + /* + * We allow DPO (Disable Page Out = don't save data in the cache) + * but we don't implement it. + */ + if ((sc->sc_transfer.cmd_data[1] & ~0x10) != 0) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + vlen = get_be16(&sc->sc_transfer.cmd_data[7]); + if (vlen == 0) { + goto done; + } + /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = vlen; + amount_left <<= 9; + file_offset = lba; + file_offset <<= 9; + + /* Range check */ + vlen += lba; + + if ((vlen < lba) || + (vlen > currlun->num_sectors) || + (lba >= currlun->num_sectors)) { + currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return (1); + } + /* XXX TODO: verify that data is readable */ +done: + return (ustorage_fs_min_len(sc, 0, 0 - 1)); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_inquiry + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_inquiry(struct ustorage_fs_softc *sc) +{ + uint8_t *buf = sc->sc_transfer.data_ptr; + static const char vendor_id[] = "FreeBSD "; + static const char product_id[] = "File-Stor Gadget"; + + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + + if (!sc->sc_transfer.currlun) { + /* Unsupported LUNs are okay */ + memset(buf, 0, 36); + buf[0] = 0x7f; + /* Unsupported, no device - type */ + return (ustorage_fs_min_len(sc, 36, 0 - 1)); + } + memset(buf, 0, 8); + /* Non - removable, direct - access device */ + if (currlun->removable) + buf[1] = 0x80; + buf[2] = 2; + /* ANSI SCSI level 2 */ + buf[3] = 2; + /* SCSI - 2 INQUIRY data format */ + buf[4] = 31; + /* Additional length */ + /* No special options */ + /* + * NOTE: We are writing an extra zero here, that is not + * transferred to the peer: + */ + snprintf(buf + 8, 28 + 1, "%-8s%-16s%04x", vendor_id, product_id, + USTORAGE_FS_RELEASE); + return (ustorage_fs_min_len(sc, 36, 0 - 1)); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_request_sense + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_request_sense(struct ustorage_fs_softc *sc) +{ + uint8_t *buf = sc->sc_transfer.data_ptr; + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint32_t sd; + uint32_t sdinfo; + uint8_t valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (currlun && currlun->unit_attention_data != SS_NO_SENSE) { + currlun->sense_data = currlun->unit_attention_data; + currlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!currlun) { + /* Unsupported LUNs are okay */ + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = currlun->sense_data; + sdinfo = currlun->sense_data_info; + valid = currlun->info_valid << 7; + currlun->sense_data = SS_NO_SENSE; + currlun->sense_data_info = 0; + currlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; + /* Valid, current error */ + buf[2] = SK(sd); + put_be32(&buf[3], sdinfo); + /* Sense information */ + buf[7] = 18 - 8; + /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return (ustorage_fs_min_len(sc, 18, 0 - 1)); +} + + +/*------------------------------------------------------------------------* + * ustorage_fs_read_capacity + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_read_capacity(struct ustorage_fs_softc *sc) +{ + uint8_t *buf = sc->sc_transfer.data_ptr; + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint32_t lba = get_be32(&sc->sc_transfer.cmd_data[2]); + uint8_t pmi = sc->sc_transfer.cmd_data[8]; + + /* Check the PMI and LBA fields */ + if ((pmi > 1) || ((pmi == 0) && (lba != 0))) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + put_be32(&buf[0], currlun->num_sectors - 1); + /* Max logical block */ + put_be32(&buf[4], 512); + /* Block length */ + return (ustorage_fs_min_len(sc, 8, 0 - 1)); +} + + +/*------------------------------------------------------------------------* + * ustorage_fs_mode_sense + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_mode_sense(struct ustorage_fs_softc *sc) +{ + uint8_t *buf = sc->sc_transfer.data_ptr; + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint8_t *buf0; + uint16_t len; + uint16_t limit; + uint8_t mscmnd = sc->sc_transfer.cmd_data[0]; + uint8_t pc; + uint8_t page_code; + uint8_t changeable_values; + uint8_t all_pages; + + buf0 = buf; + + if ((sc->sc_transfer.cmd_data[1] & ~0x08) != 0) { + /* Mask away DBD */ + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + pc = sc->sc_transfer.cmd_data[2] >> 6; + page_code = sc->sc_transfer.cmd_data[2] & 0x3f; + if (pc == 3) { + currlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return (1); + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* + * Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. + */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { + buf[2] = (currlun->read_only ? 0x80 : 0x00); + /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { + /* SC_MODE_SENSE_10 */ + buf[3] = (currlun->read_only ? 0x80 : 0x00); + /* WP, DPOFUA */ + buf += 8; + limit = 65535; + /* Should really be mod_data.buflen */ + } + + /* No block descriptors */ + + /* + * The mode pages, in numerical order. + */ + if ((page_code == 0x08) || all_pages) { + buf[0] = 0x08; + /* Page code */ + buf[1] = 10; + /* Page length */ + memset(buf + 2, 0, 10); + /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; + /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_be16(&buf[4], 0xffff); + /* Don 't disable prefetch */ + /* Minimum prefetch = 0 */ + put_be16(&buf[8], 0xffff); + /* Maximum prefetch */ + put_be16(&buf[10], 0xffff); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + /* + * Check that a valid page was requested and the mode data length + * isn't too long. + */ + len = buf - buf0; + if (len > limit) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + /* Store the mode data length */ + if (mscmnd == SC_MODE_SENSE_6) + buf0[0] = len - 1; + else + put_be16(buf0, len - 2); + return (ustorage_fs_min_len(sc, len, 0 - 1)); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_start_stop + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_start_stop(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint8_t loej; + uint8_t start; + uint8_t immed; + + if (!currlun->removable) { + currlun->sense_data = SS_INVALID_COMMAND; + return (1); + } + immed = sc->sc_transfer.cmd_data[1] & 0x01; + loej = sc->sc_transfer.cmd_data[4] & 0x02; + start = sc->sc_transfer.cmd_data[4] & 0x01; + + if (immed || loej || start) { + /* compile fix */ + } + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_prevent_allow + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint8_t prevent; + + if (!currlun->removable) { + currlun->sense_data = SS_INVALID_COMMAND; + return (1); + } + prevent = sc->sc_transfer.cmd_data[4] & 0x01; + if ((sc->sc_transfer.cmd_data[4] & ~0x01) != 0) { + /* Mask away Prevent */ + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + if (currlun->prevent_medium_removal && !prevent) { + //fsync_sub(currlun); + } + currlun->prevent_medium_removal = prevent; + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_read_format_capacities + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc) +{ + uint8_t *buf = sc->sc_transfer.data_ptr; + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; + /* Only the Current / Maximum Capacity Descriptor */ + buf += 4; + + put_be32(&buf[0], currlun->num_sectors); + /* Number of blocks */ + put_be32(&buf[4], 512); + /* Block length */ + buf[4] = 0x02; + /* Current capacity */ + return (ustorage_fs_min_len(sc, 12, 0 - 1)); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_mode_select + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_mode_select(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + + /* We don't support MODE SELECT */ + currlun->sense_data = SS_INVALID_COMMAND; + return (1); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_synchronize_cache + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint8_t rc; + + /* + * We ignore the requested LBA and write out all dirty data buffers. + */ + rc = 0; + if (rc) { + currlun->sense_data = SS_WRITE_ERROR; + } + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_read - read data from disk + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_read(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint64_t file_offset; + uint32_t lba; + uint32_t len; + + /* + * Get the starting Logical Block Address and check that it's not + * too big + */ + if (sc->sc_transfer.cmd_data[0] == SC_READ_6) { + lba = (sc->sc_transfer.cmd_data[1] << 16) | + get_be16(&sc->sc_transfer.cmd_data[2]); + } else { + lba = get_be32(&sc->sc_transfer.cmd_data[2]); + + /* + * We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. + */ + if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + } + len = sc->sc_transfer.data_rem >> 9; + len += lba; + + if ((len < lba) || + (len > currlun->num_sectors) || + (lba >= currlun->num_sectors)) { + currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return (1); + } + file_offset = lba; + file_offset <<= 9; + + sc->sc_transfer.data_ptr = + USB_ADD_BYTES(currlun->memory_image, (uint32_t)file_offset); + + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_write - write data to disk + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_write(struct ustorage_fs_softc *sc) +{ + struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; + uint64_t file_offset; + uint32_t lba; + uint32_t len; + + if (currlun->read_only) { + currlun->sense_data = SS_WRITE_PROTECTED; + return (1); + } + /* XXX clear SYNC */ + + /* + * Get the starting Logical Block Address and check that it's not + * too big. + */ + if (sc->sc_transfer.cmd_data[0] == SC_WRITE_6) + lba = (sc->sc_transfer.cmd_data[1] << 16) | + get_be16(&sc->sc_transfer.cmd_data[2]); + else { + lba = get_be32(&sc->sc_transfer.cmd_data[2]); + + /* + * We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. + */ + if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return (1); + } + if (sc->sc_transfer.cmd_data[1] & 0x08) { + /* FUA */ + /* XXX set SYNC flag here */ + } + } + + len = sc->sc_transfer.data_rem >> 9; + len += lba; + + if ((len < lba) || + (len > currlun->num_sectors) || + (lba >= currlun->num_sectors)) { + currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return (1); + } + file_offset = lba; + file_offset <<= 9; + + sc->sc_transfer.data_ptr = + USB_ADD_BYTES(currlun->memory_image, (uint32_t)file_offset); + + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_min_len + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask) +{ + if (len != sc->sc_transfer.data_rem) { + + if (sc->sc_transfer.cbw_dir == DIR_READ) { + /* + * there must be something wrong about this SCSI + * command + */ + sc->sc_csw.bCSWStatus = CSWSTATUS_PHASE; + return (1); + } + /* compute the minimum length */ + + if (sc->sc_transfer.data_rem > len) { + /* data ends prematurely */ + sc->sc_transfer.data_rem = len; + sc->sc_transfer.data_short = 1; + } + /* check length alignment */ + + if (sc->sc_transfer.data_rem & ~mask) { + /* data ends prematurely */ + sc->sc_transfer.data_rem &= mask; + sc->sc_transfer.data_short = 1; + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_check_cmd - check command routine + * + * Check whether the command is properly formed and whether its data + * size and direction agree with the values we already have. + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t min_cmd_size, + uint16_t mask, uint8_t needs_medium) +{ + struct ustorage_fs_lun *currlun; + uint8_t lun = (sc->sc_transfer.cmd_data[1] >> 5); + uint8_t i; + + /* Verify the length of the command itself */ + if (min_cmd_size > sc->sc_transfer.cmd_len) { + DPRINTF("%u > %u\n", + min_cmd_size, sc->sc_transfer.cmd_len); + sc->sc_csw.bCSWStatus = CSWSTATUS_PHASE; + return (1); + } + /* Mask away the LUN */ + sc->sc_transfer.cmd_data[1] &= 0x1f; + + /* Check if LUN is correct */ + if (lun != sc->sc_transfer.lun) { + + } + /* Check the LUN */ + if (sc->sc_transfer.lun <= sc->sc_last_lun) { + sc->sc_transfer.currlun = currlun = + sc->sc_lun + sc->sc_transfer.lun; + if (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE) { + currlun->sense_data = SS_NO_SENSE; + currlun->sense_data_info = 0; + currlun->info_valid = 0; + } + /* + * If a unit attention condition exists, only INQUIRY + * and REQUEST SENSE commands are allowed. Anything + * else must fail! + */ + if ((currlun->unit_attention_data != SS_NO_SENSE) && + (sc->sc_transfer.cmd_data[0] != SC_INQUIRY) && + (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) { + currlun->sense_data = currlun->unit_attention_data; + currlun->unit_attention_data = SS_NO_SENSE; + return (1); + } + } else { + sc->sc_transfer.currlun = currlun = NULL; + + /* + * INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. + */ + if ((sc->sc_transfer.cmd_data[0] != SC_INQUIRY) && + (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) { + return (1); + } + } + + /* + * Check that only command bytes listed in the mask are + * non-zero. + */ + for (i = 0; i != min_cmd_size; i++) { + if (sc->sc_transfer.cmd_data[i] && !(mask & (1 << i))) { + if (currlun) { + currlun->sense_data = SS_INVALID_FIELD_IN_CDB; + } + return (1); + } + } + + /* + * If the medium isn't mounted and the command needs to access + * it, return an error. + */ + if (currlun && (!currlun->memory_image) && needs_medium) { + currlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return (1); + } + return (0); +} + +/*------------------------------------------------------------------------* + * ustorage_fs_do_cmd - do command + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +ustorage_fs_do_cmd(struct ustorage_fs_softc *sc) +{ + uint8_t error = 1; + uint8_t i; + + /* set default data transfer pointer */ + sc->sc_transfer.data_ptr = sc->sc_qdata; + + DPRINTF("cmd_data[0]=0x%02x, data_rem=0x%08x\n", + sc->sc_transfer.cmd_data[0], sc->sc_transfer.data_rem); + + switch (sc->sc_transfer.cmd_data[0]) { + case SC_INQUIRY: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_inquiry(sc); + + break; + + case SC_MODE_SELECT_6: + sc->sc_transfer.cmd_dir = DIR_READ; + error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 1) | (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_mode_select(sc); + + break; + + case SC_MODE_SELECT_10: + sc->sc_transfer.cmd_dir = DIR_READ; + error = ustorage_fs_min_len(sc, + get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (1 << 1) | (3 << 7) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_mode_select(sc); + + break; + + case SC_MODE_SENSE_6: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 1) | (1 << 2) | (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_mode_sense(sc); + + break; + + case SC_MODE_SENSE_10: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, + get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (1 << 1) | (1 << 2) | (3 << 7) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_mode_sense(sc); + + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_prevent_allow(sc); + + break; + + case SC_READ_6: + i = sc->sc_transfer.cmd_data[4]; + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, + ((i == 0) ? 256 : i) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (7 << 1) | (1 << 4) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_read(sc); + + break; + + case SC_READ_10: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, + get_be16(&sc->sc_transfer.cmd_data[7]) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_read(sc); + + break; + + case SC_READ_12: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, + get_be32(&sc->sc_transfer.cmd_data[6]) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 12, + (1 << 1) | (0xf << 2) | (0xf << 6) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_read(sc); + + break; + + case SC_READ_CAPACITY: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_check_cmd(sc, 10, + (0xf << 2) | (1 << 8) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_read_capacity(sc); + + break; + + case SC_READ_FORMAT_CAPACITIES: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, + get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (3 << 7) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_read_format_capacities(sc); + + break; + + case SC_REQUEST_SENSE: + sc->sc_transfer.cmd_dir = DIR_WRITE; + error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_request_sense(sc); + + break; + + case SC_START_STOP_UNIT: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (1 << 1) | (1 << 4) | 1, 0); + if (error) { + break; + } + error = ustorage_fs_start_stop(sc); + + break; + + case SC_SYNCHRONIZE_CACHE: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (0xf << 2) | (3 << 7) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_synchronize_cache(sc); + + break; + + case SC_TEST_UNIT_READY: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + 0 | 1, 1); + break; + + /* + * Although optional, this command is used by MS-Windows. + * We support a minimal version: BytChk must be 0. + */ + case SC_VERIFY: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_verify(sc); + + break; + + case SC_WRITE_6: + i = sc->sc_transfer.cmd_data[4]; + sc->sc_transfer.cmd_dir = DIR_READ; + error = ustorage_fs_min_len(sc, + ((i == 0) ? 256 : i) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 6, + (7 << 1) | (1 << 4) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_write(sc); + + break; + + case SC_WRITE_10: + sc->sc_transfer.cmd_dir = DIR_READ; + error = ustorage_fs_min_len(sc, + get_be16(&sc->sc_transfer.cmd_data[7]) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 10, + (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_write(sc); + + break; + + case SC_WRITE_12: + sc->sc_transfer.cmd_dir = DIR_READ; + error = ustorage_fs_min_len(sc, + get_be32(&sc->sc_transfer.cmd_data[6]) << 9, 0 - (1 << 9)); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, 12, + (1 << 1) | (0xf << 2) | (0xf << 6) | 1, 1); + if (error) { + break; + } + error = ustorage_fs_write(sc); + + break; + + /* + * Some mandatory commands that we recognize but don't + * implement. They don't mean much in this setting. + * It's left as an exercise for anyone interested to + * implement RESERVE and RELEASE in terms of Posix + * locks. + */ + case SC_FORMAT_UNIT: + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: + /* Fallthrough */ + + default: + error = ustorage_fs_min_len(sc, 0, 0 - 1); + if (error) { + break; + } + error = ustorage_fs_check_cmd(sc, sc->sc_transfer.cmd_len, + 0xff, 0); + if (error) { + break; + } + sc->sc_transfer.currlun->sense_data = + SS_INVALID_COMMAND; + error = 1; + + break; + } + return (error); +} diff --git a/sys/dev/usb2/template/usb2_template.c b/sys/dev/usb2/template/usb2_template.c new file mode 100644 index 000000000000..20c866277db9 --- /dev/null +++ b/sys/dev/usb2/template/usb2_template.c @@ -0,0 +1,1306 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007 Hans Petter Selasky. 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 file contains sub-routines to build up USB descriptors from + * USB templates. + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +MODULE_DEPEND(usb2_template, usb2_core, 1, 1, 1); +MODULE_VERSION(usb2_template, 1); + +/* function prototypes */ + +static void usb2_make_raw_desc(struct usb2_temp_setup *temp, const uint8_t *raw); +static void usb2_make_endpoint_desc(struct usb2_temp_setup *temp, const struct usb2_temp_endpoint_desc *ted); +static void usb2_make_interface_desc(struct usb2_temp_setup *temp, const struct usb2_temp_interface_desc *tid); +static void usb2_make_config_desc(struct usb2_temp_setup *temp, const struct usb2_temp_config_desc *tcd); +static void usb2_make_device_desc(struct usb2_temp_setup *temp, const struct usb2_temp_device_desc *tdd); +static uint8_t usb2_hw_ep_match(const struct usb2_hw_ep_profile *pf, uint8_t ep_type, uint8_t ep_dir_in); +static uint8_t usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *ues, struct usb2_hw_ep_scratch_sub *ep, uint8_t is_simplex); +static uint8_t usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *ues, uint8_t ep_type, uint8_t is_complete); +static usb2_error_t usb2_hw_ep_resolve(struct usb2_device *udev, struct usb2_descriptor *desc); +static const struct usb2_temp_device_desc *usb2_temp_get_tdd(struct usb2_device *udev); +static void *usb2_temp_get_device_desc(struct usb2_device *udev); +static void *usb2_temp_get_qualifier_desc(struct usb2_device *udev); +static void *usb2_temp_get_config_desc(struct usb2_device *udev, uint16_t *pLength, uint8_t index); +static const void *usb2_temp_get_string_desc(struct usb2_device *udev, uint16_t lang_id, uint8_t string_index); +static const void *usb2_temp_get_vendor_desc(struct usb2_device *udev, const struct usb2_device_request *req); +static const void *usb2_temp_get_hub_desc(struct usb2_device *udev); +static void usb2_temp_get_desc(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength); +static usb2_error_t usb2_temp_setup(struct usb2_device *udev, const struct usb2_temp_device_desc *tdd); +static void usb2_temp_unsetup(struct usb2_device *udev); +static usb2_error_t usb2_temp_setup_by_index(struct usb2_device *udev, uint16_t index); +static void usb2_temp_init(void *arg); + +/*------------------------------------------------------------------------* + * usb2_make_raw_desc + * + * This function will insert a raw USB descriptor into the generated + * USB configuration. + *------------------------------------------------------------------------*/ +static void +usb2_make_raw_desc(struct usb2_temp_setup *temp, + const uint8_t *raw) +{ + void *dst; + uint8_t len; + + /* + * The first byte of any USB descriptor gives the length. + */ + if (raw) { + len = raw[0]; + if (temp->buf) { + dst = USB_ADD_BYTES(temp->buf, temp->size); + bcopy(raw, dst, len); + + /* check if we have got a CDC union descriptor */ + + if ((raw[0] >= sizeof(struct usb2_cdc_union_descriptor)) && + (raw[1] == UDESC_CS_INTERFACE) && + (raw[2] == UDESCSUB_CDC_UNION)) { + struct usb2_cdc_union_descriptor *ud = (void *)dst; + + /* update the interface numbers */ + + ud->bMasterInterface += + temp->bInterfaceNumber; + ud->bSlaveInterface[0] += + temp->bInterfaceNumber; + } + } + temp->size += len; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_make_endpoint_desc + * + * This function will generate an USB endpoint descriptor from the + * given USB template endpoint descriptor, which will be inserted into + * the USB configuration. + *------------------------------------------------------------------------*/ +static void +usb2_make_endpoint_desc(struct usb2_temp_setup *temp, + const struct usb2_temp_endpoint_desc *ted) +{ + struct usb2_endpoint_descriptor *ed; + const void **rd; + uint16_t old_size; + uint16_t mps; + uint8_t ea = 0; /* Endpoint Address */ + uint8_t et = 0; /* Endpiont Type */ + + /* Reserve memory */ + old_size = temp->size; + temp->size += sizeof(*ed); + + /* Scan all Raw Descriptors first */ + + rd = ted->ppRawDesc; + if (rd) { + while (*rd) { + usb2_make_raw_desc(temp, *rd); + rd++; + } + } + if (ted->pPacketSize == NULL) { + /* not initialized */ + temp->err = USB_ERR_INVAL; + return; + } + mps = ted->pPacketSize->mps[temp->usb2_speed]; + if (mps == 0) { + /* not initialized */ + temp->err = USB_ERR_INVAL; + return; + } else if (mps == UE_ZERO_MPS) { + /* escape for Zero Max Packet Size */ + mps = 0; + } + ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); + et = (ted->bmAttributes & UE_XFERTYPE); + + /* + * Fill out the real USB endpoint descriptor + * in case there is a buffer present: + */ + if (temp->buf) { + ed = USB_ADD_BYTES(temp->buf, old_size); + ed->bLength = sizeof(*ed); + ed->bDescriptorType = UDESC_ENDPOINT; + ed->bEndpointAddress = ea; + ed->bmAttributes = ted->bmAttributes; + USETW(ed->wMaxPacketSize, mps); + + /* setup bInterval parameter */ + + if (ted->pIntervals && + ted->pIntervals->bInterval[temp->usb2_speed]) { + ed->bInterval = + ted->pIntervals->bInterval[temp->usb2_speed]; + } else { + switch (et) { + case UE_BULK: + case UE_CONTROL: + ed->bInterval = 0; /* not used */ + break; + case UE_INTERRUPT: + switch (temp->usb2_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + ed->bInterval = 1; /* 1 ms */ + break; + default: + ed->bInterval = 8; /* 8*125 us */ + break; + } + break; + default: /* UE_ISOCHRONOUS */ + switch (temp->usb2_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + ed->bInterval = 1; /* 1 ms */ + break; + default: + ed->bInterval = 1; /* 125 us */ + break; + } + break; + } + } + } + temp->bNumEndpoints++; + return; +} + +/*------------------------------------------------------------------------* + * usb2_make_interface_desc + * + * This function will generate an USB interface descriptor from the + * given USB template interface descriptor, which will be inserted + * into the USB configuration. + *------------------------------------------------------------------------*/ +static void +usb2_make_interface_desc(struct usb2_temp_setup *temp, + const struct usb2_temp_interface_desc *tid) +{ + struct usb2_interface_descriptor *id; + const struct usb2_temp_endpoint_desc **ted; + const void **rd; + uint16_t old_size; + + /* Reserve memory */ + + old_size = temp->size; + temp->size += sizeof(*id); + + /* Update interface and alternate interface numbers */ + + if (tid->isAltInterface == 0) { + temp->bAlternateSetting = 0; + temp->bInterfaceNumber++; + } else { + temp->bAlternateSetting++; + } + + /* Scan all Raw Descriptors first */ + + rd = tid->ppRawDesc; + + if (rd) { + while (*rd) { + usb2_make_raw_desc(temp, *rd); + rd++; + } + } + /* Reset some counters */ + + temp->bNumEndpoints = 0; + + /* Scan all Endpoint Descriptors second */ + + ted = tid->ppEndpoints; + if (ted) { + while (*ted) { + usb2_make_endpoint_desc(temp, *ted); + ted++; + } + } + /* + * Fill out the real USB interface descriptor + * in case there is a buffer present: + */ + if (temp->buf) { + id = USB_ADD_BYTES(temp->buf, old_size); + id->bLength = sizeof(*id); + id->bDescriptorType = UDESC_INTERFACE; + id->bInterfaceNumber = temp->bInterfaceNumber; + id->bAlternateSetting = temp->bAlternateSetting; + id->bNumEndpoints = temp->bNumEndpoints; + id->bInterfaceClass = tid->bInterfaceClass; + id->bInterfaceSubClass = tid->bInterfaceSubClass; + id->bInterfaceProtocol = tid->bInterfaceProtocol; + id->iInterface = tid->iInterface; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_make_config_desc + * + * This function will generate an USB config descriptor from the given + * USB template config descriptor, which will be inserted into the USB + * configuration. + *------------------------------------------------------------------------*/ +static void +usb2_make_config_desc(struct usb2_temp_setup *temp, + const struct usb2_temp_config_desc *tcd) +{ + struct usb2_config_descriptor *cd; + const struct usb2_temp_interface_desc **tid; + uint16_t old_size; + + /* Reserve memory */ + + old_size = temp->size; + temp->size += sizeof(*cd); + + /* Reset some counters */ + + temp->bInterfaceNumber = 0 - 1; + temp->bAlternateSetting = 0; + + /* Scan all the USB interfaces */ + + tid = tcd->ppIfaceDesc; + if (tid) { + while (*tid) { + usb2_make_interface_desc(temp, *tid); + tid++; + } + } + /* + * Fill out the real USB config descriptor + * in case there is a buffer present: + */ + if (temp->buf) { + cd = USB_ADD_BYTES(temp->buf, old_size); + + /* compute total size */ + old_size = temp->size - old_size; + + cd->bLength = sizeof(*cd); + cd->bDescriptorType = UDESC_CONFIG; + USETW(cd->wTotalLength, old_size); + cd->bNumInterface = temp->bInterfaceNumber + 1; + cd->bConfigurationValue = temp->bConfigurationValue; + cd->iConfiguration = tcd->iConfiguration; + cd->bmAttributes = tcd->bmAttributes; + cd->bMaxPower = tcd->bMaxPower; + cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); + + if (temp->self_powered) { + cd->bmAttributes |= UC_SELF_POWERED; + } else { + cd->bmAttributes &= ~UC_SELF_POWERED; + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_make_device_desc + * + * This function will generate an USB device descriptor from the + * given USB template device descriptor. + *------------------------------------------------------------------------*/ +static void +usb2_make_device_desc(struct usb2_temp_setup *temp, + const struct usb2_temp_device_desc *tdd) +{ + struct usb2_temp_data *utd; + const struct usb2_temp_config_desc **tcd; + uint16_t old_size; + + /* Reserve memory */ + + old_size = temp->size; + temp->size += sizeof(*utd); + + /* Scan all the USB configs */ + + temp->bConfigurationValue = 1; + tcd = tdd->ppConfigDesc; + if (tcd) { + while (*tcd) { + usb2_make_config_desc(temp, *tcd); + temp->bConfigurationValue++; + tcd++; + } + } + /* + * Fill out the real USB device descriptor + * in case there is a buffer present: + */ + + if (temp->buf) { + utd = USB_ADD_BYTES(temp->buf, old_size); + + /* Store a pointer to our template device descriptor */ + utd->tdd = tdd; + + /* Fill out USB device descriptor */ + utd->udd.bLength = sizeof(utd->udd); + utd->udd.bDescriptorType = UDESC_DEVICE; + utd->udd.bDeviceClass = tdd->bDeviceClass; + utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; + utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; + USETW(utd->udd.idVendor, tdd->idVendor); + USETW(utd->udd.idProduct, tdd->idProduct); + USETW(utd->udd.bcdDevice, tdd->bcdDevice); + utd->udd.iManufacturer = tdd->iManufacturer; + utd->udd.iProduct = tdd->iProduct; + utd->udd.iSerialNumber = tdd->iSerialNumber; + utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; + + /* + * Fill out the USB device qualifier. Pretend that we + * don't support any other speeds by setting + * "bNumConfigurations" equal to zero. That saves us + * generating an extra set of configuration + * descriptors. + */ + utd->udq.bLength = sizeof(utd->udq); + utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; + utd->udq.bDeviceClass = tdd->bDeviceClass; + utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; + utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; + utd->udq.bNumConfigurations = 0; + USETW(utd->udq.bcdUSB, 0x0200); + utd->udq.bMaxPacketSize0 = 0; + + switch (temp->usb2_speed) { + case USB_SPEED_LOW: + USETW(utd->udd.bcdUSB, 0x0110); + utd->udd.bMaxPacketSize = 8; + break; + case USB_SPEED_FULL: + USETW(utd->udd.bcdUSB, 0x0110); + utd->udd.bMaxPacketSize = 32; + break; + case USB_SPEED_HIGH: + USETW(utd->udd.bcdUSB, 0x0200); + utd->udd.bMaxPacketSize = 64; + break; + case USB_SPEED_VARIABLE: + USETW(utd->udd.bcdUSB, 0x0250); + utd->udd.bMaxPacketSize = 255; /* 512 bytes */ + break; + default: + temp->err = USB_ERR_INVAL; + break; + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_hw_ep_match + * + * Return values: + * 0: The endpoint profile does not match the criterias + * Else: The endpoint profile matches the criterias + *------------------------------------------------------------------------*/ +static uint8_t +usb2_hw_ep_match(const struct usb2_hw_ep_profile *pf, + uint8_t ep_type, uint8_t ep_dir_in) +{ + if (ep_type == UE_CONTROL) { + /* special */ + return (pf->support_control); + } + if ((pf->support_in && ep_dir_in) || + (pf->support_out && !ep_dir_in)) { + if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || + (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || + (pf->support_bulk && (ep_type == UE_BULK))) { + return (1); + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb2_hw_ep_find_match + * + * This function is used to find the best matching endpoint profile + * for and endpoint belonging to an USB descriptor. + * + * Return values: + * 0: Success. Got a match. + * Else: Failure. No match. + *------------------------------------------------------------------------*/ +static uint8_t +usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *ues, + struct usb2_hw_ep_scratch_sub *ep, uint8_t is_simplex) +{ + const struct usb2_hw_ep_profile *pf; + uint16_t distance; + uint16_t temp; + uint16_t max_frame_size; + uint8_t n; + uint8_t best_n; + uint8_t dir_in; + uint8_t dir_out; + + distance = 0xFFFF; + best_n = 0; + + if ((!ep->needs_in) && (!ep->needs_out)) { + return (0); /* we are done */ + } + if (ep->needs_ep_type == UE_CONTROL) { + dir_in = 1; + dir_out = 1; + } else { + if (ep->needs_in) { + dir_in = 1; + dir_out = 0; + } else { + dir_in = 0; + dir_out = 1; + } + } + + for (n = 1; n != (USB_EP_MAX / 2); n++) { + + /* get HW endpoint profile */ + (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); + if (pf == NULL) { + /* end of profiles */ + break; + } + /* check if IN-endpoint is reserved */ + if (dir_in || pf->is_simplex) { + if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { + /* mismatch */ + continue; + } + } + /* check if OUT-endpoint is reserved */ + if (dir_out || pf->is_simplex) { + if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { + /* mismatch */ + continue; + } + } + /* check simplex */ + if (pf->is_simplex == is_simplex) { + /* mismatch */ + continue; + } + /* check if HW endpoint matches */ + if (!usb2_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { + /* mismatch */ + continue; + } + /* get maximum frame size */ + if (dir_in) + max_frame_size = pf->max_in_frame_size; + else + max_frame_size = pf->max_out_frame_size; + + /* check if we have a matching profile */ + if (max_frame_size >= ep->max_frame_size) { + temp = (max_frame_size - ep->max_frame_size); + if (distance > temp) { + distance = temp; + best_n = n; + ep->pf = pf; + } + } + } + + /* see if we got a match */ + if (best_n != 0) { + /* get the correct profile */ + pf = ep->pf; + + /* reserve IN-endpoint */ + if (dir_in) { + ues->bmInAlloc[best_n / 8] |= + (1 << (best_n % 8)); + ep->hw_endpoint_in = best_n | UE_DIR_IN; + ep->needs_in = 0; + } + /* reserve OUT-endpoint */ + if (dir_out) { + ues->bmOutAlloc[best_n / 8] |= + (1 << (best_n % 8)); + ep->hw_endpoint_out = best_n | UE_DIR_OUT; + ep->needs_out = 0; + } + return (0); /* got a match */ + } + return (1); /* failure */ +} + +/*------------------------------------------------------------------------* + * usb2_hw_ep_get_needs + * + * This function will figure out the type and number of endpoints + * which are needed for an USB configuration. + * + * Return values: + * 0: Success. + * Else: Failure. + *------------------------------------------------------------------------*/ +static uint8_t +usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *ues, + uint8_t ep_type, uint8_t is_complete) +{ + const struct usb2_hw_ep_profile *pf; + struct usb2_hw_ep_scratch_sub *ep_iface; + struct usb2_hw_ep_scratch_sub *ep_curr; + struct usb2_hw_ep_scratch_sub *ep_max; + struct usb2_hw_ep_scratch_sub *ep_end; + struct usb2_descriptor *desc; + struct usb2_interface_descriptor *id; + struct usb2_endpoint_descriptor *ed; + uint16_t wMaxPacketSize; + uint16_t temp; + uint8_t speed; + uint8_t ep_no; + + ep_iface = ues->ep_max; + ep_curr = ues->ep_max; + ep_end = ues->ep + USB_EP_MAX; + ep_max = ues->ep_max; + desc = NULL; + speed = usb2_get_speed(ues->udev); + +repeat: + + while ((desc = usb2_desc_foreach(ues->cd, desc))) { + + if ((desc->bDescriptorType == UDESC_INTERFACE) && + (desc->bLength >= sizeof(*id))) { + + id = (void *)desc; + + if (id->bAlternateSetting == 0) { + /* going forward */ + ep_iface = ep_max; + } else { + /* reset */ + ep_curr = ep_iface; + } + } + if ((desc->bDescriptorType == UDESC_ENDPOINT) && + (desc->bLength >= sizeof(*ed))) { + + ed = (void *)desc; + + goto handle_endpoint_desc; + } + } + ues->ep_max = ep_max; + return (0); + +handle_endpoint_desc: + temp = (ed->bmAttributes & UE_XFERTYPE); + + if (temp == ep_type) { + + if (ep_curr == ep_end) { + /* too many endpoints */ + return (1); /* failure */ + } + wMaxPacketSize = UGETW(ed->wMaxPacketSize); + if ((wMaxPacketSize & 0xF800) && + (speed == USB_SPEED_HIGH)) { + /* handle packet multiplier */ + temp = (wMaxPacketSize >> 11) & 3; + wMaxPacketSize &= 0x7FF; + if (temp == 1) { + wMaxPacketSize *= 2; + } else { + wMaxPacketSize *= 3; + } + } + /* + * Check if we have a fixed endpoint number, else the + * endpoint number is allocated dynamically: + */ + ep_no = (ed->bEndpointAddress & UE_ADDR); + if (ep_no != 0) { + + /* get HW endpoint profile */ + (ues->methods->get_hw_ep_profile) + (ues->udev, &pf, ep_no); + if (pf == NULL) { + /* HW profile does not exist - failure */ + DPRINTFN(0, "Endpoint profile %u " + "does not exist\n", ep_no); + return (1); + } + /* reserve fixed endpoint number */ + if (ep_type == UE_CONTROL) { + ues->bmInAlloc[ep_no / 8] |= + (1 << (ep_no % 8)); + ues->bmOutAlloc[ep_no / 8] |= + (1 << (ep_no % 8)); + if ((pf->max_in_frame_size < wMaxPacketSize) || + (pf->max_out_frame_size < wMaxPacketSize)) { + DPRINTFN(0, "Endpoint profile %u " + "has too small buffer!\n", ep_no); + return (1); + } + } else if (ed->bEndpointAddress & UE_DIR_IN) { + ues->bmInAlloc[ep_no / 8] |= + (1 << (ep_no % 8)); + if (pf->max_in_frame_size < wMaxPacketSize) { + DPRINTFN(0, "Endpoint profile %u " + "has too small buffer!\n", ep_no); + return (1); + } + } else { + ues->bmOutAlloc[ep_no / 8] |= + (1 << (ep_no % 8)); + if (pf->max_out_frame_size < wMaxPacketSize) { + DPRINTFN(0, "Endpoint profile %u " + "has too small buffer!\n", ep_no); + return (1); + } + } + } else if (is_complete) { + + /* check if we have enough buffer space */ + if (wMaxPacketSize > + ep_curr->max_frame_size) { + return (1); /* failure */ + } + if (ed->bEndpointAddress & UE_DIR_IN) { + ed->bEndpointAddress = + ep_curr->hw_endpoint_in; + } else { + ed->bEndpointAddress = + ep_curr->hw_endpoint_out; + } + + } else { + + /* compute the maximum frame size */ + if (ep_curr->max_frame_size < wMaxPacketSize) { + ep_curr->max_frame_size = wMaxPacketSize; + } + if (temp == UE_CONTROL) { + ep_curr->needs_in = 1; + ep_curr->needs_out = 1; + } else { + if (ed->bEndpointAddress & UE_DIR_IN) { + ep_curr->needs_in = 1; + } else { + ep_curr->needs_out = 1; + } + } + ep_curr->needs_ep_type = ep_type; + } + + ep_curr++; + if (ep_max < ep_curr) { + ep_max = ep_curr; + } + } + goto repeat; +} + +/*------------------------------------------------------------------------* + * usb2_hw_ep_resolve + * + * This function will try to resolve endpoint requirements by the + * given endpoint profiles that the USB hardware reports. + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_hw_ep_resolve(struct usb2_device *udev, + struct usb2_descriptor *desc) +{ + struct usb2_hw_ep_scratch *ues; + struct usb2_hw_ep_scratch_sub *ep; + const struct usb2_hw_ep_profile *pf; + struct usb2_bus_methods *methods; + struct usb2_device_descriptor *dd; + uint16_t mps; + + if (desc == NULL) { + return (USB_ERR_INVAL); + } + /* get bus methods */ + methods = udev->bus->methods; + + if (methods->get_hw_ep_profile == NULL) { + return (USB_ERR_INVAL); + } + if (desc->bDescriptorType == UDESC_DEVICE) { + + if (desc->bLength < sizeof(*dd)) { + return (USB_ERR_INVAL); + } + dd = (void *)desc; + + /* get HW control endpoint 0 profile */ + (methods->get_hw_ep_profile) (udev, &pf, 0); + if (pf == NULL) { + return (USB_ERR_INVAL); + } + if (!usb2_hw_ep_match(pf, UE_CONTROL, 0)) { + DPRINTFN(0, "Endpoint 0 does not " + "support control\n"); + return (USB_ERR_INVAL); + } + mps = dd->bMaxPacketSize; + + if (udev->speed == USB_SPEED_FULL) { + /* + * We can optionally choose another packet size ! + */ + while (1) { + /* check if "mps" is ok */ + if (pf->max_in_frame_size >= mps) { + break; + } + /* reduce maximum packet size */ + mps /= 2; + + /* check if "mps" is too small */ + if (mps < 8) { + return (USB_ERR_INVAL); + } + } + + dd->bMaxPacketSize = mps; + + } else { + /* We only have one choice */ + if (mps == 255) { + mps = 512; + } + /* Check if we support the specified wMaxPacketSize */ + if (pf->max_in_frame_size < mps) { + return (USB_ERR_INVAL); + } + } + return (0); /* success */ + } + if (desc->bDescriptorType != UDESC_CONFIG) { + return (USB_ERR_INVAL); + } + if (desc->bLength < sizeof(*(ues->cd))) { + return (USB_ERR_INVAL); + } + ues = udev->bus->scratch[0].hw_ep_scratch; + + bzero(ues, sizeof(*ues)); + + ues->ep_max = ues->ep; + ues->cd = (void *)desc; + ues->methods = methods; + ues->udev = udev; + + /* Get all the endpoints we need */ + + if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || + usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || + usb2_hw_ep_get_needs(ues, UE_CONTROL, 0) || + usb2_hw_ep_get_needs(ues, UE_BULK, 0)) { + DPRINTFN(0, "Could not get needs\n"); + return (USB_ERR_INVAL); + } + for (ep = ues->ep; ep != ues->ep_max; ep++) { + + while (ep->needs_in || ep->needs_out) { + + /* + * First try to use a simplex endpoint. + * Then try to use a duplex endpoint. + */ + if (usb2_hw_ep_find_match(ues, ep, 1) && + usb2_hw_ep_find_match(ues, ep, 0)) { + DPRINTFN(0, "Could not find match\n"); + return (USB_ERR_INVAL); + } + } + } + + ues->ep_max = ues->ep; + + /* Update all endpoint addresses */ + + if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || + usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || + usb2_hw_ep_get_needs(ues, UE_CONTROL, 1) || + usb2_hw_ep_get_needs(ues, UE_BULK, 1)) { + DPRINTFN(0, "Could not update endpoint address\n"); + return (USB_ERR_INVAL); + } + return (0); /* success */ +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_tdd + * + * Returns: + * NULL: No USB template device descriptor found. + * Else: Pointer to the USB template device descriptor. + *------------------------------------------------------------------------*/ +static const struct usb2_temp_device_desc * +usb2_temp_get_tdd(struct usb2_device *udev) +{ + if (udev->usb2_template_ptr == NULL) { + return (NULL); + } + return (udev->usb2_template_ptr->tdd); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_device_desc + * + * Returns: + * NULL: No USB device descriptor found. + * Else: Pointer to USB device descriptor. + *------------------------------------------------------------------------*/ +static void * +usb2_temp_get_device_desc(struct usb2_device *udev) +{ + struct usb2_device_descriptor *dd; + + if (udev->usb2_template_ptr == NULL) { + return (NULL); + } + dd = &udev->usb2_template_ptr->udd; + if (dd->bDescriptorType != UDESC_DEVICE) { + /* sanity check failed */ + return (NULL); + } + return (dd); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_qualifier_desc + * + * Returns: + * NULL: No USB device_qualifier descriptor found. + * Else: Pointer to USB device_qualifier descriptor. + *------------------------------------------------------------------------*/ +static void * +usb2_temp_get_qualifier_desc(struct usb2_device *udev) +{ + struct usb2_device_qualifier *dq; + + if (udev->usb2_template_ptr == NULL) { + return (NULL); + } + dq = &udev->usb2_template_ptr->udq; + if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { + /* sanity check failed */ + return (NULL); + } + return (dq); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_config_desc + * + * Returns: + * NULL: No USB config descriptor found. + * Else: Pointer to USB config descriptor having index "index". + *------------------------------------------------------------------------*/ +static void * +usb2_temp_get_config_desc(struct usb2_device *udev, + uint16_t *pLength, uint8_t index) +{ + struct usb2_device_descriptor *dd; + struct usb2_config_descriptor *cd; + uint16_t temp; + + if (udev->usb2_template_ptr == NULL) { + return (NULL); + } + dd = &udev->usb2_template_ptr->udd; + cd = (void *)(udev->usb2_template_ptr + 1); + + if (index >= dd->bNumConfigurations) { + /* out of range */ + return (NULL); + } + while (index--) { + if (cd->bDescriptorType != UDESC_CONFIG) { + /* sanity check failed */ + return (NULL); + } + temp = UGETW(cd->wTotalLength); + cd = USB_ADD_BYTES(cd, temp); + } + + if (pLength) { + *pLength = UGETW(cd->wTotalLength); + } + return (cd); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_vendor_desc + * + * Returns: + * NULL: No vendor descriptor found. + * Else: Pointer to a vendor descriptor. + *------------------------------------------------------------------------*/ +static const void * +usb2_temp_get_vendor_desc(struct usb2_device *udev, + const struct usb2_device_request *req) +{ + const struct usb2_temp_device_desc *tdd; + + tdd = usb2_temp_get_tdd(udev); + if (tdd == NULL) { + return (NULL); + } + if (tdd->getVendorDesc == NULL) { + return (NULL); + } + return ((tdd->getVendorDesc) (req)); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_string_desc + * + * Returns: + * NULL: No string descriptor found. + * Else: Pointer to a string descriptor. + *------------------------------------------------------------------------*/ +static const void * +usb2_temp_get_string_desc(struct usb2_device *udev, + uint16_t lang_id, uint8_t string_index) +{ + const struct usb2_temp_device_desc *tdd; + + tdd = usb2_temp_get_tdd(udev); + if (tdd == NULL) { + return (NULL); + } + if (tdd->getStringDesc == NULL) { + return (NULL); + } + return ((tdd->getStringDesc) (lang_id, string_index)); +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_hub_desc + * + * Returns: + * NULL: No USB HUB descriptor found. + * Else: Pointer to a USB HUB descriptor. + *------------------------------------------------------------------------*/ +static const void * +usb2_temp_get_hub_desc(struct usb2_device *udev) +{ + return (NULL); /* needs to be implemented */ +} + +/*------------------------------------------------------------------------* + * usb2_temp_get_desc + * + * This function is a demultiplexer for local USB device side control + * endpoint requests. + *------------------------------------------------------------------------*/ +static void +usb2_temp_get_desc(struct usb2_device *udev, struct usb2_device_request *req, + const void **pPtr, uint16_t *pLength) +{ + const uint8_t *buf; + uint16_t len; + + buf = NULL; + len = 0; + + switch (req->bmRequestType) { + case UT_READ_DEVICE: + switch (req->bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_descriptor; + default: + goto tr_stalled; + } + break; + case UT_READ_CLASS_DEVICE: + switch (req->bRequest) { + case UR_GET_DESCRIPTOR: + goto tr_handle_get_class_descriptor; + default: + goto tr_stalled; + } + break; + case UT_READ_VENDOR_DEVICE: + case UT_READ_VENDOR_OTHER: + buf = usb2_temp_get_vendor_desc(udev, req); + goto tr_valid; + default: + goto tr_stalled; + } + +tr_handle_get_descriptor: + switch (req->wValue[1]) { + case UDESC_DEVICE: + if (req->wValue[0]) { + goto tr_stalled; + } + buf = usb2_temp_get_device_desc(udev); + goto tr_valid; + case UDESC_DEVICE_QUALIFIER: + if (udev->speed != USB_SPEED_HIGH) { + goto tr_stalled; + } + if (req->wValue[0]) { + goto tr_stalled; + } + buf = usb2_temp_get_qualifier_desc(udev); + goto tr_valid; + case UDESC_OTHER_SPEED_CONFIGURATION: + if (udev->speed != USB_SPEED_HIGH) { + goto tr_stalled; + } + case UDESC_CONFIG: + buf = usb2_temp_get_config_desc(udev, + &len, req->wValue[0]); + goto tr_valid; + case UDESC_STRING: + buf = usb2_temp_get_string_desc(udev, + UGETW(req->wIndex), req->wValue[0]); + goto tr_valid; + default: + goto tr_stalled; + } + goto tr_stalled; + +tr_handle_get_class_descriptor: + if (req->wValue[0]) { + goto tr_stalled; + } + buf = usb2_temp_get_hub_desc(udev); + goto tr_valid; + +tr_valid: + if (buf == NULL) { + goto tr_stalled; + } + if (len == 0) { + len = buf[0]; + } + *pPtr = buf; + *pLength = len; + return; + +tr_stalled: + *pPtr = NULL; + *pLength = 0; + return; +} + +/*------------------------------------------------------------------------* + * usb2_temp_setup + * + * This function generates USB descriptors according to the given USB + * template device descriptor. It will also try to figure out the best + * matching endpoint addresses using the hardware endpoint profiles. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_temp_setup(struct usb2_device *udev, + const struct usb2_temp_device_desc *tdd) +{ + struct usb2_temp_setup *uts; + void *buf; + uint8_t n; + + if (tdd == NULL) { + /* be NULL safe */ + return (0); + } + uts = udev->bus->scratch[0].temp_setup; + + bzero(uts, sizeof(*uts)); + + uts->usb2_speed = udev->speed; + uts->self_powered = udev->flags.self_powered; + + /* first pass */ + + usb2_make_device_desc(uts, tdd); + + if (uts->err) { + /* some error happened */ + return (uts->err); + } + /* sanity check */ + if (uts->size == 0) { + return (USB_ERR_INVAL); + } + /* allocate zeroed memory */ + uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO); + if (uts->buf == NULL) { + /* could not allocate memory */ + return (USB_ERR_NOMEM); + } + /* second pass */ + + uts->size = 0; + + usb2_make_device_desc(uts, tdd); + + /* + * Store a pointer to our descriptors: + */ + udev->usb2_template_ptr = uts->buf; + + if (uts->err) { + /* some error happened during second pass */ + goto error; + } + /* + * Resolve all endpoint addresses ! + */ + buf = usb2_temp_get_device_desc(udev); + uts->err = usb2_hw_ep_resolve(udev, buf); + if (uts->err) { + DPRINTFN(0, "Could not resolve endpoints for " + "Device Descriptor, error = %s\n", + usb2_errstr(uts->err)); + goto error; + } + for (n = 0;; n++) { + + buf = usb2_temp_get_config_desc(udev, NULL, n); + if (buf == NULL) { + break; + } + uts->err = usb2_hw_ep_resolve(udev, buf); + if (uts->err) { + DPRINTFN(0, "Could not resolve endpoints for " + "Config Descriptor %u, error = %s\n", n, + usb2_errstr(uts->err)); + goto error; + } + } + return (uts->err); + +error: + usb2_temp_unsetup(udev); + return (uts->err); +} + +/*------------------------------------------------------------------------* + * usb2_temp_unsetup + * + * This function frees any memory associated with the currently + * setup template, if any. + *------------------------------------------------------------------------*/ +static void +usb2_temp_unsetup(struct usb2_device *udev) +{ + if (udev->usb2_template_ptr) { + + free(udev->usb2_template_ptr, M_USB); + + udev->usb2_template_ptr = NULL; + } + return; +} + +static usb2_error_t +usb2_temp_setup_by_index(struct usb2_device *udev, uint16_t index) +{ + usb2_error_t err; + + switch (index) { + case 0: + err = usb2_temp_setup(udev, &usb2_template_msc); + break; + case 1: + err = usb2_temp_setup(udev, &usb2_template_cdce); + break; + case 2: + err = usb2_temp_setup(udev, &usb2_template_mtp); + break; + default: + return (USB_ERR_INVAL); + } + + return (err); +} + +static void +usb2_temp_init(void *arg) +{ + /* register our functions */ + usb2_temp_get_desc_p = &usb2_temp_get_desc; + usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index; + usb2_temp_unsetup_p = &usb2_temp_unsetup; + return; +} + +SYSINIT(usb2_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_temp_init, NULL); +SYSUNINIT(usb2_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb2_temp_unload, NULL); diff --git a/sys/dev/usb2/template/usb2_template.h b/sys/dev/usb2/template/usb2_template.h new file mode 100644 index 000000000000..361de3abf428 --- /dev/null +++ b/sys/dev/usb2/template/usb2_template.h @@ -0,0 +1,102 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007 Hans Petter Selasky + * 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. + */ + +/* USB templates are used to build up real USB descriptors */ + +#ifndef _USB_TEMPLATE_H_ +#define _USB_TEMPLATE_H_ + +typedef const void *(usb2_temp_get_string_desc_t)(uint16_t lang_id, uint8_t string_index); +typedef const void *(usb2_temp_get_vendor_desc_t)(const struct usb2_device_request *req); + +struct usb2_temp_packet_size { + uint16_t mps[USB_SPEED_MAX]; +}; + +struct usb2_temp_interval { + uint8_t bInterval[USB_SPEED_MAX]; +}; + +struct usb2_temp_endpoint_desc { + const void **ppRawDesc; + const struct usb2_temp_packet_size *pPacketSize; + const struct usb2_temp_interval *pIntervals; + /* + * If (bEndpointAddress & UE_ADDR) is non-zero the endpoint number + * is pre-selected for this endpoint descriptor. Else an endpoint + * number is automatically chosen. + */ + uint8_t bEndpointAddress; /* UE_DIR_IN or UE_DIR_OUT */ + uint8_t bmAttributes; +}; + +struct usb2_temp_interface_desc { + const void **ppRawDesc; + const struct usb2_temp_endpoint_desc **ppEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + uint8_t isAltInterface; +}; + +struct usb2_temp_config_desc { + const struct usb2_temp_interface_desc **ppIfaceDesc; + uint8_t bmAttributes; + uint8_t bMaxPower; + uint8_t iConfiguration; +}; + +struct usb2_temp_device_desc { + usb2_temp_get_string_desc_t *getStringDesc; + usb2_temp_get_vendor_desc_t *getVendorDesc; + const struct usb2_temp_config_desc **ppConfigDesc; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; +}; + +struct usb2_temp_data { + const struct usb2_temp_device_desc *tdd; + struct usb2_device_descriptor udd; /* device descriptor */ + struct usb2_device_qualifier udq; /* device qualifier */ +}; + +/* prototypes */ + +extern const struct usb2_temp_device_desc usb2_template_cdce; +extern const struct usb2_temp_device_desc usb2_template_msc; /* Mass Storage Class */ +extern const struct usb2_temp_device_desc usb2_template_mtp; /* Message Transfer + * Protocol */ + +#endif /* _USB_TEMPLATE_H_ */ diff --git a/sys/dev/usb2/template/usb2_template_cdce.c b/sys/dev/usb2/template/usb2_template_cdce.c new file mode 100644 index 000000000000..119c6bd628a0 --- /dev/null +++ b/sys/dev/usb2/template/usb2_template_cdce.c @@ -0,0 +1,325 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2007 Hans Petter Selasky + * 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 file contains the USB templates for a CDC USB ethernet device. + */ + +#include +#include +#include + +#include + +#include + +enum { + STRING_LANG_INDEX, + STRING_MAC_INDEX, + STRING_ETH_CONTROL_INDEX, + STRING_ETH_CONTROL_512X4_INDEX, + STRING_ETH_DATA_INDEX, + STRING_ETH_CONFIG_INDEX, + STRING_ETH_VENDOR_INDEX, + STRING_ETH_PRODUCT_INDEX, + STRING_ETH_SERIAL_INDEX, + STRING_ETH_MAX, +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_MAC \ + '2', 0, 'A', 0, '2', 0, '3', 0, \ + '4', 0, '5', 0, '6', 0, '7', 0, \ + '8', 0, '9', 0, 'A', 0, 'B', 0, + +#define STRING_ETH_CONTROL \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'E', 0, 't', 0, 'h', 0, 'e', 0, \ + 'r', 0, 'n', 0, 'e', 0, 't', 0, \ + ' ', 0, 'C', 0, 'o', 0, 'm', 0, \ + 'm', 0, ' ', 0, 'i', 0, 'n', 0, \ + 't', 0, 'e', 0, 'r', 0, 'f', 0, \ + 'a', 0, 'c', 0, 'e', 0, + +/* + * Hardware Accelerated Zero Copy CDC ethernet. Buffer capacity: 512 + * frames by 4 fragments per USB transfer. + */ +#define STRING_ETH_512X4_CONTROL \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'E', 0, 't', 0, 'h', 0, 'e', 0, \ + 'r', 0, 'n', 0, 'e', 0, 't', 0, \ + ' ', 0, 'C', 0, 'o', 0, 'm', 0, \ + 'm', 0, ' ', 0, 'i', 0, 'n', 0, \ + 't', 0, 'e', 0, 'r', 0, 'f', 0, \ + 'a', 0, 'c', 0, 'e', 0, ' ', 0, \ + '5', 0, '1', 0, '2', 0, 'x', 0, \ + '4', 0, ' ', 0, 'H', 0, 'W', 0, \ + ' ', 0, 'A', 0, 'c', 0, 'c', 0, \ + 'e', 0, 'l', 0, 'e', 0, 'r', 0, \ + 'a', 0, 't', 0, 'e', 0, 'd', 0, + +#define STRING_ETH_DATA \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'E', 0, 't', 0, 'h', 0, 'e', 0, \ + 'r', 0, 'n', 0, 'e', 0, 't', 0, \ + ' ', 0, 'D', 0, 'a', 0, 't', 0, \ + 'a', 0, ' ', 0, 'i', 0, 'n', 0, \ + 't', 0, 'e', 0, 'r', 0, 'f', 0, \ + 'a', 0, 'c', 0, 'e', 0, + +#define STRING_ETH_CONFIG \ + 'D', 0, 'e', 0, 'f', 0, 'a', 0, \ + 'u', 0, 'l', 0, 't', 0, ' ', 0, \ + 'c', 0, 'o', 0, 'n', 0, 'f', 0, \ + 'i', 0, 'g', 0, + +#define STRING_ETH_VENDOR \ + 'F', 0, 'r', 0, 'e', 0, 'e', 0, \ + 'B', 0, 'S', 0, 'D', 0, ' ', 0, \ + 'f', 0, 'o', 0, 'u', 0, 'n', 0, \ + 'd', 0, 'a', 0, 't', 0, 'i', 0, \ + 'o', 0, 'n', 0, + +#define STRING_ETH_PRODUCT \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'E', 0, 't', 0, 'h', 0, 'e', 0, \ + 'r', 0, 'n', 0, 'e', 0, 't', 0, \ + ' ', 0, 'A', 0, 'd', 0, 'a', 0, \ + 'p', 0, 't', 0, 'e', 0, 'r', 0, + +#define STRING_ETH_SERIAL \ + 'D', 0, 'e', 0, 'c', 0, 'e', 0, \ + 'm', 0, 'b', 0, 'e', 0, 'r', 0, \ + ' ', 0, '2', 0, '0', 0, '0', 0, \ + '7', 0, + +/* make the real string descriptors */ + +USB_MAKE_STRING_DESC(STRING_LANG, string_lang); +USB_MAKE_STRING_DESC(STRING_MAC, string_mac); +USB_MAKE_STRING_DESC(STRING_ETH_CONTROL, string_eth_control); +USB_MAKE_STRING_DESC(STRING_ETH_512X4_CONTROL, string_eth_512x4_control); +USB_MAKE_STRING_DESC(STRING_ETH_DATA, string_eth_data); +USB_MAKE_STRING_DESC(STRING_ETH_CONFIG, string_eth_config); +USB_MAKE_STRING_DESC(STRING_ETH_VENDOR, string_eth_vendor); +USB_MAKE_STRING_DESC(STRING_ETH_PRODUCT, string_eth_product); +USB_MAKE_STRING_DESC(STRING_ETH_SERIAL, string_eth_serial); + +/* prototypes */ + +static usb2_temp_get_string_desc_t eth_get_string_desc; + +static const struct usb2_cdc_union_descriptor eth_union_desc = { + .bLength = sizeof(eth_union_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_UNION, + .bMasterInterface = 0, /* this is automatically updated */ + .bSlaveInterface[0] = 1, /* this is automatically updated */ +}; + +static const struct usb2_cdc_header_descriptor eth_header_desc = { + .bLength = sizeof(eth_header_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_HEADER, + .bcdCDC[0] = 0x10, + .bcdCDC[1] = 0x01, +}; + +static const struct usb2_cdc_ethernet_descriptor eth_enf_desc = { + .bLength = sizeof(eth_enf_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_ENF, + .iMacAddress = STRING_MAC_INDEX, + .bmEthernetStatistics = {0, 0, 0, 0}, + .wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */ + .wNumberMCFilters = {0, 0}, + .bNumberPowerFilters = 0, +}; + +static const void *eth_control_if_desc[] = { + ð_union_desc, + ð_header_desc, + ð_enf_desc, + NULL, +}; + +static const struct usb2_temp_packet_size bulk_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 512, +}; + +static const struct usb2_temp_packet_size intr_mps = { + .mps[USB_SPEED_FULL] = 8, + .mps[USB_SPEED_HIGH] = 8, +}; + +static const struct usb2_temp_endpoint_desc bulk_in_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_IN_EP_0 + .bEndpointAddress = USB_HIP_IN_EP_0, +#else + .bEndpointAddress = UE_DIR_IN, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc bulk_out_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_OUT_EP_0 + .bEndpointAddress = USB_HIP_OUT_EP_0, +#else + .bEndpointAddress = UE_DIR_OUT, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc intr_in_ep = { + .pPacketSize = &intr_mps, + .bEndpointAddress = UE_DIR_IN, + .bmAttributes = UE_INTERRUPT, +}; + +static const struct usb2_temp_endpoint_desc *eth_intr_endpoints[] = { + &intr_in_ep, + NULL, +}; + +static const struct usb2_temp_interface_desc eth_control_interface = { + .ppEndpoints = eth_intr_endpoints, + .ppRawDesc = eth_control_if_desc, + .bInterfaceClass = UICLASS_CDC, + .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, + .bInterfaceProtocol = 0, + .iInterface = STRING_ETH_CONTROL_INDEX, +}; + +static const struct usb2_temp_interface_desc eth_control_if_512x4 = { + .ppEndpoints = eth_intr_endpoints, + .ppRawDesc = eth_control_if_desc, + .bInterfaceClass = UICLASS_CDC, + .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, + .bInterfaceProtocol = UIPROTO_CDC_ETH_512X4, + .iInterface = STRING_ETH_CONTROL_512X4_INDEX, + .isAltInterface = 1, /* this is an alternate setting */ +}; + + +static const struct usb2_temp_endpoint_desc *eth_data_endpoints[] = { + &bulk_in_ep, + &bulk_out_ep, + NULL, +}; + +static const struct usb2_temp_interface_desc eth_data_null_interface = { + .ppEndpoints = NULL, /* no endpoints */ + .bInterfaceClass = UICLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_ETH_DATA_INDEX, +}; + +static const struct usb2_temp_interface_desc eth_data_interface = { + .ppEndpoints = eth_data_endpoints, + .bInterfaceClass = UICLASS_CDC_DATA, + .bInterfaceSubClass = UISUBCLASS_DATA, + .bInterfaceProtocol = 0, + .iInterface = STRING_ETH_DATA_INDEX, + .isAltInterface = 1, /* this is an alternate setting */ +}; + +static const struct usb2_temp_interface_desc *eth_interfaces[] = { + ð_control_interface, + ð_control_if_512x4, + ð_data_null_interface, + ð_data_interface, + NULL, +}; + +static const struct usb2_temp_config_desc eth_config_desc = { + .ppIfaceDesc = eth_interfaces, + .bmAttributes = UC_BUS_POWERED, + .bMaxPower = 25, /* 50 mA */ + .iConfiguration = STRING_ETH_CONFIG_INDEX, +}; + +static const struct usb2_temp_config_desc *eth_configs[] = { + ð_config_desc, + NULL, +}; + +const struct usb2_temp_device_desc usb2_template_cdce = { + .getStringDesc = ð_get_string_desc, + .ppConfigDesc = eth_configs, + .idVendor = 0x0001, + .idProduct = 0x0001, + .bcdDevice = 0x0100, + .bDeviceClass = UDCLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .iManufacturer = STRING_ETH_VENDOR_INDEX, + .iProduct = STRING_ETH_PRODUCT_INDEX, + .iSerialNumber = STRING_ETH_SERIAL_INDEX, +}; + +/*------------------------------------------------------------------------* + * eth_get_string_desc + * + * Return values: + * NULL: Failure. No such string. + * Else: Success. Pointer to string descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +eth_get_string_desc(uint16_t lang_id, uint8_t string_index) +{ + static const void *ptr[STRING_ETH_MAX] = { + [STRING_LANG_INDEX] = &string_lang, + [STRING_MAC_INDEX] = &string_mac, + [STRING_ETH_CONTROL_INDEX] = &string_eth_control, + [STRING_ETH_CONTROL_512X4_INDEX] = &string_eth_512x4_control, + [STRING_ETH_DATA_INDEX] = &string_eth_data, + [STRING_ETH_CONFIG_INDEX] = &string_eth_config, + [STRING_ETH_VENDOR_INDEX] = &string_eth_vendor, + [STRING_ETH_PRODUCT_INDEX] = &string_eth_product, + [STRING_ETH_SERIAL_INDEX] = &string_eth_serial, + }; + + if (string_index == 0) { + return (&string_lang); + } + if (lang_id != 0x0409) { + return (NULL); + } + if (string_index < STRING_ETH_MAX) { + return (ptr[string_index]); + } + return (NULL); +} diff --git a/sys/dev/usb2/template/usb2_template_msc.c b/sys/dev/usb2/template/usb2_template_msc.c new file mode 100644 index 000000000000..b8bc7ad2dfd8 --- /dev/null +++ b/sys/dev/usb2/template/usb2_template_msc.c @@ -0,0 +1,199 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2008 Hans Petter Selasky + * 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 file contains the USB templates for an USB Mass Storage Device. + */ + +#include +#include + +#include + +#include + +enum { + STRING_LANG_INDEX, + STRING_MSC_DATA_INDEX, + STRING_MSC_CONFIG_INDEX, + STRING_MSC_VENDOR_INDEX, + STRING_MSC_PRODUCT_INDEX, + STRING_MSC_SERIAL_INDEX, + STRING_MSC_MAX, +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_MSC_DATA \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'M', 0, 'a', 0, 's', 0, 's', 0, \ + ' ', 0, 'S', 0, 't', 0, 'o', 0, \ + 'r', 0, 'a', 0, 'g', 0, 'e', 0, \ + ' ', 0, 'I', 0, 'n', 0, 't', 0, \ + 'e', 0, 'r', 0, 'f', 0, 'a', 0, \ + 'c', 0, 'e', 0, + +#define STRING_MSC_CONFIG \ + 'D', 0, 'e', 0, 'f', 0, 'a', 0, \ + 'u', 0, 'l', 0, 't', 0, ' ', 0, \ + 'c', 0, 'o', 0, 'n', 0, 'f', 0, \ + 'i', 0, 'g', 0, + +#define STRING_MSC_VENDOR \ + 'F', 0, 'r', 0, 'e', 0, 'e', 0, \ + 'B', 0, 'S', 0, 'D', 0, ' ', 0, \ + 'f', 0, 'o', 0, 'u', 0, 'n', 0, \ + 'd', 0, 'a', 0, 't', 0, 'i', 0, \ + 'o', 0, 'n', 0, + +#define STRING_MSC_PRODUCT \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'M', 0, 'e', 0, 'm', 0, 'o', 0, \ + 'r', 0, 'y', 0, ' ', 0, 'S', 0, \ + 't', 0, 'i', 0, 'c', 0, 'k', 0 + +#define STRING_MSC_SERIAL \ + 'M', 0, 'a', 0, 'r', 0, 'c', 0, \ + 'h', 0, ' ', 0, '2', 0, '0', 0, \ + '0', 0, '8', 0, + +/* make the real string descriptors */ + +USB_MAKE_STRING_DESC(STRING_LANG, string_lang); +USB_MAKE_STRING_DESC(STRING_MSC_DATA, string_msc_data); +USB_MAKE_STRING_DESC(STRING_MSC_CONFIG, string_msc_config); +USB_MAKE_STRING_DESC(STRING_MSC_VENDOR, string_msc_vendor); +USB_MAKE_STRING_DESC(STRING_MSC_PRODUCT, string_msc_product); +USB_MAKE_STRING_DESC(STRING_MSC_SERIAL, string_msc_serial); + +/* prototypes */ + +static usb2_temp_get_string_desc_t msc_get_string_desc; + +static const struct usb2_temp_packet_size bulk_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 512, +}; + +static const struct usb2_temp_endpoint_desc bulk_in_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_IN_EP_0 + .bEndpointAddress = USB_HIP_IN_EP_0, +#else + .bEndpointAddress = UE_DIR_IN, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc bulk_out_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_OUT_EP_0 + .bEndpointAddress = USB_HIP_OUT_EP_0, +#else + .bEndpointAddress = UE_DIR_OUT, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc *msc_data_endpoints[] = { + &bulk_in_ep, + &bulk_out_ep, + NULL, +}; + +static const struct usb2_temp_interface_desc msc_data_interface = { + .ppEndpoints = msc_data_endpoints, + .bInterfaceClass = UICLASS_MASS, + .bInterfaceSubClass = UISUBCLASS_SCSI, + .bInterfaceProtocol = UIPROTO_MASS_BBB, + .iInterface = STRING_MSC_DATA_INDEX, +}; + +static const struct usb2_temp_interface_desc *msc_interfaces[] = { + &msc_data_interface, + NULL, +}; + +static const struct usb2_temp_config_desc msc_config_desc = { + .ppIfaceDesc = msc_interfaces, + .bmAttributes = UC_BUS_POWERED, + .bMaxPower = 25, /* 50 mA */ + .iConfiguration = STRING_MSC_CONFIG_INDEX, +}; + +static const struct usb2_temp_config_desc *msc_configs[] = { + &msc_config_desc, + NULL, +}; + +const struct usb2_temp_device_desc usb2_template_msc = { + .getStringDesc = &msc_get_string_desc, + .ppConfigDesc = msc_configs, + .idVendor = 0x0001, + .idProduct = 0x0001, + .bcdDevice = 0x0100, + .bDeviceClass = UDCLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .iManufacturer = STRING_MSC_VENDOR_INDEX, + .iProduct = STRING_MSC_PRODUCT_INDEX, + .iSerialNumber = STRING_MSC_SERIAL_INDEX, +}; + +/*------------------------------------------------------------------------* + * msc_get_string_desc + * + * Return values: + * NULL: Failure. No such string. + * Else: Success. Pointer to string descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +msc_get_string_desc(uint16_t lang_id, uint8_t string_index) +{ + static const void *ptr[STRING_MSC_MAX] = { + [STRING_LANG_INDEX] = &string_lang, + [STRING_MSC_DATA_INDEX] = &string_msc_data, + [STRING_MSC_CONFIG_INDEX] = &string_msc_config, + [STRING_MSC_VENDOR_INDEX] = &string_msc_vendor, + [STRING_MSC_PRODUCT_INDEX] = &string_msc_product, + [STRING_MSC_SERIAL_INDEX] = &string_msc_serial, + }; + + if (string_index == 0) { + return (&string_lang); + } + if (lang_id != 0x0409) { + return (NULL); + } + if (string_index < STRING_MSC_MAX) { + return (ptr[string_index]); + } + return (NULL); +} diff --git a/sys/dev/usb2/template/usb2_template_mtp.c b/sys/dev/usb2/template/usb2_template_mtp.c new file mode 100644 index 000000000000..f1599fc79388 --- /dev/null +++ b/sys/dev/usb2/template/usb2_template_mtp.c @@ -0,0 +1,262 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2008 Hans Petter Selasky + * 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 file contains the USB templates for an USB Message Transfer + * Protocol device. + * + * NOTE: It is common practice that MTP devices use some dummy + * descriptor cludges to be automatically detected by the host + * operating system. These descriptors are documented in the LibMTP + * library at sourceforge.net. The alternative is to supply the host + * operating system the VID and PID of your device. + */ + +#include +#include + +#include + +#include + +#define MTP_BREQUEST 0x08 + +enum { + STRING_LANG_INDEX, + STRING_MTP_DATA_INDEX, + STRING_MTP_CONFIG_INDEX, + STRING_MTP_VENDOR_INDEX, + STRING_MTP_PRODUCT_INDEX, + STRING_MTP_SERIAL_INDEX, + STRING_MTP_MAX, +}; + +#define STRING_LANG \ + 0x09, 0x04, /* American English */ + +#define STRING_MTP_DATA \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'M', 0, 'T', 0, 'P', 0, \ + ' ', 0, 'I', 0, 'n', 0, 't', 0, \ + 'e', 0, 'r', 0, 'f', 0, 'a', 0, \ + 'c', 0, 'e', 0, + +#define STRING_MTP_CONFIG \ + 'D', 0, 'e', 0, 'f', 0, 'a', 0, \ + 'u', 0, 'l', 0, 't', 0, ' ', 0, \ + 'c', 0, 'o', 0, 'n', 0, 'f', 0, \ + 'i', 0, 'g', 0, + +#define STRING_MTP_VENDOR \ + 'F', 0, 'r', 0, 'e', 0, 'e', 0, \ + 'B', 0, 'S', 0, 'D', 0, ' ', 0, \ + 'f', 0, 'o', 0, 'u', 0, 'n', 0, \ + 'd', 0, 'a', 0, 't', 0, 'i', 0, \ + 'o', 0, 'n', 0, + +#define STRING_MTP_PRODUCT \ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, \ + 'M', 0, 'T', 0, 'P', 0, + +#define STRING_MTP_SERIAL \ + 'J', 0, 'u', 0, 'n', 0, 'e', 0, \ + ' ', 0, '2', 0, '0', 0, '0', 0, \ + '8', 0, + +/* make the real string descriptors */ + +USB_MAKE_STRING_DESC(STRING_LANG, string_lang); +USB_MAKE_STRING_DESC(STRING_MTP_DATA, string_mtp_data); +USB_MAKE_STRING_DESC(STRING_MTP_CONFIG, string_mtp_config); +USB_MAKE_STRING_DESC(STRING_MTP_VENDOR, string_mtp_vendor); +USB_MAKE_STRING_DESC(STRING_MTP_PRODUCT, string_mtp_product); +USB_MAKE_STRING_DESC(STRING_MTP_SERIAL, string_mtp_serial); + +/* prototypes */ + +static usb2_temp_get_string_desc_t mtp_get_string_desc; +static usb2_temp_get_vendor_desc_t mtp_get_vendor_desc; + +static const struct usb2_temp_packet_size bulk_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 512, +}; + +static const struct usb2_temp_packet_size intr_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 64, +}; + +static const struct usb2_temp_endpoint_desc bulk_out_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_OUT_EP_0 + .bEndpointAddress = USB_HIP_OUT_EP_0, +#else + .bEndpointAddress = UE_DIR_OUT, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc intr_in_ep = { + .pPacketSize = &intr_mps, + .bEndpointAddress = UE_DIR_IN, + .bmAttributes = UE_INTERRUPT, +}; + +static const struct usb2_temp_endpoint_desc bulk_in_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_IN_EP_0 + .bEndpointAddress = USB_HIP_IN_EP_0, +#else + .bEndpointAddress = UE_DIR_IN, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb2_temp_endpoint_desc *mtp_data_endpoints[] = { + &bulk_in_ep, + &bulk_out_ep, + &intr_in_ep, + NULL, +}; + +static const struct usb2_temp_interface_desc mtp_data_interface = { + .ppEndpoints = mtp_data_endpoints, + .bInterfaceClass = UICLASS_IMAGE, + .bInterfaceSubClass = UISUBCLASS_SIC, /* Still Image Class */ + .bInterfaceProtocol = 1, /* PIMA 15740 */ + .iInterface = STRING_MTP_DATA_INDEX, +}; + +static const struct usb2_temp_interface_desc *mtp_interfaces[] = { + &mtp_data_interface, + NULL, +}; + +static const struct usb2_temp_config_desc mtp_config_desc = { + .ppIfaceDesc = mtp_interfaces, + .bmAttributes = UC_BUS_POWERED, + .bMaxPower = 25, /* 50 mA */ + .iConfiguration = STRING_MTP_CONFIG_INDEX, +}; + +static const struct usb2_temp_config_desc *mtp_configs[] = { + &mtp_config_desc, + NULL, +}; + +const struct usb2_temp_device_desc usb2_template_mtp = { + .getStringDesc = &mtp_get_string_desc, + .getVendorDesc = &mtp_get_vendor_desc, + .ppConfigDesc = mtp_configs, + .idVendor = 0x0001, + .idProduct = 0x0001, + .bcdDevice = 0x0100, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .iManufacturer = STRING_MTP_VENDOR_INDEX, + .iProduct = STRING_MTP_PRODUCT_INDEX, + .iSerialNumber = STRING_MTP_SERIAL_INDEX, +}; + +/*------------------------------------------------------------------------* + * mtp_get_vendor_desc + * + * Return values: + * NULL: Failure. No such vendor descriptor. + * Else: Success. Pointer to vendor descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +mtp_get_vendor_desc(const struct usb2_device_request *req) +{ + static const uint8_t dummy_desc[0x28] = { + 0x28, 0, 0, 0, 0, 1, 4, 0, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0x4D, 0x54, 0x50, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + + if ((req->bmRequestType == UT_READ_VENDOR_DEVICE) && + (req->bRequest == MTP_BREQUEST) && (req->wValue[0] == 0) && + (req->wValue[1] == 0) && (req->wIndex[1] == 0) && + ((req->wIndex[0] == 4) || (req->wIndex[0] == 5))) { + /* + * By returning this descriptor LibMTP will + * automatically pickup our device. + */ + return (dummy_desc); + } + return (NULL); +} + +/*------------------------------------------------------------------------* + * mtp_get_string_desc + * + * Return values: + * NULL: Failure. No such string. + * Else: Success. Pointer to string descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +mtp_get_string_desc(uint16_t lang_id, uint8_t string_index) +{ + static const void *ptr[STRING_MTP_MAX] = { + [STRING_LANG_INDEX] = &string_lang, + [STRING_MTP_DATA_INDEX] = &string_mtp_data, + [STRING_MTP_CONFIG_INDEX] = &string_mtp_config, + [STRING_MTP_VENDOR_INDEX] = &string_mtp_vendor, + [STRING_MTP_PRODUCT_INDEX] = &string_mtp_product, + [STRING_MTP_SERIAL_INDEX] = &string_mtp_serial, + }; + + static const uint8_t dummy_desc[0x12] = { + 0x12, 0x03, 0x4D, 0x00, 0x53, 0x00, 0x46, 0x00, + 0x54, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, + MTP_BREQUEST, 0x00, + }; + + if (string_index == 0xEE) { + /* + * By returning this string LibMTP will automatically + * pickup our device. + */ + return (dummy_desc); + } + if (string_index == 0) { + return (&string_lang); + } + if (lang_id != 0x0409) { + return (NULL); + } + if (string_index < STRING_MTP_MAX) { + return (ptr[string_index]); + } + return (NULL); +} diff --git a/sys/dev/usb2/wlan/if_rum2.c b/sys/dev/usb2/wlan/if_rum2.c new file mode 100644 index 000000000000..bae3581d6f62 --- /dev/null +++ b/sys/dev/usb2/wlan/if_rum2.c @@ -0,0 +1,2961 @@ +/*- + * Copyright (c) 2005-2007 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * Copyright (c) 2007-2008 Hans Petter Selasky + * + * 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. + */ + +/* + * NOTE: all function names beginning like "rum_cfg_" can only + * be called from within the config thread function ! + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2501USB/RT2601USB chipset driver + * http://www.ralinktech.com.tw/ + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc rum_config_copy +#define usb2_config_td_softc rum_softc + +#define USB_DEBUG_VAR rum_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if USB_DEBUG +static int rum_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); +SYSCTL_INT(_hw_usb2_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0, + "Debug level"); +#endif + +/* prototypes */ + +static device_probe_t rum_probe; +static device_attach_t rum_attach; +static device_detach_t rum_detach; + +static usb2_callback_t rum_bulk_read_callback; +static usb2_callback_t rum_bulk_read_clear_stall_callback; +static usb2_callback_t rum_bulk_write_callback; +static usb2_callback_t rum_bulk_write_clear_stall_callback; + +static usb2_config_td_command_t rum_cfg_first_time_setup; +static usb2_config_td_command_t rum_config_copy; +static usb2_config_td_command_t rum_cfg_scan_start; +static usb2_config_td_command_t rum_cfg_scan_end; +static usb2_config_td_command_t rum_cfg_select_band; +static usb2_config_td_command_t rum_cfg_set_chan; +static usb2_config_td_command_t rum_cfg_enable_tsf_sync; +static usb2_config_td_command_t rum_cfg_enable_mrr; +static usb2_config_td_command_t rum_cfg_update_slot; +static usb2_config_td_command_t rum_cfg_select_antenna; +static usb2_config_td_command_t rum_cfg_set_txpreamble; +static usb2_config_td_command_t rum_cfg_update_promisc; +static usb2_config_td_command_t rum_cfg_pre_init; +static usb2_config_td_command_t rum_cfg_init; +static usb2_config_td_command_t rum_cfg_pre_stop; +static usb2_config_td_command_t rum_cfg_stop; +static usb2_config_td_command_t rum_cfg_amrr_timeout; +static usb2_config_td_command_t rum_cfg_prepare_beacon; +static usb2_config_td_command_t rum_cfg_newstate; + +static const char *rum_get_rf(uint32_t rev); +static int rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); +static void rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func); +static void rum_scan_start_cb(struct ieee80211com *); +static void rum_scan_end_cb(struct ieee80211com *); +static void rum_set_channel_cb(struct ieee80211com *); +static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr); +static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *sc); +static uint32_t rum_cfg_read(struct rum_softc *sc, uint16_t reg); +static uint8_t rum_cfg_bbp_init(struct rum_softc *sc); +static uint8_t rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg); +static void rum_cfg_amrr_start(struct rum_softc *sc); +static void rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val); +static void rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, void *data); +static void rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len); +static void rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size); +static void rum_cfg_read_eeprom(struct rum_softc *sc); +static void rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len); +static void rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val); +static void rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid); +static void rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr); +static void rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val); +static void rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len); +static void rum_end_of_commands(struct rum_softc *sc); +static void rum_init_cb(void *arg); +static void rum_start_cb(struct ifnet *ifp); +static void rum_watchdog(void *arg); +static uint8_t rum_get_rssi(struct rum_softc *sc, uint8_t raw); +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]); +static void rum_vap_delete(struct ieee80211vap *); +static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); +static void rum_newassoc(struct ieee80211_node *, int); +static void rum_cfg_disable_tsf_sync(struct rum_softc *sc); +static void rum_cfg_set_run(struct rum_softc *sc, struct rum_config_copy *cc); +static void rum_fill_write_queue(struct rum_softc *sc); +static void rum_tx_clean_queue(struct rum_softc *sc); +static void rum_tx_freem(struct mbuf *m); +static void rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static struct ieee80211vap *rum_get_vap(struct rum_softc *sc); +static void rum_tx_data(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static void rum_tx_prot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate); +static void rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params); +static int rum_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); +static void rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, uint16_t xflags, uint16_t rate); +static int rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg); +static void rum_update_mcast_cb(struct ifnet *ifp); +static void rum_update_promisc_cb(struct ifnet *ifp); + +/* various supported device vendors/products */ +static const struct usb2_device_id rum_devs[] = { + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4, 0)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700, 0)}, + {USB_VPI(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO, 0)}, + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1, 0)}, + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR, 0)}, + {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2, 0)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL, 0)}, + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX, 0)}, + {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F, 0)}, + {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573, 0)}, + {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1, 0)}, + {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340, 0)}, + {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111, 0)}, + {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110, 0)}, + {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS, 0)}, + {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS, 0)}, + {USB_VPI(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573, 0)}, + {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573, 0)}, + {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB, 0)}, + {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP, 0)}, + {USB_VPI(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4, 0)}, + {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573, 0)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP, 0)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2, 0)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM, 0)}, + {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573, 0)}, + {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671, 0)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2, 0)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172, 0)}, + {USB_VPI(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573, 0)}, + {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573, 0)}, +}; + +struct rum_def_mac { + uint32_t reg; + uint32_t val; +}; + +static const struct rum_def_mac 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} +}; + +struct rum_def_bbp { + uint8_t reg; + uint8_t val; +}; + +static const struct rum_def_bbp 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} +}; + +struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +}; + +static const struct rfprog 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} +}; + +static const struct rfprog 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 const struct usb2_config rum_config[RUM_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &rum_bulk_write_callback, + .mh.timeout = 5000, /* ms */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &rum_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &rum_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &rum_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t rum_devclass; + +static device_method_t rum_methods[] = { + DEVMETHOD(device_probe, rum_probe), + DEVMETHOD(device_attach, rum_attach), + DEVMETHOD(device_detach, rum_detach), + {0, 0} +}; + +static driver_t rum_driver = { + .name = "rum", + .methods = rum_methods, + .size = sizeof(struct rum_softc), +}; + +DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0); +MODULE_DEPEND(rum, usb2_wlan, 1, 1, 1); +MODULE_DEPEND(rum, usb2_core, 1, 1, 1); +MODULE_DEPEND(rum, wlan, 1, 1, 1); +MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); + +static int +rum_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != 0) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); +} + +static int +rum_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct rum_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "rum lock", MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + sc->sc_udev = uaa->device; + sc->sc_unit = device_get_unit(dev); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = RT2573_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + &rum_end_of_commands, + sizeof(struct usb2_config_td_cc), 24); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &rum_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + rum_watchdog(sc); + + return (0); /* success */ + +detach: + rum_detach(dev); + return (ENXIO); /* failure */ +} + +static int +rum_detach(device_t dev) +{ + struct rum_softc *sc = device_get_softc(dev); + struct ieee80211com *ic; + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + rum_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + ic = ifp->if_l2com; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + +repeat: + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + + /* wait a little before next try */ + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { + goto error; + } + /* try until we are detached */ + goto repeat; + +error: + /* the device has been detached */ + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + rum_cfg_do_request(sc, &req, buf); + return; +} + +static uint16_t +rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr) +{ + uint16_t tmp; + + rum_cfg_eeprom_read(sc, addr, &tmp, sizeof(tmp)); + return (le16toh(tmp)); +} + +static uint32_t +rum_cfg_read(struct rum_softc *sc, uint16_t reg) +{ + uint32_t val; + + rum_cfg_read_multi(sc, reg, &val, sizeof(val)); + return (le32toh(val)); +} + +static void +rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + rum_cfg_do_request(sc, &req, buf); + return; +} + +static void +rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val) +{ + uint32_t tmp = htole32(val); + + rum_cfg_write_multi(sc, reg, &tmp, sizeof(tmp)); + return; +} + +static void +rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + rum_cfg_do_request(sc, &req, buf); + return; +} + +static uint32_t +rum_cfg_bbp_disbusy(struct rum_softc *sc) +{ + uint32_t tmp; + uint8_t to; + + for (to = 0;; to++) { + if (to < 100) { + tmp = rum_cfg_read(sc, RT2573_PHY_CSR3); + + if ((tmp & RT2573_BBP_BUSY) == 0) { + return (tmp); + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + break; + } + } + DPRINTF("could not disbusy BBP\n"); + return (RT2573_BBP_BUSY); /* failure */ +} + +static void +rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + + if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { + return; + } + tmp = RT2573_BBP_BUSY | ((reg & 0x7f) << 8) | val; + rum_cfg_write(sc, RT2573_PHY_CSR3, tmp); + return; +} + +static uint8_t +rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg) +{ + uint32_t val; + + if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { + return (0); + } + val = RT2573_BBP_BUSY | RT2573_BBP_READ | (reg << 8); + rum_cfg_write(sc, RT2573_PHY_CSR3, val); + + val = rum_cfg_bbp_disbusy(sc); + return (val & 0xff); +} + +static void +rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + uint8_t to; + + reg &= 3; + + for (to = 0;; to++) { + if (to < 100) { + tmp = rum_cfg_read(sc, RT2573_PHY_CSR4); + if (!(tmp & RT2573_RF_BUSY)) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return; + } + } else { + DPRINTF("could not write to RF\n"); + return; + } + } + + tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | ((val & 0xfffff) << 2) | reg; + rum_cfg_write(sc, RT2573_PHY_CSR4, tmp); + + DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); + return; +} + +static void +rum_cfg_first_time_setup(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ieee80211com *ic; + struct ifnet *ifp; + uint32_t tmp; + uint16_t i; + uint8_t bands; + + /* setup RX tap header */ + 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); + + /* setup TX tap header */ + 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); + + /* retrieve RT2573 rev. no */ + for (i = 0; i < 100; i++) { + + tmp = rum_cfg_read(sc, RT2573_MAC_CSR0); + if (tmp != 0) { + break; + } + /* wait a little */ + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + /* device detached */ + goto done; + } + } + + if (tmp == 0) { + DPRINTF("chip is maybe not ready\n"); + } + /* retrieve MAC address and various other things from EEPROM */ + rum_cfg_read_eeprom(sc); + + printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n", + sc->sc_name, tmp, rum_get_rf(sc->sc_rf_rev)); + + rum_cfg_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_IEEE80211); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + DPRINTFN(0, "could not if_alloc()!\n"); + goto done; + } + sc->sc_evilhack = ifp; + sc->sc_ifp = ifp; + ic = ifp->if_l2com; + + ifp->if_softc = sc; + if_initname(ifp, "rum", sc->sc_unit); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = &rum_init_cb; + ifp->if_ioctl = &rum_ioctl_cb; + ifp->if_start = &rum_start_cb; + ifp->if_watchdog = NULL; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); + + 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 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); + ieee80211_init_channels(ic, NULL, &bands); + + if ((sc->sc_rf_rev == RT2573_RF_5225) || + (sc->sc_rf_rev == RT2573_RF_5226)) { + + struct ieee80211_channel *c; + + /* set supported .11a channels */ + for (i = 34; i <= 46; i += 4) { + c = ic->ic_channels + (ic->ic_nchans++); + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; + } + for (i = 36; i <= 64; i += 4) { + c = ic->ic_channels + (ic->ic_nchans++); + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; + } + for (i = 100; i <= 140; i += 4) { + c = ic->ic_channels + (ic->ic_nchans++); + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; + } + for (i = 149; i <= 165; i += 4) { + c = ic->ic_channels + (ic->ic_nchans++); + c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + c->ic_flags = IEEE80211_CHAN_A; + c->ic_ieee = i; + } + } + mtx_unlock(&sc->sc_mtx); + + ieee80211_ifattach(ic); + + mtx_lock(&sc->sc_mtx); + + ic->ic_newassoc = &rum_newassoc; + ic->ic_raw_xmit = &rum_raw_xmit_cb; + ic->ic_node_alloc = &rum_node_alloc; + ic->ic_update_mcast = &rum_update_mcast_cb; + ic->ic_update_promisc = &rum_update_promisc_cb; + ic->ic_scan_start = &rum_scan_start_cb; + ic->ic_scan_end = &rum_scan_end_cb; + ic->ic_set_channel = &rum_set_channel_cb; + ic->ic_vap_create = &rum_vap_create; + ic->ic_vap_delete = &rum_vap_delete; + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + mtx_unlock(&sc->sc_mtx); + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); + + if (bootverbose) { + ieee80211_announce(ic); + } + mtx_lock(&sc->sc_mtx); +done: + return; +} + +static void +rum_end_of_commands(struct rum_softc *sc) +{ + sc->sc_flags &= ~RUM_FLAG_WAIT_COMMAND; + + /* start write transfer, if not started */ + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +rum_config_copy_chan(struct rum_config_copy_chan *cc, + struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (!c) + return; + cc->chan_to_ieee = + ieee80211_chan2ieee(ic, c); + if (c != IEEE80211_CHAN_ANYC) { + cc->chan_to_mode = + ieee80211_chan2mode(c); + if (IEEE80211_IS_CHAN_B(c)) + cc->chan_is_b = 1; + if (IEEE80211_IS_CHAN_A(c)) + cc->chan_is_a = 1; + if (IEEE80211_IS_CHAN_2GHZ(c)) + cc->chan_is_2ghz = 1; + if (IEEE80211_IS_CHAN_5GHZ(c)) + cc->chan_is_5ghz = 1; + if (IEEE80211_IS_CHAN_ANYG(c)) + cc->chan_is_g = 1; + } + return; +} + +static void +rum_config_copy(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + struct ieee80211com *ic; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + const struct ieee80211_txparam *tp; + + bzero(cc, sizeof(*cc)); + + ifp = sc->sc_ifp; + if (ifp) { + cc->if_flags = ifp->if_flags; + bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, + sizeof(cc->if_broadcastaddr)); + + ic = ifp->if_l2com; + if (ic) { + rum_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); + rum_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); + vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap) { + ni = vap->iv_bss; + if (ni) { + cc->iv_bss.ni_intval = ni->ni_intval; + bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, + sizeof(cc->iv_bss.ni_bssid)); + } + tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { + cc->iv_bss.fixed_rate_none = 1; + } + } + cc->ic_opmode = ic->ic_opmode; + cc->ic_flags = ic->ic_flags; + cc->ic_txpowlimit = ic->ic_txpowlimit; + cc->ic_curmode = ic->ic_curmode; + + bcopy(ic->ic_myaddr, cc->ic_myaddr, + sizeof(cc->ic_myaddr)); + } + } + sc->sc_flags |= RUM_FLAG_WAIT_COMMAND; + return; +} + +static const char * +rum_get_rf(uint32_t rev) +{ + ; /* indent fix */ + 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_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct rum_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_node *ni; + + struct mbuf *m = NULL; + uint32_t flags; + uint32_t max_len; + uint8_t rssi = 0; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); + + if (xfer->actlen < (RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { + DPRINTF("too short transfer, " + "%d bytes\n", xfer->actlen); + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, + &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); + + flags = le32toh(sc->sc_rx_desc.flags); + + if (flags & RT2573_RX_CRC_ERROR) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RAL_TXRX_CSR2: + */ + DPRINTFN(6, "PHY or CRC error\n"); + ifp->if_ierrors++; + goto tr_setup; + } + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + ifp->if_ierrors++; + goto tr_setup; + } + max_len = (xfer->actlen - RT2573_RX_DESC_SIZE); + + usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, + m->m_data, max_len); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; + + if (m->m_len > max_len) { + DPRINTF("invalid length in RX " + "descriptor, %u bytes, received %u bytes\n", + m->m_len, max_len); + ifp->if_ierrors++; + m_freem(m); + m = NULL; + goto tr_setup; + } + rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); + + DPRINTF("real length=%d bytes, rssi=%d\n", m->m_len, 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(sc->sc_rx_desc.rate, + (sc->sc_rx_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->sc_rx_ant; + tap->wr_antsignal = rssi; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & RUM_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + + ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + if (ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0)) { + /* ignore */ + } + /* node is no longer needed */ + ieee80211_free_node(ni); + } else { + if (ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0)) { + /* ignore */ + } + } + + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= RUM_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +rum_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct rum_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~RUM_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static uint8_t +rum_plcp_signal(uint16_t rate) +{ + ; /* indent fix */ + switch (rate) { + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + + /* 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); + + /* XXX unsupported/unknown rate */ + default: + return (0xff); + } +} + +/* + * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that + * should be freed, when "rum_setup_desc_and_tx" is called. + */ + +static void +rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, + uint16_t xflags, uint16_t rate) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct mbuf *mm; + enum ieee80211_phytype phytype; + uint16_t plcp_length; + uint16_t len; + uint8_t remainder; + uint8_t is_beacon; + + if (xflags & RT2573_TX_BEACON) { + xflags &= ~RT2573_TX_BEACON; + is_beacon = 1; + } else { + is_beacon = 0; + } + + if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { + /* free packet */ + rum_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (!((sc->sc_flags & RUM_FLAG_LL_READY) && + (sc->sc_flags & RUM_FLAG_HL_READY))) { + /* free packet */ + rum_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (rate < 2) { + DPRINTF("rate < 2!\n"); + + /* avoid division by zero */ + rate = 2; + } + ic->ic_lastdata = ticks; + 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->sc_tx_ant; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); + } + len = m->m_pkthdr.len; + + flags |= RT2573_TX_VALID; + flags |= (len << 16); + + sc->sc_tx_desc.flags = htole32(flags); + sc->sc_tx_desc.xflags = htole16(xflags); + + sc->sc_tx_desc.wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | + RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); + + /* setup PLCP fields */ + sc->sc_tx_desc.plcp_signal = rum_plcp_signal(rate); + sc->sc_tx_desc.plcp_service = 4; + + len += IEEE80211_CRC_LEN; + + phytype = ieee80211_rate2phytype(sc->sc_rates, rate); + + if (phytype == IEEE80211_T_OFDM) { + sc->sc_tx_desc.flags |= htole32(RT2573_TX_OFDM); + + plcp_length = (len & 0xfff); + sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; + sc->sc_tx_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)) { + sc->sc_tx_desc.plcp_service |= + RT2573_PLCP_LENGEXT; + } + } + sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; + sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; + + if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { + sc->sc_tx_desc.plcp_signal |= 0x08; + } + } + + if (sizeof(sc->sc_tx_desc) > MHLEN) { + DPRINTF("No room for header structure!\n"); + rum_tx_freem(m); + return; + } + mm = m_gethdr(M_NOWAIT, MT_DATA); + if (mm == NULL) { + DPRINTF("Could not allocate header mbuf!\n"); + rum_tx_freem(m); + return; + } + bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); + mm->m_len = sizeof(sc->sc_tx_desc); + mm->m_next = m; + mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; + mm->m_pkthdr.rcvif = NULL; + + if (is_beacon) { + + if (mm->m_pkthdr.len > sizeof(sc->sc_beacon_buf)) { + DPRINTFN(0, "Truncating beacon" + ", %u bytes!\n", mm->m_pkthdr.len); + mm->m_pkthdr.len = sizeof(sc->sc_beacon_buf); + } + m_copydata(mm, 0, mm->m_pkthdr.len, sc->sc_beacon_buf); + + /* copy the first 24 bytes of Tx descriptor into NIC memory */ + rum_cfg_write_multi(sc, RT2573_HW_BEACON_BASE0, + sc->sc_beacon_buf, mm->m_pkthdr.len); + rum_tx_freem(mm); + return; + } + /* start write transfer, if not started */ + _IF_ENQUEUE(&sc->sc_tx_queue, mm); + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +rum_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct rum_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint16_t temp_len; + uint8_t align; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + if (sc->sc_flags & RUM_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + break; + } + if (sc->sc_flags & RUM_FLAG_WAIT_COMMAND) { + /* + * don't send anything while a command is pending ! + */ + break; + } + rum_fill_write_queue(sc); + + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (m) { + + if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); + } + usb2_m_copy_in(xfer->frbuffers, 0, + m, 0, m->m_pkthdr.len); + + /* compute transfer length */ + temp_len = m->m_pkthdr.len; + + /* make transfer length 32-bit aligned */ + align = (-(temp_len)) & 3; + + /* check if we need to add four extra bytes */ + if (((temp_len + align) % 64) == 0) { + align += 4; + } + /* check if we need to align length */ + if (align != 0) { + /* zero the extra bytes */ + usb2_bzero(xfer->frbuffers, temp_len, align); + temp_len += align; + } + DPRINTFN(11, "sending frame len=%u ferlen=%u\n", + m->m_pkthdr.len, temp_len); + + xfer->frlengths[0] = temp_len; + usb2_start_hardware(xfer); + + /* free mbuf and node */ + rum_tx_freem(m); + + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= RUM_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + break; + } + return; +} + +static void +rum_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct rum_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~RUM_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +rum_watchdog(void *arg) +{ + struct rum_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (sc->sc_amrr_timer) { + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, + &rum_cfg_amrr_timeout, 0, 0); + } + usb2_callout_reset(&sc->sc_watchdog, + hz, &rum_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +rum_init_cb(void *arg) +{ + struct rum_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_cfg_pre_init, + &rum_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static int +rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct rum_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + int error; + + switch (cmd) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_cfg_pre_init, + &rum_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_cfg_pre_stop, + &rum_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + error = 0; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + } + return (error); +} + +static void +rum_start_cb(struct ifnet *ifp) +{ + struct rum_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + /* start write transfer, if not started */ + usb2_transfer_start(sc->sc_xfer[0]); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +rum_cfg_newstate(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct rum_vap *uvp = RUM_VAP(vap); + enum ieee80211_state ostate; + enum ieee80211_state nstate; + int arg; + + ostate = vap->iv_state; + nstate = sc->sc_ns_state; + arg = sc->sc_ns_arg; + + if (ostate == IEEE80211_S_INIT) { + /* We are leaving INIT. TSF sync should be off. */ + rum_cfg_disable_tsf_sync(sc); + } + switch (nstate) { + case IEEE80211_S_INIT: + break; + + case IEEE80211_S_RUN: + rum_cfg_set_run(sc, cc); + break; + + default: + break; + } + + mtx_unlock(&sc->sc_mtx); + IEEE80211_LOCK(ic); + uvp->newstate(vap, nstate, arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + mtx_lock(&sc->sc_mtx); + return; +} + +static int +rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rum_vap *uvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF("setting new state: %d\n", nstate); + + /* Special case - cannot defer this call and cannot block ! */ + if (nstate == IEEE80211_S_INIT) { + /* stop timers */ + mtx_lock(&sc->sc_mtx); + sc->sc_amrr_timer = 0; + mtx_unlock(&sc->sc_mtx); + return (uvp->newstate(vap, nstate, arg)); + } + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + return (0); /* nothing to do */ + } + /* store next state */ + sc->sc_ns_state = nstate; + sc->sc_ns_arg = arg; + + /* stop timers */ + sc->sc_amrr_timer = 0; + + /* + * USB configuration can only be done from the USB configuration + * thread: + */ + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_config_copy, + &rum_cfg_newstate, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (EINPROGRESS); +} + +static void +rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_config_copy, func, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +rum_scan_start_cb(struct ieee80211com *ic) +{ + rum_std_command(ic, &rum_cfg_scan_start); + return; +} + +static void +rum_scan_end_cb(struct ieee80211com *ic) +{ + rum_std_command(ic, &rum_cfg_scan_end); + return; +} + +static void +rum_set_channel_cb(struct ieee80211com *ic) +{ + rum_std_command(ic, &rum_cfg_set_chan); + return; +} + +static void +rum_cfg_scan_start(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* abort TSF synchronization */ + rum_cfg_disable_tsf_sync(sc); + rum_cfg_set_bssid(sc, cc->if_broadcastaddr); + return; +} + +static void +rum_cfg_scan_end(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* enable TSF synchronization */ + rum_cfg_enable_tsf_sync(sc, cc, 0); + rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + return; +} + +/* + * Reprogram MAC/BBP to switch to a new band. Values taken from the reference + * driver. + */ +static void +rum_cfg_select_band(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; + + /* update all BBP registers that depend on the band */ + bbp17 = 0x20; + bbp96 = 0x48; + bbp104 = 0x2c; + bbp35 = 0x50; + bbp97 = 0x48; + bbp98 = 0x48; + + if (cc->ic_curchan.chan_is_5ghz) { + bbp17 += 0x08; + bbp96 += 0x10; + bbp104 += 0x0c; + bbp35 += 0x10; + bbp97 += 0x10; + bbp98 += 0x10; + } + if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || + (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { + bbp17 += 0x10; + bbp96 += 0x10; + bbp104 += 0x10; + } + sc->sc_bbp17 = bbp17; + rum_cfg_bbp_write(sc, 17, bbp17); + rum_cfg_bbp_write(sc, 96, bbp96); + rum_cfg_bbp_write(sc, 104, bbp104); + + if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || + (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { + rum_cfg_bbp_write(sc, 75, 0x80); + rum_cfg_bbp_write(sc, 86, 0x80); + rum_cfg_bbp_write(sc, 88, 0x80); + } + rum_cfg_bbp_write(sc, 35, bbp35); + rum_cfg_bbp_write(sc, 97, bbp97); + rum_cfg_bbp_write(sc, 98, bbp98); + + tmp = rum_cfg_read(sc, RT2573_PHY_CSR0); + tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); + if (cc->ic_curchan.chan_is_2ghz) + tmp |= RT2573_PA_PE_2GHZ; + else + tmp |= RT2573_PA_PE_5GHZ; + rum_cfg_write(sc, RT2573_PHY_CSR0, tmp); + + /* 802.11a uses a 16 microseconds short interframe space */ + sc->sc_sifs = cc->ic_curchan.chan_is_5ghz ? 16 : 10; + + return; +} + +static void +rum_cfg_set_chan(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + enum { + N_RF5225 = (sizeof(rum_rf5225) / sizeof(rum_rf5225[0]))}; + const struct rfprog *rfprog; + uint32_t chan; + uint16_t i; + uint8_t bbp3; + uint8_t bbp94 = RT2573_BBPR94_DEFAULT; + int8_t power; + + chan = cc->ic_curchan.chan_to_ieee; + + if ((chan == 0) || + (chan == IEEE80211_CHAN_ANY)) { + /* nothing to do */ + return; + } + if (chan == sc->sc_last_chan) { + return; + } + sc->sc_last_chan = chan; + + /* select the appropriate RF settings based on what EEPROM says */ + rfprog = ((sc->sc_rf_rev == RT2573_RF_5225) || + (sc->sc_rf_rev == RT2573_RF_2527)) ? rum_rf5225 : rum_rf5226; + + /* find the settings for this channel */ + for (i = 0;; i++) { + if (i == (N_RF5225 - 1)) + break; + if (rfprog[i].chan == chan) + break; + } + + DPRINTF("chan=%d, i=%d\n", chan, i); + + power = sc->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. + */ + rum_cfg_select_band(sc, cc, 0); + rum_cfg_select_antenna(sc, cc, 0); + + rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); + rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); + + rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7) | 1); + rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); + + rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); + rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); + + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return; + } + /* enable smart mode for MIMO-capable RFs */ + bbp3 = rum_cfg_bbp_read(sc, 3); + + if ((sc->sc_rf_rev == RT2573_RF_5225) || + (sc->sc_rf_rev == RT2573_RF_2527)) + bbp3 &= ~RT2573_SMART_MODE; + else + bbp3 |= RT2573_SMART_MODE; + + rum_cfg_bbp_write(sc, 3, bbp3); + + rum_cfg_bbp_write(sc, 94, bbp94); + + /* update basic rate set */ + + if (cc->ic_curchan.chan_is_b) { + /* 11b basic rates: 1, 2Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); + } else if (cc->ic_curchan.chan_is_a) { + /* 11a basic rates: 6, 12, 24Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); + } else { + /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); + } + + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return; + } + return; +} + +static void +rum_cfg_set_run(struct rum_softc *sc, + struct usb2_config_td_cc *cc) +{ + + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + rum_cfg_update_slot(sc, cc, 0); + rum_cfg_enable_mrr(sc, cc, 0); + rum_cfg_set_txpreamble(sc, cc, 0); + + /* update basic rate set */ + + if (cc->ic_bsschan.chan_is_5ghz) { + /* 11a basic rates: 6, 12, 24Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); + } else if (cc->ic_bsschan.chan_is_g) { + /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); + } else { + /* 11b basic rates: 1, 2Mbps */ + rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); + } + rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + } + if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || + (cc->ic_opmode == IEEE80211_M_IBSS)) { + rum_cfg_prepare_beacon(sc, cc, 0); + } + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + rum_cfg_enable_tsf_sync(sc, cc, 0); + } + if (cc->iv_bss.fixed_rate_none) { + /* enable automatic rate adaptation */ + rum_cfg_amrr_start(sc); + } + return; +} + +static void +rum_cfg_enable_tsf_sync(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + + if (cc->ic_opmode != IEEE80211_M_STA) { + /* + * Change default 16ms TBTT adjustment to 8ms. + * Must be done before enabling beacon generation. + */ + rum_cfg_write(sc, RT2573_TXRX_CSR10, (1 << 12) | 8); + } + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9) & 0xff000000; + + /* set beacon interval (in 1/16ms unit) */ + tmp |= cc->iv_bss.ni_intval * 16; + + tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; + if (cc->ic_opmode == IEEE80211_M_STA) + tmp |= RT2573_TSF_MODE(1); + else + tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; + + rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp); + + return; +} + +static void +rum_cfg_disable_tsf_sync(struct rum_softc *sc) +{ + uint32_t tmp; + + /* abort TSF synchronization */ + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9); + rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); + return; +} + +/* + * Enable multi-rate retries for frames sent at OFDM rates. + * In 802.11b/g mode, allow fallback to CCK rates. + */ +static void +rum_cfg_enable_mrr(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); + + if (cc->ic_curchan.chan_is_5ghz) + tmp &= ~RT2573_MRR_CCK_FALLBACK; + else + tmp |= RT2573_MRR_CCK_FALLBACK; + + tmp |= RT2573_MRR_ENABLED; + + rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); + + return; +} + +static void +rum_cfg_update_slot(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + uint8_t slottime; + + slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + + tmp = rum_cfg_read(sc, RT2573_MAC_CSR9); + tmp = (tmp & ~0xff) | slottime; + rum_cfg_write(sc, RT2573_MAC_CSR9, tmp); + + DPRINTF("setting slot time to %u us\n", slottime); + + return; +} + +static void +rum_cfg_set_txpreamble(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); + + if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2573_SHORT_PREAMBLE; + else + tmp &= ~RT2573_SHORT_PREAMBLE; + + rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); + + return; +} + +static void +rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid) +{ + uint32_t tmp; + + tmp = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24); + rum_cfg_write(sc, RT2573_MAC_CSR4, tmp); + + tmp = (bssid[4]) | (bssid[5] << 8) | (RT2573_ONE_BSSID << 16); + rum_cfg_write(sc, RT2573_MAC_CSR5, tmp); + + return; +} + +static void +rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr) +{ + uint32_t tmp; + + tmp = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); + rum_cfg_write(sc, RT2573_MAC_CSR2, tmp); + + tmp = addr[4] | (addr[5] << 8) | (0xff << 16); + rum_cfg_write(sc, RT2573_MAC_CSR3, tmp); + + return; +} + +static void +rum_cfg_update_promisc(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); + + if (cc->if_flags & IFF_PROMISC) + tmp &= ~RT2573_DROP_NOT_TO_ME; + else + tmp |= RT2573_DROP_NOT_TO_ME; + + rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); + + DPRINTF("%s promiscuous mode\n", + (cc->if_flags & IFF_PROMISC) ? + "entering" : "leaving"); + return; +} + +static void +rum_cfg_select_antenna(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + uint8_t bbp3; + uint8_t bbp4; + uint8_t bbp77; + uint8_t rx_ant; + uint8_t is_5ghz; + + bbp3 = rum_cfg_bbp_read(sc, 3); + bbp4 = rum_cfg_bbp_read(sc, 4); + bbp77 = rum_cfg_bbp_read(sc, 77); + + bbp3 &= ~0x01; + bbp4 &= ~0x23; + + rx_ant = sc->sc_rx_ant; + is_5ghz = cc->ic_curchan.chan_is_5ghz; + + switch (sc->sc_rf_rev) { + case RT2573_RF_5226: + case RT2573_RF_5225: + if (rx_ant == 0) { + /* Diversity */ + bbp4 |= 0x02; + if (is_5ghz == 0) + bbp4 |= 0x20; + } else if (rx_ant == 1) { + /* RX: Antenna A */ + bbp4 |= 0x01; + if (is_5ghz) + bbp77 &= ~0x03; + else + bbp77 |= 0x03; + } else if (rx_ant == 2) { + /* RX: Antenna B */ + bbp4 |= 0x01; + if (is_5ghz) + bbp77 |= 0x03; + else + bbp77 &= ~0x03; + } + break; + + case RT2573_RF_2528: + case RT2573_RF_2527: + if (rx_ant == 0) { + /* Diversity */ + bbp4 |= 0x22; + } else if (rx_ant == 1) { + /* RX: Antenna A */ + bbp4 |= 0x21; + bbp77 |= 0x03; + } else if (rx_ant == 2) { + /* RX: Antenna B */ + bbp4 |= 0x21; + bbp77 &= ~0x03; + } + break; + default: + break; + } + bbp4 &= ~(sc->sc_ftype << 5); + + /* make sure Rx is disabled before switching antenna */ + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); + rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + rum_cfg_bbp_write(sc, 3, bbp3); + rum_cfg_bbp_write(sc, 4, bbp4); + rum_cfg_bbp_write(sc, 77, bbp77); + + rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); + + return; +} + +static void +rum_cfg_read_eeprom(struct rum_softc *sc) +{ + uint16_t val; + + /* read MAC address */ + rum_cfg_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_myaddr, 6); + + val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_ANTENNA); + sc->sc_rf_rev = (val >> 11) & 0x1f; + sc->sc_hw_radio = (val >> 10) & 0x1; + sc->sc_ftype = (val >> 6) & 0x1; + sc->sc_rx_ant = (val >> 4) & 0x3; + sc->sc_tx_ant = (val >> 2) & 0x3; + sc->sc_nb_ant = (val & 0x3); + + DPRINTF("RF revision=%d\n", sc->sc_rf_rev); + + val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_CONFIG2); + sc->sc_ext_5ghz_lna = (val >> 6) & 0x1; + sc->sc_ext_2ghz_lna = (val >> 4) & 0x1; + + DPRINTF("External 2GHz LNA=%d, External 5GHz LNA=%d\n", + sc->sc_ext_2ghz_lna, sc->sc_ext_5ghz_lna); + + val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET); + if ((val & 0xff) != 0xff) + sc->sc_rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + else + sc->sc_rssi_2ghz_corr = 0; + + /* range check */ + if ((sc->sc_rssi_2ghz_corr < -10) || + (sc->sc_rssi_2ghz_corr > 10)) { + sc->sc_rssi_2ghz_corr = 0; + } + val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET); + if ((val & 0xff) != 0xff) + sc->sc_rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + else + sc->sc_rssi_5ghz_corr = 0; + + /* range check */ + if ((sc->sc_rssi_5ghz_corr < -10) || + (sc->sc_rssi_5ghz_corr > 10)) { + sc->sc_rssi_5ghz_corr = 0; + } + if (sc->sc_ext_2ghz_lna) { + sc->sc_rssi_2ghz_corr -= 14; + } + if (sc->sc_ext_5ghz_lna) { + sc->sc_rssi_5ghz_corr -= 14; + } + DPRINTF("RSSI 2GHz corr=%d, RSSI 5GHz corr=%d\n", + sc->sc_rssi_2ghz_corr, sc->sc_rssi_5ghz_corr); + + val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_FREQ_OFFSET); + if ((val & 0xff) != 0xff) + sc->sc_rffreq = (val & 0xff); + else + sc->sc_rffreq = 0; + + DPRINTF("RF freq=%d\n", sc->sc_rffreq); + + /* read Tx power for all a/b/g channels */ + rum_cfg_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->sc_txpow, 14); + + /* XXX default Tx power for 802.11a channels */ + memset(sc->sc_txpow + 14, 24, sizeof(sc->sc_txpow) - 14); + + /* read default values for BBP registers */ + rum_cfg_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->sc_bbp_prom, 2 * 16); + + return; +} + +static uint8_t +rum_cfg_bbp_init(struct rum_softc *sc) +{ + enum { + N_DEF_BBP = (sizeof(rum_def_bbp) / sizeof(rum_def_bbp[0])), + }; + uint16_t i; + uint8_t to; + uint8_t tmp; + + /* wait for BBP to become ready */ + for (to = 0;; to++) { + if (to < 100) { + tmp = rum_cfg_bbp_read(sc, 0); + if ((tmp != 0x00) && + (tmp != 0xff)) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return (1); /* failure */ + } + } else { + DPRINTF("timeout waiting for BBP\n"); + return (1); /* failure */ + } + } + + /* initialize BBP registers to default values */ + for (i = 0; i < N_DEF_BBP; i++) { + rum_cfg_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->sc_bbp_prom[i].reg == 0) || + (sc->sc_bbp_prom[i].reg == 0xff)) { + continue; + } + rum_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); + } + return (0); +} + +static void +rum_cfg_pre_init(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* immediate configuration */ + + rum_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= RUM_FLAG_HL_READY; + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + return; +} + +static void +rum_cfg_init(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + enum { + N_DEF_MAC = (sizeof(rum_def_mac) / sizeof(rum_def_mac[0])), + }; + + uint32_t tmp; + uint16_t i; + uint8_t to; + + /* delayed configuration */ + + rum_cfg_stop(sc, cc, 0); + + /* initialize MAC registers to default values */ + for (i = 0; i < N_DEF_MAC; i++) { + rum_cfg_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); + } + + /* set host ready */ + rum_cfg_write(sc, RT2573_MAC_CSR1, 3); + rum_cfg_write(sc, RT2573_MAC_CSR1, 0); + + /* wait for BBP/RF to wakeup */ + for (to = 0;; to++) { + if (to < 100) { + if (rum_cfg_read(sc, RT2573_MAC_CSR12) & 8) { + break; + } + rum_cfg_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ + + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + goto fail; + } + } else { + DPRINTF("timeout waiting for " + "BBP/RF to wakeup\n"); + goto fail; + } + } + + if (rum_cfg_bbp_init(sc)) { + goto fail; + } + /* select default channel */ + + sc->sc_last_chan = 0; + + rum_cfg_set_chan(sc, cc, 0); + + /* clear STA registers */ + rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); + /* set MAC address */ + rum_cfg_set_macaddr(sc, cc->ic_myaddr); + + /* initialize ASIC */ + rum_cfg_write(sc, RT2573_MAC_CSR1, 4); + + /* + * make sure that the first transaction + * clears the stall: + */ + sc->sc_flags |= (RUM_FLAG_READ_STALL | + RUM_FLAG_WRITE_STALL | + RUM_FLAG_LL_READY); + + if ((sc->sc_flags & RUM_FLAG_LL_READY) && + (sc->sc_flags & RUM_FLAG_HL_READY)) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + + /* + * start IEEE802.11 layer + */ + mtx_unlock(&sc->sc_mtx); + ieee80211_start_all(ic); + mtx_lock(&sc->sc_mtx); + } + /* update Rx filter */ + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0) & 0xffff; + + tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; + + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | + RT2573_DROP_ACKCTS; + if (cc->ic_opmode != IEEE80211_M_HOSTAP) { + tmp |= RT2573_DROP_TODS; + } + if (!(cc->if_flags & IFF_PROMISC)) { + tmp |= RT2573_DROP_NOT_TO_ME; + } + } + rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); + + return; + +fail: + rum_cfg_pre_stop(sc, NULL, 0); + + if (cc) { + rum_cfg_stop(sc, cc, 0); + } + return; +} + +static void +rum_cfg_pre_stop(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + rum_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(RUM_FLAG_HL_READY | + RUM_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + + /* clean up transmission */ + rum_tx_clean_queue(sc); + return; +} + +static void +rum_cfg_stop(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t tmp; + + /* disable Rx */ + tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); + rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + /* reset ASIC */ + rum_cfg_write(sc, RT2573_MAC_CSR1, 3); + + /* wait a little */ + usb2_config_td_sleep(&sc->sc_config_td, hz / 10); + + rum_cfg_write(sc, RT2573_MAC_CSR1, 0); + + /* wait a little */ + usb2_config_td_sleep(&sc->sc_config_td, hz / 10); + + return; +} + +static void +rum_cfg_amrr_start(struct rum_softc *sc) +{ + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + vap = rum_get_vap(sc); + + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + /* init AMRR */ + + ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); + + /* enable AMRR timer */ + + sc->sc_amrr_timer = 1; + return; +} + +static void +rum_cfg_amrr_timeout(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + uint32_t ok; + uint32_t fail; + + /* clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); + + vap = rum_get_vap(sc); + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + if ((sc->sc_flags & RUM_FLAG_LL_READY) && + (sc->sc_flags & RUM_FLAG_HL_READY)) { + + ok = (le32toh(sc->sc_sta[4]) >> 16) + /* TX ok w/o retry */ + (le32toh(sc->sc_sta[5]) & 0xffff); /* TX ok w/ retry */ + fail = (le32toh(sc->sc_sta[5]) >> 16); /* TX retry-fail count */ + + if (sc->sc_amrr_timer) { + ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn, + ok + fail, ok, (le32toh(sc->sc_sta[5]) & 0xffff) + fail); + + if (ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn)) { + /* ignore */ + } + } + ifp->if_oerrors += fail;/* count TX retry-fail as Tx errors */ + } + return; +} + +static void +rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size) +{ + struct usb2_device_request req; + uint16_t reg = RT2573_MCU_CODE_BASE; + + /* copy firmware image into NIC */ + while (size >= 4) { + rum_cfg_write(sc, reg, UGETDW(ucode)); + reg += 4; + ucode += 4; + size -= 4; + } + + if (size != 0) { + DPRINTF("possibly invalid firmware\n"); + } + 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); + + rum_cfg_do_request(sc, &req, NULL); + + return; +} + +static void +rum_cfg_prepare_beacon(struct rum_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ieee80211com *ic; + const struct ieee80211_txparam *tp; + struct mbuf *m; + + vap = rum_get_vap(sc); + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + ic = vap->iv_ic; + if (ic == NULL) { + return; + } + DPRINTFN(11, "Sending beacon frame.\n"); + + m = ieee80211_beacon_alloc(ni, &RUM_VAP(vap)->bo); + if (m == NULL) { + DPRINTFN(0, "could not allocate beacon\n"); + return; + } + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + + m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); + rum_setup_desc_and_tx(sc, m, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ | RT2573_TX_BEACON, tp->mgmtrate); + return; +} + +static uint8_t +rum_get_rssi(struct rum_softc *sc, uint8_t raw) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + int16_t rssi; + uint8_t lna; + uint8_t agc; + + 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 (0); + } + rssi = (2 * agc) - RT2573_NOISE_FLOOR; + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + + rssi += sc->sc_rssi_2ghz_corr; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 74; + else if (lna == 3) + rssi -= 90; + } else { + + rssi += sc->sc_rssi_5ghz_corr; + + if ((!sc->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; + } + + /* range check */ + + if (rssi < 0) + rssi = 0; + else if (rssi > 255) + rssi = 255; + + return (rssi); +} + +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; + struct rum_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF("\n"); + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + /* config thread is gone */ + return (NULL); + } + mtx_unlock(&sc->sc_mtx); + + 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_cb; + + 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); + + /* store current operation mode */ + ic->ic_opmode = opmode; + + return (vap); +} + +static void +rum_vap_delete(struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + DPRINTF("\n"); + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + /* ignore */ + } + mtx_unlock(&sc->sc_mtx); + + ieee80211_amrr_cleanup(&rvp->amrr); + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); + return; +} + +/* 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); + return; +} + +static void +rum_fill_write_queue(struct rum_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + + /* + * We only fill up half of the queue with data frames. The rest is + * reserved for other kinds of frames. + */ + + while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + ni = (void *)(m->m_pkthdr.rcvif); + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + continue; + } + rum_tx_data(sc, m, ni); + } + return; +} + +static void +rum_tx_clean_queue(struct rum_softc *sc) +{ + struct mbuf *m; + + for (;;) { + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (!m) { + break; + } + rum_tx_freem(m); + } + return; +} + +static void +rum_tx_freem(struct mbuf *m) +{ + struct ieee80211_node *ni; + + while (m) { + ni = (void *)(m->m_pkthdr.rcvif); + if (!ni) { + m = m_free(m); + continue; + } + if (m->m_flags & M_TXCB) { + ieee80211_process_callback(ni, m, 0); + } + m_freem(m); + ieee80211_free_node(ni); + + break; + } + return; +} + +static void +rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags; + uint16_t dur; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + wh = mtod(m, struct ieee80211_frame *); + } + flags = 0; + 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); + USETW(wh->i_dur, 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; + } + m->m_pkthdr.rcvif = (void *)ni; + rum_setup_desc_and_tx(sc, m, flags, 0, tp->mgmtrate); + return; +} + +static struct ieee80211vap * +rum_get_vap(struct rum_softc *sc) +{ + struct ifnet *ifp; + struct ieee80211com *ic; + + if (sc == NULL) { + return NULL; + } + ifp = sc->sc_ifp; + if (ifp == NULL) { + return NULL; + } + ic = ifp->if_l2com; + if (ic == NULL) { + return NULL; + } + return TAILQ_FIRST(&ic->ic_vaps); +} + +static void +rum_tx_data(struct rum_softc *sc, struct mbuf *m, + struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + uint16_t rate; + + DPRINTFN(11, "Sending data.\n"); + + wh = mtod(m, 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, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m, struct ieee80211_frame *); + } + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + uint8_t prot = IEEE80211_PROT_NONE; + + if (m->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) { + rum_tx_prot(sc, m, ni, prot, rate); + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + flags |= RT2573_TX_NEED_ACK; + flags |= RT2573_TX_MORE_FRAG; + + dur = ieee80211_ack_duration(sc->sc_rates, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + m->m_pkthdr.rcvif = (void *)ni; + rum_setup_desc_and_tx(sc, m, flags, 0, rate); + return; +} + +static void +rum_tx_prot(struct rum_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, + uint8_t prot, uint16_t rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct mbuf *mprot; + uint32_t flags; + uint16_t protrate; + uint16_t ackrate; + uint16_t pktlen; + uint16_t dur; + uint8_t isshort; + + KASSERT((prot == IEEE80211_PROT_RTSCTS) || + (prot == IEEE80211_PROT_CTSONLY), + ("protection %u", prot)); + + DPRINTFN(11, "Sending protection frame.\n"); + + 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) { + return; + } + mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); + rum_setup_desc_and_tx(sc, mprot, flags, 0, protrate); + return; +} + +static void +rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + uint32_t flags; + uint16_t rate; + + DPRINTFN(11, "Sending raw frame.\n"); + + rate = params->ibp_rate0 & IEEE80211_RATE_VAL; + + /* XXX validate */ + if (rate == 0) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + 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)) { + rum_tx_prot(sc, m, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + m->m_pkthdr.rcvif = (void *)ni; + rum_setup_desc_and_tx(sc, m, flags, 0, rate); + return; +} + +static int +rum_raw_xmit_cb(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 rum_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + rum_tx_mgt(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + rum_tx_raw(sc, m, ni, params); + } + mtx_unlock(&sc->sc_mtx); + return (0); +} + +static void +rum_update_mcast_cb(struct ifnet *ifp) +{ + /* not supported */ + return; +} + +static void +rum_update_promisc_cb(struct ifnet *ifp) +{ + struct rum_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &rum_config_copy, + &rum_cfg_update_promisc, 0, 0); + mtx_unlock(&sc->sc_mtx); + return; +} diff --git a/sys/dev/usb2/wlan/if_rum2_fw.h b/sys/dev/usb2/wlan/if_rum2_fw.h new file mode 100644 index 000000000000..0f0867445131 --- /dev/null +++ b/sys/dev/usb2/wlan/if_rum2_fw.h @@ -0,0 +1,213 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005-2006, Ralink Technology, Corp. + * Paul Lin + * + * 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/dev/usb2/wlan/if_rum2_reg.h b/sys/dev/usb2/wlan/if_rum2_reg.h new file mode 100644 index 000000000000..cc88ef8eadbe --- /dev/null +++ b/sys/dev/usb2/wlan/if_rum2_reg.h @@ -0,0 +1,235 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * + * 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) +#define RT2573_TX_BEACON (1 << 15) /* Internal flag only! */ + + 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/dev/usb2/wlan/if_rum2_var.h b/sys/dev/usb2/wlan/if_rum2_var.h new file mode 100644 index 000000000000..8551a4115162 --- /dev/null +++ b/sys/dev/usb2/wlan/if_rum2_var.h @@ -0,0 +1,172 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * + * 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_N_TRANSFER 4 + +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; + + int (*newstate) (struct ieee80211vap *, + enum ieee80211_state, int); +}; + +#define RUM_VAP(vap) ((struct rum_vap *)(vap)) + +struct rum_config_copy_chan { + uint32_t chan_to_ieee; + enum ieee80211_phymode chan_to_mode; + uint8_t chan_is_5ghz:1; + uint8_t chan_is_2ghz:1; + uint8_t chan_is_b:1; + uint8_t chan_is_a:1; + uint8_t chan_is_g:1; + uint8_t unused:3; +}; + +struct rum_config_copy_bss { + uint16_t ni_intval; + uint8_t ni_bssid[IEEE80211_ADDR_LEN]; + uint8_t fixed_rate_none; +}; + +struct rum_config_copy { + struct rum_config_copy_chan ic_curchan; + struct rum_config_copy_chan ic_bsschan; + struct rum_config_copy_bss iv_bss; + + enum ieee80211_opmode ic_opmode; + uint32_t ic_flags; + uint32_t if_flags; + + uint16_t ic_txpowlimit; + uint16_t ic_curmode; + + uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; + uint8_t if_broadcastaddr[IEEE80211_ADDR_LEN]; +}; + +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_bbp_prom { + uint8_t val; + uint8_t reg; +} __packed; + +struct rum_ifq { + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + uint16_t ifq_len; +}; + +struct rum_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct rum_ifq sc_tx_queue; + struct usb2_config_td sc_config_td; + struct rum_tx_desc sc_tx_desc; + struct rum_rx_desc sc_rx_desc; + struct mtx sc_mtx; + struct usb2_callout sc_watchdog; + struct rum_bbp_prom sc_bbp_prom[16]; + struct rum_rx_radiotap_header sc_rxtap; + struct rum_tx_radiotap_header sc_txtap; + + struct usb2_xfer *sc_xfer[RUM_N_TRANSFER]; + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + const struct ieee80211_rate_table *sc_rates; + + int (*sc_newstate) + (struct ieee80211com *, enum ieee80211_state, int); + + enum ieee80211_state sc_ns_state; + uint32_t sc_sta[6]; + uint32_t sc_unit; + int sc_ns_arg; + + uint16_t sc_flags; +#define RUM_FLAG_READ_STALL 0x0001 +#define RUM_FLAG_WRITE_STALL 0x0002 +#define RUM_FLAG_LL_READY 0x0008 +#define RUM_FLAG_HL_READY 0x0010 +#define RUM_FLAG_WAIT_COMMAND 0x0020 + uint16_t sc_txtap_len; + uint16_t sc_rxtap_len; + uint16_t sc_last_chan; + + uint8_t sc_txpow[44]; + uint8_t sc_rf_rev; + uint8_t sc_rffreq; + uint8_t sc_ftype; + uint8_t sc_rx_ant; + uint8_t sc_tx_ant; + uint8_t sc_nb_ant; + uint8_t sc_ext_2ghz_lna; + uint8_t sc_ext_5ghz_lna; + uint8_t sc_sifs; + uint8_t sc_bbp17; + uint8_t sc_hw_radio; + uint8_t sc_amrr_timer; + uint8_t sc_beacon_buf[0x800]; + uint8_t sc_myaddr[IEEE80211_ADDR_LEN]; + + int8_t sc_rssi_2ghz_corr; + int8_t sc_rssi_5ghz_corr; + + char sc_name[32]; +}; diff --git a/sys/dev/usb2/wlan/if_ural2.c b/sys/dev/usb2/wlan/if_ural2.c new file mode 100644 index 000000000000..3c2e12331ad1 --- /dev/null +++ b/sys/dev/usb2/wlan/if_ural2.c @@ -0,0 +1,2788 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * Damien Bergamini + * + * Copyright (c) 2006, 2008 + * Hans Petter Selasky + * + * 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. + * + */ + +/* + * + * NOTE: all function names beginning like "ural_cfg_" can only + * be called from within the config thread function ! + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2500USB chipset driver + * http://www.ralinktech.com/ + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc ural_config_copy +#define usb2_config_td_softc ural_softc + +#define USB_DEBUG_VAR ural_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if USB_DEBUG +static int ural_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); +SYSCTL_INT(_hw_usb2_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, + "Debug level"); +#endif + +#define URAL_RSSI(rssi) \ + ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ + ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) + +/* prototypes */ + +static device_probe_t ural_probe; +static device_attach_t ural_attach; +static device_detach_t ural_detach; + +static usb2_callback_t ural_bulk_read_callback; +static usb2_callback_t ural_bulk_read_clear_stall_callback; +static usb2_callback_t ural_bulk_write_callback; +static usb2_callback_t ural_bulk_write_clear_stall_callback; + +static usb2_config_td_command_t ural_cfg_first_time_setup; +static usb2_config_td_command_t ural_config_copy; +static usb2_config_td_command_t ural_cfg_scan_start; +static usb2_config_td_command_t ural_cfg_scan_end; +static usb2_config_td_command_t ural_cfg_set_chan; +static usb2_config_td_command_t ural_cfg_enable_tsf_sync; +static usb2_config_td_command_t ural_cfg_update_slot; +static usb2_config_td_command_t ural_cfg_set_txpreamble; +static usb2_config_td_command_t ural_cfg_update_promisc; +static usb2_config_td_command_t ural_cfg_pre_init; +static usb2_config_td_command_t ural_cfg_init; +static usb2_config_td_command_t ural_cfg_pre_stop; +static usb2_config_td_command_t ural_cfg_stop; +static usb2_config_td_command_t ural_cfg_amrr_timeout; +static usb2_config_td_command_t ural_cfg_newstate; + +static void ural_cfg_do_request(struct ural_softc *sc, struct usb2_device_request *req, void *data); +static void ural_cfg_set_testmode(struct ural_softc *sc); +static void ural_cfg_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, uint16_t len); +static uint16_t ural_cfg_read(struct ural_softc *sc, uint16_t reg); +static void ural_cfg_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len); +static void ural_cfg_write(struct ural_softc *sc, uint16_t reg, uint16_t val); +static void ural_cfg_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len); +static void ural_cfg_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val); +static uint8_t ural_cfg_bbp_read(struct ural_softc *sc, uint8_t reg); +static void ural_cfg_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val); +static void ural_end_of_commands(struct ural_softc *sc); +static const char *ural_get_rf(int rev); +static void ural_watchdog(void *arg); +static void ural_init_cb(void *arg); +static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); +static void ural_start_cb(struct ifnet *ifp); +static int ural_newstate_cb(struct ieee80211vap *ic, enum ieee80211_state nstate, int arg); +static void ural_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func); +static void ural_scan_start_cb(struct ieee80211com *); +static void ural_scan_end_cb(struct ieee80211com *); +static void ural_set_channel_cb(struct ieee80211com *); +static void ural_cfg_disable_rf_tune(struct ural_softc *sc); +static void ural_cfg_set_bssid(struct ural_softc *sc, uint8_t *bssid); +static void ural_cfg_set_macaddr(struct ural_softc *sc, uint8_t *addr); +static void ural_cfg_set_txantenna(struct ural_softc *sc, uint8_t antenna); +static void ural_cfg_set_rxantenna(struct ural_softc *sc, uint8_t antenna); +static void ural_cfg_read_eeprom(struct ural_softc *sc); +static uint8_t ural_cfg_bbp_init(struct ural_softc *sc); +static void ural_cfg_amrr_start(struct ural_softc *sc); +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]); +static void ural_vap_delete(struct ieee80211vap *); +static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); +static void ural_newassoc(struct ieee80211_node *, int); +static void ural_cfg_disable_tsf_sync(struct ural_softc *sc); +static void ural_cfg_set_run(struct ural_softc *sc, struct usb2_config_td_cc *cc); +static void ural_fill_write_queue(struct ural_softc *sc); +static void ural_tx_clean_queue(struct ural_softc *sc); +static void ural_tx_freem(struct mbuf *m); +static void ural_tx_mgt(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static struct ieee80211vap *ural_get_vap(struct ural_softc *sc); +static void ural_tx_bcn(struct ural_softc *sc); +static void ural_tx_data(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static void ural_tx_prot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate); +static void ural_tx_raw(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params); +static int ural_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); +static void ural_setup_desc_and_tx(struct ural_softc *sc, struct mbuf *m, uint32_t flags, uint16_t rate); +static void ural_update_mcast_cb(struct ifnet *ifp); +static void ural_update_promisc_cb(struct ifnet *ifp); + +/* various supported device vendors/products */ +static const struct usb2_device_id ural_devs[] = { + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G, 0)}, + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050, 0)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G, 0)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP, 0)}, + {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU, 0)}, + {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122, 0)}, + {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G, 0)}, + {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG, 0)}, + {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB, 0)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2, 0)}, + {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3, 0)}, + {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3, 0)}, + {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, + {USB_VPI(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G, 0)}, + {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG, 0)}, + {USB_VPI(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R, 0)}, + {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570, 0)}, + {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570, 0)}, + {USB_VPI(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570, 0)}, +}; + +/* + * Default values for MAC registers; values taken from + * the reference driver: + */ +struct ural_def_mac { + uint16_t reg; + uint16_t val; +}; + +static const struct ural_def_mac 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, RAL_FRAME_SIZE}, + {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. + */ +struct ural_def_bbp { + uint8_t reg; + uint8_t val; +}; + +static const struct ural_def_bbp 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. + */ +struct ural_rf5222 { + uint8_t chan; + uint32_t r1; + uint32_t r2; + uint32_t r4; +}; + +static const struct ural_rf5222 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 const struct usb2_config ural_config[URAL_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &ural_bulk_write_callback, + .mh.timeout = 5000, /* ms */ + }, + + [1] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &ural_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ural_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ural_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t ural_devclass; + +static device_method_t ural_methods[] = { + DEVMETHOD(device_probe, ural_probe), + DEVMETHOD(device_attach, ural_attach), + DEVMETHOD(device_detach, ural_detach), + {0, 0} +}; + +static driver_t ural_driver = { + .name = "ural", + .methods = ural_methods, + .size = sizeof(struct ural_softc), +}; + +DRIVER_MODULE(ural, ushub, ural_driver, ural_devclass, NULL, 0); +MODULE_DEPEND(ural, usb2_wlan, 1, 1, 1); +MODULE_DEPEND(ural, usb2_core, 1, 1, 1); +MODULE_DEPEND(ural, wlan, 1, 1, 1); +MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); + +static int +ural_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != 0) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); +} + +static int +ural_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ural_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, "ural lock", MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + sc->sc_udev = uaa->device; + sc->sc_unit = device_get_unit(dev); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + iface_index = RAL_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, ural_config, + URAL_N_TRANSFER, sc, &sc->sc_mtx); + + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + &ural_end_of_commands, + sizeof(struct usb2_config_td_cc), 24); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &ural_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + ural_watchdog(sc); + + return (0); /* success */ + +detach: + ural_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ural_detach(device_t dev) +{ + struct ural_softc *sc = device_get_softc(dev); + struct ieee80211com *ic; + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + ural_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + ic = ifp->if_l2com; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +/*========================================================================* + * REGISTER READ / WRITE WRAPPER ROUTINES + *========================================================================*/ + +static void +ural_cfg_do_request(struct ural_softc *sc, struct usb2_device_request *req, + void *data) +{ + uint16_t length; + usb2_error_t err; + +repeat: + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTF("device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + + /* wait a little before next try */ + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { + goto error; + } + /* try until we are detached */ + goto repeat; + +error: + /* the device has been detached */ + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +ural_cfg_set_testmode(struct ural_softc *sc) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_VENDOR_REQUEST; + USETW(req.wValue, 4); + USETW(req.wIndex, 1); + USETW(req.wLength, 0); + + ural_cfg_do_request(sc, &req, NULL); + return; +} + +static void +ural_cfg_eeprom_read(struct ural_softc *sc, uint16_t addr, + void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + ural_cfg_do_request(sc, &req, buf); + return; +} + +static uint16_t +ural_cfg_read(struct ural_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + 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(val)); + + ural_cfg_do_request(sc, &req, &val); + + return (le16toh(val)); +} + +static void +ural_cfg_read_multi(struct ural_softc *sc, uint16_t reg, + void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + ural_cfg_do_request(sc, &req, buf); + return; +} + +static void +ural_cfg_write(struct ural_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MAC; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + ural_cfg_do_request(sc, &req, NULL); + return; +} + +static void +ural_cfg_write_multi(struct ural_softc *sc, uint16_t reg, + void *buf, uint16_t len) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + ural_cfg_do_request(sc, &req, buf); + return; +} + +static uint8_t +ural_cfg_bbp_disbusy(struct ural_softc *sc) +{ + uint16_t tmp; + uint8_t to; + + for (to = 0;; to++) { + if (to < 100) { + tmp = ural_cfg_read(sc, RAL_PHY_CSR8); + tmp &= RAL_BBP_BUSY; + + if (tmp == 0) { + return (0); + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + break; + } + } else { + break; + } + } + DPRINTF("could not disbusy BBP\n"); + return (1); /* failure */ +} + +static void +ural_cfg_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) +{ + uint16_t tmp; + + if (ural_cfg_bbp_disbusy(sc)) { + return; + } + tmp = (reg << 8) | val; + ural_cfg_write(sc, RAL_PHY_CSR7, tmp); + return; +} + +static uint8_t +ural_cfg_bbp_read(struct ural_softc *sc, uint8_t reg) +{ + uint16_t val; + + if (ural_cfg_bbp_disbusy(sc)) { + return (0); + } + val = RAL_BBP_WRITE | (reg << 8); + ural_cfg_write(sc, RAL_PHY_CSR7, val); + + if (ural_cfg_bbp_disbusy(sc)) { + return (0); + } + return (ural_cfg_read(sc, RAL_PHY_CSR7) & 0xff); +} + +static void +ural_cfg_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + uint8_t to; + + reg &= 3; + + /* remember last written value */ + sc->sc_rf_regs[reg] = val; + + for (to = 0;; to++) { + if (to < 100) { + tmp = ural_cfg_read(sc, RAL_PHY_CSR10); + + if (!(tmp & RAL_RF_LOBUSY)) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return; + } + } else { + DPRINTF("could not write to RF\n"); + return; + } + } + + tmp = RAL_RF_BUSY | RAL_RF_20BIT | ((val & 0xfffff) << 2) | reg; + ural_cfg_write(sc, RAL_PHY_CSR9, tmp & 0xffff); + ural_cfg_write(sc, RAL_PHY_CSR10, tmp >> 16); + + DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); + return; +} + +static void +ural_cfg_first_time_setup(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ieee80211com *ic; + struct ifnet *ifp; + uint8_t bands; + + /* setup RX tap header */ + 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); + + /* setup TX tap header */ + 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); + + /* retrieve RT2570 rev. no */ + sc->sc_asic_rev = ural_cfg_read(sc, RAL_MAC_CSR0); + + /* retrieve MAC address and various other things from EEPROM */ + ural_cfg_read_eeprom(sc); + + printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", + sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev)); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_IEEE80211); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + DPRINTFN(0, "could not if_alloc()!\n"); + goto done; + } + sc->sc_evilhack = ifp; + sc->sc_ifp = ifp; + ic = ifp->if_l2com; + + ifp->if_softc = sc; + if_initname(ifp, "ural", sc->sc_unit); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = &ural_init_cb; + ifp->if_ioctl = &ural_ioctl_cb; + ifp->if_start = &ural_start_cb; + ifp->if_watchdog = NULL; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); + + 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 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->sc_rf_rev == RAL_RF_5222) { + setbit(&bands, IEEE80211_MODE_11A); + } + ieee80211_init_channels(ic, NULL, &bands); + + mtx_unlock(&sc->sc_mtx); + + ieee80211_ifattach(ic); + + mtx_lock(&sc->sc_mtx); + + ic->ic_newassoc = &ural_newassoc; + ic->ic_raw_xmit = &ural_raw_xmit_cb; + ic->ic_node_alloc = &ural_node_alloc; + ic->ic_update_mcast = &ural_update_mcast_cb; + ic->ic_update_promisc = &ural_update_promisc_cb; + ic->ic_scan_start = &ural_scan_start_cb; + ic->ic_scan_end = &ural_scan_end_cb; + ic->ic_set_channel = &ural_set_channel_cb; + ic->ic_vap_create = &ural_vap_create; + ic->ic_vap_delete = &ural_vap_delete; + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + mtx_unlock(&sc->sc_mtx); + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); + + if (bootverbose) { + ieee80211_announce(ic); + } + mtx_lock(&sc->sc_mtx); +done: + return; +} + +static void +ural_end_of_commands(struct ural_softc *sc) +{ + sc->sc_flags &= ~URAL_FLAG_WAIT_COMMAND; + + /* start write transfer, if not started */ + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ural_config_copy_chan(struct ural_config_copy_chan *cc, + struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (!c) + return; + cc->chan_to_ieee = + ieee80211_chan2ieee(ic, c); + if (c != IEEE80211_CHAN_ANYC) { + cc->chan_to_mode = + ieee80211_chan2mode(c); + if (IEEE80211_IS_CHAN_B(c)) + cc->chan_is_b = 1; + if (IEEE80211_IS_CHAN_A(c)) + cc->chan_is_a = 1; + if (IEEE80211_IS_CHAN_2GHZ(c)) + cc->chan_is_2ghz = 1; + if (IEEE80211_IS_CHAN_5GHZ(c)) + cc->chan_is_5ghz = 1; + if (IEEE80211_IS_CHAN_ANYG(c)) + cc->chan_is_g = 1; + } + return; +} + +static void +ural_config_copy(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp; + struct ieee80211com *ic; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + const struct ieee80211_txparam *tp; + + bzero(cc, sizeof(*cc)); + + ifp = sc->sc_ifp; + if (ifp) { + cc->if_flags = ifp->if_flags; + bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, + sizeof(cc->if_broadcastaddr)); + + ic = ifp->if_l2com; + if (ic) { + ural_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); + ural_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); + vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap) { + ni = vap->iv_bss; + if (ni) { + cc->iv_bss.ni_intval = ni->ni_intval; + bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, + sizeof(cc->iv_bss.ni_bssid)); + } + tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { + cc->iv_bss.fixed_rate_none = 1; + } + } + cc->ic_opmode = ic->ic_opmode; + cc->ic_flags = ic->ic_flags; + cc->ic_txpowlimit = ic->ic_txpowlimit; + cc->ic_curmode = ic->ic_curmode; + + bcopy(ic->ic_myaddr, cc->ic_myaddr, + sizeof(cc->ic_myaddr)); + } + } + sc->sc_flags |= URAL_FLAG_WAIT_COMMAND; + return; +} + +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"; + } +} + +/*------------------------------------------------------------------------* + * ural_bulk_read_callback - data read "thread" + *------------------------------------------------------------------------*/ +static void +ural_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct ural_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + uint32_t flags; + uint32_t max_len; + uint32_t real_len; + uint8_t rssi = 0; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); + + if (xfer->actlen < RAL_RX_DESC_SIZE) { + DPRINTF("too short transfer, " + "%d bytes\n", xfer->actlen); + ifp->if_ierrors++; + goto tr_setup; + } + max_len = (xfer->actlen - RAL_RX_DESC_SIZE); + + usb2_copy_out(xfer->frbuffers, max_len, + &sc->sc_rx_desc, RAL_RX_DESC_SIZE); + + flags = le32toh(sc->sc_rx_desc.flags); + + if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RAL_TXRX_CSR2: + */ + DPRINTFN(6, "PHY or CRC error\n"); + ifp->if_ierrors++; + goto tr_setup; + } + if (max_len > MCLBYTES) { + max_len = MCLBYTES; + } + real_len = (flags >> 16) & 0xfff; + + if (real_len > max_len) { + DPRINTF("invalid length in RX " + "descriptor, %u bytes, received %u bytes\n", + real_len, max_len); + ifp->if_ierrors++; + goto tr_setup; + } + /* ieee80211_input() will check if the mbuf is too short */ + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, m->m_data, max_len); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = real_len; + + DPRINTF("real length=%d bytes\n", real_len); + + rssi = URAL_RSSI(sc->sc_rx_desc.rssi); + + 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(sc->sc_rx_desc.rate, + (sc->sc_rx_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->sc_rx_ant; + tap->wr_antsignal = rssi; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + /* Strip trailing 802.11 MAC FCS. */ + m_adj(m, -IEEE80211_CRC_LEN); + + case USB_ST_SETUP: +tr_setup: + + if (sc->sc_flags & URAL_FLAG_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[3]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + if (m) { + mtx_unlock(&sc->sc_mtx); + + ni = ieee80211_find_rxnode(ic, (void *)(m->m_data)); + + if (ni) { + /* send the frame to the 802.11 layer */ + if (ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0)) { + /* ignore */ + } + /* node is no longer needed */ + ieee80211_free_node(ni); + } else { + /* broadcast */ + if (ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0)) { + /* ignore */ + } + } + + mtx_lock(&sc->sc_mtx); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= URAL_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[3]); + } + return; + + } +} + +static void +ural_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ural_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[1]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~URAL_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static uint8_t +ural_plcp_signal(uint16_t rate) +{ + ; /* indent fix */ + switch (rate) { + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + + /* 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); + + /* XXX unsupported/unknown rate */ + default: + return (0xff); + } +} + +/* + * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that + * should be freed, when "ural_setup_desc_and_tx" is called. + */ +static void +ural_setup_desc_and_tx(struct ural_softc *sc, struct mbuf *m, + uint32_t flags, uint16_t rate) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct mbuf *mm; + enum ieee80211_phytype phytype; + uint16_t plcp_length; + uint16_t len; + uint8_t remainder; + + DPRINTF("in\n"); + + if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { + /* free packet */ + ural_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (!((sc->sc_flags & URAL_FLAG_LL_READY) && + (sc->sc_flags & URAL_FLAG_HL_READY))) { + /* free packet */ + ural_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (rate < 2) { + DPRINTF("rate < 2!\n"); + + /* avoid division by zero */ + rate = 2; + } + ic->ic_lastdata = ticks; + + 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->sc_tx_ant; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); + } + len = m->m_pkthdr.len; + + sc->sc_tx_desc.flags = htole32(flags); + sc->sc_tx_desc.flags |= htole32(RAL_TX_NEWSEQ); + sc->sc_tx_desc.flags |= htole32(len << 16); + + sc->sc_tx_desc.wme = htole16(RAL_AIFSN(2) | + RAL_LOGCWMIN(3) | + RAL_LOGCWMAX(5) | + RAL_IVOFFSET(sizeof(struct ieee80211_frame))); + + /* setup PLCP fields */ + sc->sc_tx_desc.plcp_signal = ural_plcp_signal(rate); + sc->sc_tx_desc.plcp_service = 4; + + len += IEEE80211_CRC_LEN; + + phytype = ieee80211_rate2phytype(sc->sc_rates, rate); + + if (phytype == IEEE80211_T_OFDM) { + sc->sc_tx_desc.flags |= htole32(RAL_TX_OFDM); + + plcp_length = len & 0xfff; + sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; + sc->sc_tx_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)) { + sc->sc_tx_desc.plcp_service |= + RAL_PLCP_LENGEXT; + } + } + sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; + sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; + + if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { + sc->sc_tx_desc.plcp_signal |= 0x08; + } + } + + sc->sc_tx_desc.iv = 0; + sc->sc_tx_desc.eiv = 0; + + if (sizeof(sc->sc_tx_desc) > MHLEN) { + DPRINTF("No room for header structure!\n"); + ural_tx_freem(m); + return; + } + mm = m_gethdr(M_NOWAIT, MT_DATA); + if (mm == NULL) { + DPRINTF("Could not allocate header mbuf!\n"); + ural_tx_freem(m); + return; + } + DPRINTF(" %zu %u (out)\n", sizeof(sc->sc_tx_desc), m->m_pkthdr.len); + + bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); + mm->m_len = sizeof(sc->sc_tx_desc); + + mm->m_next = m; + mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; + mm->m_pkthdr.rcvif = NULL; + + /* start write transfer, if not started */ + _IF_ENQUEUE(&sc->sc_tx_queue, mm); + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +ural_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct ural_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint16_t temp_len; + uint8_t align; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); + + ifp->if_opackets++; + + case USB_ST_SETUP: + if (sc->sc_flags & URAL_FLAG_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[2]); + break; + } + if (sc->sc_flags & URAL_FLAG_WAIT_COMMAND) { + /* + * don't send anything while a command is pending ! + */ + break; + } + ural_fill_write_queue(sc); + + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (m) { + + if (m->m_pkthdr.len > (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); + } + usb2_m_copy_in(xfer->frbuffers, 0, + m, 0, m->m_pkthdr.len); + + /* compute transfer length */ + temp_len = m->m_pkthdr.len; + + /* make transfer length 16-bit aligned */ + align = (temp_len & 1); + + /* check if we need to add two extra bytes */ + if (((temp_len + align) % 64) == 0) { + align += 2; + } + /* check if we need to align length */ + if (align != 0) { + /* zero the extra bytes */ + usb2_bzero(xfer->frbuffers, temp_len, align); + temp_len += align; + } + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, temp_len); + + xfer->frlengths[0] = temp_len; + + usb2_start_hardware(xfer); + + /* free mbuf and node */ + ural_tx_freem(m); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= URAL_FLAG_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[2]); + } + ifp->if_oerrors++; + break; + } + return; +} + +static void +ural_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct ural_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[0]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~URAL_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +ural_watchdog(void *arg) +{ + struct ural_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (sc->sc_amrr_timer) { + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, + &ural_cfg_amrr_timeout, 0, 0); + } + usb2_callout_reset(&sc->sc_watchdog, + hz, &ural_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +/*========================================================================* + * IF-net callbacks + *========================================================================*/ + +static void +ural_init_cb(void *arg) +{ + struct ural_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_cfg_pre_init, + &ural_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static int +ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ural_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + int error; + + switch (cmd) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_cfg_pre_init, + &ural_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_cfg_pre_stop, + &ural_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + error = 0; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + } + return (error); +} + +static void +ural_start_cb(struct ifnet *ifp) +{ + struct ural_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + /* start write transfer, if not started */ + usb2_transfer_start(sc->sc_xfer[0]); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ural_cfg_newstate(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + 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); + enum ieee80211_state ostate; + enum ieee80211_state nstate; + int arg; + + ostate = vap->iv_state; + nstate = sc->sc_ns_state; + arg = sc->sc_ns_arg; + + if (ostate == IEEE80211_S_INIT) { + /* We are leaving INIT. TSF sync should be off. */ + ural_cfg_disable_tsf_sync(sc); + } + switch (nstate) { + case IEEE80211_S_INIT: + break; + + case IEEE80211_S_RUN: + ural_cfg_set_run(sc, cc); + break; + + default: + break; + } + + mtx_unlock(&sc->sc_mtx); + IEEE80211_LOCK(ic); + uvp->newstate(vap, nstate, arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + mtx_lock(&sc->sc_mtx); + return; +} + +static int +ural_newstate_cb(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; + + DPRINTF("setting new state: %d\n", nstate); + + /* Special case - cannot defer this call and cannot block ! */ + if (nstate == IEEE80211_S_INIT) { + /* stop timers */ + mtx_lock(&sc->sc_mtx); + sc->sc_amrr_timer = 0; + mtx_unlock(&sc->sc_mtx); + return (uvp->newstate(vap, nstate, arg)); + } + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + return (0); /* nothing to do */ + } + /* store next state */ + sc->sc_ns_state = nstate; + sc->sc_ns_arg = arg; + + /* stop timers */ + sc->sc_amrr_timer = 0; + + /* + * USB configuration can only be done from the USB configuration + * thread: + */ + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_config_copy, + &ural_cfg_newstate, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return (EINPROGRESS); +} + +static void +ural_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_config_copy, func, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +ural_scan_start_cb(struct ieee80211com *ic) +{ + ural_std_command(ic, &ural_cfg_scan_start); + return; +} + +static void +ural_scan_end_cb(struct ieee80211com *ic) +{ + ural_std_command(ic, &ural_cfg_scan_end); + return; +} + +static void +ural_set_channel_cb(struct ieee80211com *ic) +{ + ural_std_command(ic, &ural_cfg_set_chan); + return; +} + +/*========================================================================* + * configure sub-routines, ural_cfg_xxx + *========================================================================*/ + +static void +ural_cfg_scan_start(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* abort TSF synchronization */ + ural_cfg_disable_tsf_sync(sc); + ural_cfg_set_bssid(sc, cc->if_broadcastaddr); + return; +} + +static void +ural_cfg_scan_end(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* enable TSF synchronization */ + ural_cfg_enable_tsf_sync(sc, cc, 0); + ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + return; +} + +static void +ural_cfg_set_chan(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + enum { + N_RF5222 = (sizeof(ural_rf5222) / sizeof(ural_rf5222[0])), + }; + uint32_t i; + uint32_t chan; + uint8_t power; + uint8_t tmp; + + chan = cc->ic_curchan.chan_to_ieee; + + if ((chan == 0) || + (chan == IEEE80211_CHAN_ANY)) { + /* nothing to do */ + return; + } + if (cc->ic_curchan.chan_is_2ghz) + power = min(sc->sc_txpow[chan - 1], 31); + else + power = 31; + + /* adjust txpower using ifconfig settings */ + power -= (100 - cc->ic_txpowlimit) / 8; + + DPRINTFN(3, "setting channel to %u, " + "tx-power to %u\n", chan, power); + + switch (sc->sc_rf_rev) { + case RAL_RF_2522: + ural_cfg_rf_write(sc, RAL_RF1, 0x00814); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); + break; + + case RAL_RF_2523: + ural_cfg_rf_write(sc, RAL_RF1, 0x08804); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x38044); + ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2524: + ural_cfg_rf_write(sc, RAL_RF1, 0x0c808); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); + ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525: + ural_cfg_rf_write(sc, RAL_RF1, 0x08808); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); + ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + + ural_cfg_rf_write(sc, RAL_RF1, 0x08808); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); + ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525E: + ural_cfg_rf_write(sc, RAL_RF1, 0x08808); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); + ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); + break; + + case RAL_RF_2526: + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + ural_cfg_rf_write(sc, RAL_RF1, 0x08804); + + ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); + ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + break; + + /* dual-band RF */ + case RAL_RF_5222: + for (i = 0; i < N_RF5222; i++) { + if (ural_rf5222[i].chan == chan) { + ural_cfg_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); + ural_cfg_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); + ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); + ural_cfg_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); + break; + } + } + break; + } + + if ((cc->ic_opmode != IEEE80211_M_MONITOR) && + (!(cc->ic_flags & IEEE80211_F_SCAN))) { + + /* set Japan filter bit for channel 14 */ + tmp = ural_cfg_bbp_read(sc, 70); + + if (chan == 14) { + tmp |= RAL_JAPAN_FILTER; + } else { + tmp &= ~RAL_JAPAN_FILTER; + } + + ural_cfg_bbp_write(sc, 70, tmp); + + /* clear CRC errors */ + ural_cfg_read(sc, RAL_STA_CSR0); + + ural_cfg_disable_rf_tune(sc); + } + /* update basic rate set */ + if (cc->ic_curchan.chan_is_b) { + /* 11b basic rates: 1, 2Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); + } else if (cc->ic_curchan.chan_is_a) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); + } else { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); + } + + /* wait a little */ + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return; + } + return; +} + +static void +ural_cfg_set_run(struct ural_softc *sc, + struct usb2_config_td_cc *cc) +{ + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + ural_cfg_update_slot(sc, cc, 0); + ural_cfg_set_txpreamble(sc, cc, 0); + + /* update basic rate set */ + + if (cc->ic_bsschan.chan_is_5ghz) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); + } else if (cc->ic_bsschan.chan_is_g) { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); + } else { + /* 11b basic rates: 1, 2Mbps */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); + } + ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + } + if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || + (cc->ic_opmode == IEEE80211_M_IBSS)) { + ural_tx_bcn(sc); + } + /* make tx led blink on tx (controlled by ASIC) */ + ural_cfg_write(sc, RAL_MAC_CSR20, 1); + + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + ural_cfg_enable_tsf_sync(sc, cc, 0); + } + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); + + if (cc->iv_bss.fixed_rate_none) { + /* enable automatic rate adaptation */ + ural_cfg_amrr_start(sc); + } + return; +} + +/*------------------------------------------------------------------------* + * ural_cfg_disable_rf_tune - disable RF auto-tuning + *------------------------------------------------------------------------*/ +static void +ural_cfg_disable_rf_tune(struct ural_softc *sc) +{ + uint32_t tmp; + + if (sc->sc_rf_rev != RAL_RF_2523) { + tmp = sc->sc_rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; + ural_cfg_rf_write(sc, RAL_RF1, tmp); + } + tmp = sc->sc_rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; + ural_cfg_rf_write(sc, RAL_RF3, tmp); + + DPRINTFN(3, "disabling RF autotune\n"); + + return; +} + +/*------------------------------------------------------------------------* + * ural_cfg_enable_tsf_sync - refer to IEEE Std 802.11-1999 pp. 123 + * for more information on TSF synchronization + *------------------------------------------------------------------------*/ +static void +ural_cfg_enable_tsf_sync(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t logcwmin; + uint16_t preload; + uint16_t tmp; + + /* first, disable TSF synchronization */ + ural_cfg_write(sc, RAL_TXRX_CSR19, 0); + + tmp = (16 * cc->iv_bss.ni_intval) << 4; + ural_cfg_write(sc, RAL_TXRX_CSR18, tmp); + + logcwmin = (cc->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; + preload = (cc->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; + tmp = (logcwmin << 12) | preload; + ural_cfg_write(sc, RAL_TXRX_CSR20, tmp); + + /* finally, enable TSF synchronization */ + tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; + if (cc->ic_opmode == IEEE80211_M_STA) + tmp |= RAL_ENABLE_TSF_SYNC(1); + else + tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; + + ural_cfg_write(sc, RAL_TXRX_CSR19, tmp); + + DPRINTF("enabling TSF synchronization\n"); + + return; +} + +static void +ural_cfg_disable_tsf_sync(struct ural_softc *sc) +{ + /* abort TSF synchronization */ + ural_cfg_write(sc, RAL_TXRX_CSR19, 0); + + /* force tx led to stop blinking */ + ural_cfg_write(sc, RAL_MAC_CSR20, 0); + + return; +} + +#define RAL_RXTX_TURNAROUND 5 /* us */ + +static void +ural_cfg_update_slot(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t slottime; + uint16_t sifs; + uint16_t eifs; + + slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + + /* + * These settings may sound a bit inconsistent but this is what the + * reference driver does. + */ + if (cc->ic_curmode == IEEE80211_MODE_11B) { + sifs = 16 - RAL_RXTX_TURNAROUND; + eifs = 364; + } else { + sifs = 10 - RAL_RXTX_TURNAROUND; + eifs = 64; + } + + ural_cfg_write(sc, RAL_MAC_CSR10, slottime); + ural_cfg_write(sc, RAL_MAC_CSR11, sifs); + ural_cfg_write(sc, RAL_MAC_CSR12, eifs); + return; +} + +static void +ural_cfg_set_txpreamble(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t tmp; + + tmp = ural_cfg_read(sc, RAL_TXRX_CSR10); + + if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) { + tmp |= RAL_SHORT_PREAMBLE; + } else { + tmp &= ~RAL_SHORT_PREAMBLE; + } + + ural_cfg_write(sc, RAL_TXRX_CSR10, tmp); + return; +} + +static void +ural_cfg_set_bssid(struct ural_softc *sc, uint8_t *bssid) +{ + ural_cfg_write_multi(sc, RAL_MAC_CSR5, bssid, IEEE80211_ADDR_LEN); + + DPRINTF("setting BSSID to 0x%02x%02x%02x%02x%02x%02x\n", + bssid[5], bssid[4], bssid[3], + bssid[2], bssid[1], bssid[0]); + return; +} + +static void +ural_cfg_set_macaddr(struct ural_softc *sc, uint8_t *addr) +{ + ural_cfg_write_multi(sc, RAL_MAC_CSR2, addr, IEEE80211_ADDR_LEN); + + DPRINTF("setting MAC to 0x%02x%02x%02x%02x%02x%02x\n", + addr[5], addr[4], addr[3], + addr[2], addr[1], addr[0]); + return; +} + +static void +ural_cfg_update_promisc(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint16_t tmp; + + tmp = ural_cfg_read(sc, RAL_TXRX_CSR2); + + if (cc->if_flags & IFF_PROMISC) { + tmp &= ~RAL_DROP_NOT_TO_ME; + } else { + tmp |= RAL_DROP_NOT_TO_ME; + } + + ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); + + DPRINTF("%s promiscuous mode\n", + (cc->if_flags & IFF_PROMISC) ? + "entering" : "leaving"); + return; +} + +static void +ural_cfg_set_txantenna(struct ural_softc *sc, uint8_t antenna) +{ + uint16_t tmp; + uint8_t tx; + + tx = ural_cfg_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->sc_rf_rev == RAL_RF_2525E) || + (sc->sc_rf_rev == RAL_RF_2526) || + (sc->sc_rf_rev == RAL_RF_5222)) { + tx |= RAL_BBP_FLIPIQ; + } + ural_cfg_bbp_write(sc, RAL_BBP_TX, tx); + + /* update values in PHY_CSR5 and PHY_CSR6 */ + tmp = ural_cfg_read(sc, RAL_PHY_CSR5) & ~0x7; + ural_cfg_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); + + tmp = ural_cfg_read(sc, RAL_PHY_CSR6) & ~0x7; + ural_cfg_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); + + return; +} + +static void +ural_cfg_set_rxantenna(struct ural_softc *sc, uint8_t antenna) +{ + uint8_t rx; + + rx = ural_cfg_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->sc_rf_rev == RAL_RF_2525E) || + (sc->sc_rf_rev == RAL_RF_2526)) { + rx &= ~RAL_BBP_FLIPIQ; + } + ural_cfg_bbp_write(sc, RAL_BBP_RX, rx); + return; +} + +static void +ural_cfg_read_eeprom(struct ural_softc *sc) +{ + uint16_t val; + + ural_cfg_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); + + val = le16toh(val); + + sc->sc_rf_rev = (val >> 11) & 0x7; + sc->sc_hw_radio = (val >> 10) & 0x1; + sc->sc_led_mode = (val >> 6) & 0x7; + sc->sc_rx_ant = (val >> 4) & 0x3; + sc->sc_tx_ant = (val >> 2) & 0x3; + sc->sc_nb_ant = (val & 0x3); + + DPRINTF("val = 0x%04x\n", val); + + /* read MAC address */ + ural_cfg_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_myaddr, + sizeof(sc->sc_myaddr)); + + /* read default values for BBP registers */ + ural_cfg_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->sc_bbp_prom, + sizeof(sc->sc_bbp_prom)); + + /* read Tx power for all b/g channels */ + ural_cfg_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->sc_txpow, + sizeof(sc->sc_txpow)); + return; +} + +static uint8_t +ural_cfg_bbp_init(struct ural_softc *sc) +{ + enum { + N_DEF_BBP = (sizeof(ural_def_bbp) / sizeof(ural_def_bbp[0])), + }; + uint16_t i; + uint8_t to; + + /* wait for BBP to become ready */ + for (to = 0;; to++) { + if (to < 100) { + if (ural_cfg_bbp_read(sc, RAL_BBP_VERSION) != 0) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + return (1); /* failure */ + } + } else { + DPRINTF("timeout waiting for BBP\n"); + return (1); /* failure */ + } + } + + /* initialize BBP registers to default values */ + for (i = 0; i < N_DEF_BBP; i++) { + ural_cfg_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->sc_bbp_prom[i].reg == 0xff) { + continue; + } + ural_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, + sc->sc_bbp_prom[i].val); + } +#endif + return (0); /* success */ +} + +static void +ural_cfg_pre_init(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* immediate configuration */ + + ural_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= URAL_FLAG_HL_READY; + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + + return; +} + +static void +ural_cfg_init(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + enum { + N_DEF_MAC = (sizeof(ural_def_mac) / sizeof(ural_def_mac[0])), + }; + uint16_t tmp; + uint16_t i; + uint8_t to; + + /* delayed configuration */ + + ural_cfg_set_testmode(sc); + + ural_cfg_write(sc, 0x308, 0x00f0); /* XXX magic */ + + ural_cfg_stop(sc, cc, 0); + + /* initialize MAC registers to default values */ + for (i = 0; i < N_DEF_MAC; i++) { + ural_cfg_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 (to = 0;; to++) { + if (to < 100) { + tmp = ural_cfg_read(sc, RAL_MAC_CSR17); + if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == + (RAL_BBP_AWAKE | RAL_RF_AWAKE)) { + break; + } + if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { + goto fail; + } + } else { + DPRINTF("timeout waiting for " + "BBP/RF to wakeup\n"); + goto fail; + } + } + + /* we're ready! */ + ural_cfg_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); + + /* set basic rate set (will be updated later) */ + ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); + + if (ural_cfg_bbp_init(sc)) { + goto fail; + } + /* set default BSS channel */ + ural_cfg_set_chan(sc, cc, 0); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, + sizeof(sc->sc_sta)); + + DPRINTF("rx_ant=%d, tx_ant=%d\n", + sc->sc_rx_ant, sc->sc_tx_ant); + + ural_cfg_set_txantenna(sc, sc->sc_tx_ant); + ural_cfg_set_rxantenna(sc, sc->sc_rx_ant); + + ural_cfg_set_macaddr(sc, cc->ic_myaddr); + + /* + * make sure that the first transaction + * clears the stall: + */ + sc->sc_flags |= (URAL_FLAG_READ_STALL | + URAL_FLAG_WRITE_STALL | + URAL_FLAG_LL_READY); + + if ((sc->sc_flags & URAL_FLAG_LL_READY) && + (sc->sc_flags & URAL_FLAG_HL_READY)) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + + /* + * start IEEE802.11 layer + */ + mtx_unlock(&sc->sc_mtx); + ieee80211_start_all(ic); + mtx_lock(&sc->sc_mtx); + } + /* + * start Rx + */ + tmp = RAL_DROP_PHY | RAL_DROP_CRC; + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + + tmp |= (RAL_DROP_CTL | RAL_DROP_BAD_VERSION); + + if (cc->ic_opmode != IEEE80211_M_HOSTAP) { + tmp |= RAL_DROP_TODS; + } + if (!(cc->if_flags & IFF_PROMISC)) { + tmp |= RAL_DROP_NOT_TO_ME; + } + } + ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); + + return; + +fail: + ural_cfg_pre_stop(sc, NULL, 0); + + if (cc) { + ural_cfg_stop(sc, cc, 0); + } + return; +} + +static void +ural_cfg_pre_stop(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + ural_config_copy(sc, cc, refcount); + } + /* immediate configuration */ + + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(URAL_FLAG_HL_READY | + URAL_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[0]); + usb2_transfer_stop(sc->sc_xfer[1]); + usb2_transfer_stop(sc->sc_xfer[2]); + usb2_transfer_stop(sc->sc_xfer[3]); + + /* clean up transmission */ + ural_tx_clean_queue(sc); + return; +} + +static void +ural_cfg_stop(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* disable Rx */ + ural_cfg_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); + + /* reset ASIC and BBP (but won't reset MAC registers!) */ + ural_cfg_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); + + /* wait a little */ + usb2_config_td_sleep(&sc->sc_config_td, hz / 10); + + /* clear reset */ + ural_cfg_write(sc, RAL_MAC_CSR1, 0); + + /* wait a little */ + usb2_config_td_sleep(&sc->sc_config_td, hz / 10); + + return; +} + +static void +ural_cfg_amrr_start(struct ural_softc *sc) +{ + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + vap = ural_get_vap(sc); + + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + /* init AMRR */ + + ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); + /* enable AMRR timer */ + + sc->sc_amrr_timer = 1; + return; +} + +static void +ural_cfg_amrr_timeout(struct ural_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + uint32_t ok; + uint32_t fail; + + /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); + + vap = ural_get_vap(sc); + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + if ((sc->sc_flags & URAL_FLAG_LL_READY) && + (sc->sc_flags & URAL_FLAG_HL_READY)) { + + ok = sc->sc_sta[7] + /* TX ok w/o retry */ + sc->sc_sta[8]; /* TX ok w/ retry */ + fail = sc->sc_sta[9]; /* TX retry-fail count */ + + if (sc->sc_amrr_timer) { + + ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, + ok + fail, ok, sc->sc_sta[8] + fail); + + if (ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn)) { + /* ignore */ + } + } + ifp->if_oerrors += fail; + } + return; +} + +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; + struct ural_softc *sc = ic->ic_ifp->if_softc; + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + /* config thread is gone */ + return (NULL); + } + mtx_unlock(&sc->sc_mtx); + + 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_cb; + + 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); + + /* store current operation mode */ + ic->ic_opmode = opmode; + + return (vap); +} + +static void +ural_vap_delete(struct ieee80211vap *vap) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + /* ignore */ + } + mtx_unlock(&sc->sc_mtx); + + ieee80211_amrr_cleanup(&uvp->amrr); + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); + return; +} + +/* 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); + return; +} + +static void +ural_fill_write_queue(struct ural_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + + /* + * We only fill up half of the queue with data frames. The rest is + * reserved for other kinds of frames. + */ + + while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + ni = (void *)(m->m_pkthdr.rcvif); + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + continue; + } + ural_tx_data(sc, m, ni); + } + return; +} + +static void +ural_tx_clean_queue(struct ural_softc *sc) +{ + struct mbuf *m; + + for (;;) { + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (!m) { + break; + } + ural_tx_freem(m); + } + + return; +} + +static void +ural_tx_freem(struct mbuf *m) +{ + struct ieee80211_node *ni; + + while (m) { + ni = (void *)(m->m_pkthdr.rcvif); + if (!ni) { + m = m_free(m); + continue; + } + if (m->m_flags & M_TXCB) { + ieee80211_process_callback(ni, m, 0); + } + m_freem(m); + ieee80211_free_node(ni); + + break; + } + return; +} + +static void +ural_tx_mgt(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags; + uint16_t dur; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + wh = mtod(m, struct ieee80211_frame *); + } + 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); + USETW(wh->i_dur, 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; + } + m->m_pkthdr.rcvif = (void *)ni; + ural_setup_desc_and_tx(sc, m, flags, tp->mgmtrate); + return; +} + +static struct ieee80211vap * +ural_get_vap(struct ural_softc *sc) +{ + struct ifnet *ifp; + struct ieee80211com *ic; + + if (sc == NULL) { + return NULL; + } + ifp = sc->sc_ifp; + if (ifp == NULL) { + return NULL; + } + ic = ifp->if_l2com; + if (ic == NULL) { + return NULL; + } + return TAILQ_FIRST(&ic->ic_vaps); +} + +static void +ural_tx_bcn(struct ural_softc *sc) +{ + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ieee80211com *ic; + const struct ieee80211_txparam *tp; + struct mbuf *m; + + vap = ural_get_vap(sc); + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + ic = vap->iv_ic; + if (ic == NULL) { + return; + } + DPRINTFN(11, "Sending beacon frame.\n"); + + m = ieee80211_beacon_alloc(ni, &URAL_VAP(vap)->bo); + if (m == NULL) { + DPRINTFN(0, "could not allocate beacon\n"); + return; + } + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + + m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); + ural_setup_desc_and_tx(sc, m, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, + tp->mgmtrate); + return; +} + +static void +ural_tx_data(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + uint16_t rate; + + DPRINTFN(11, "Sending data.\n"); + + wh = mtod(m, 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, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m, struct ieee80211_frame *); + } + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + uint8_t prot = IEEE80211_PROT_NONE; + + if (m->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) { + ural_tx_prot(sc, m, ni, prot, rate); + flags |= RAL_TX_IFS_SIFS; + } + flags |= RAL_TX_ACK; + flags |= RAL_TX_RETRY(7); + + dur = ieee80211_ack_duration(sc->sc_rates, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + m->m_pkthdr.rcvif = (void *)ni; + ural_setup_desc_and_tx(sc, m, flags, rate); + return; +} + +static void +ural_tx_prot(struct ural_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, + uint8_t prot, uint16_t rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct mbuf *mprot; + uint32_t flags; + uint16_t protrate; + uint16_t ackrate; + uint16_t pktlen; + uint16_t dur; + uint8_t isshort; + + KASSERT((prot == IEEE80211_PROT_RTSCTS) || + (prot == IEEE80211_PROT_CTSONLY), + ("protection %u", prot)); + + DPRINTFN(16, "Sending protection frame.\n"); + + 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) { + return; + } + mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); + ural_setup_desc_and_tx(sc, mprot, flags, protrate); + return; +} + +static void +ural_tx_raw(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + uint32_t flags; + uint16_t rate; + + DPRINTFN(11, "Sending raw frame.\n"); + + rate = params->ibp_rate0 & IEEE80211_RATE_VAL; + + /* XXX validate */ + if (rate == 0) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RAL_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { + ural_tx_prot(sc, m, ni, + (params->ibp_flags & IEEE80211_BPF_RTS) ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + flags |= RAL_TX_IFS_SIFS; + } + m->m_pkthdr.rcvif = (void *)ni; + ural_setup_desc_and_tx(sc, m, flags, rate); + return; +} + +static int +ural_raw_xmit_cb(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; + + mtx_lock(&sc->sc_mtx); + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + ural_tx_mgt(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + ural_tx_raw(sc, m, ni, params); + } + mtx_unlock(&sc->sc_mtx); + return (0); +} + +static void +ural_update_mcast_cb(struct ifnet *ifp) +{ + /* not supported */ + return; +} + +static void +ural_update_promisc_cb(struct ifnet *ifp) +{ + struct ural_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &ural_config_copy, + &ural_cfg_update_promisc, 0, 0); + mtx_unlock(&sc->sc_mtx); + return; +} diff --git a/sys/dev/usb2/wlan/if_ural2_reg.h b/sys/dev/usb2/wlan/if_ural2_reg.h new file mode 100644 index 000000000000..1837693e03a2 --- /dev/null +++ b/sys/dev/usb2/wlan/if_ural2_reg.h @@ -0,0 +1,198 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * Damien Bergamini + * + * 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_FRAME_SIZE 0x780 /* NOTE: using 0x980 does not work */ +#if (RAL_FRAME_SIZE % 0x80) +#error "Invalid RAL_FRAME_SIZE" +#endif + +#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 + +#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 + +struct ural_tx_desc { + uint32_t flags; +#define RAL_TX_PACKET_ID(x) ((x) & 0xf) +#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; diff --git a/sys/dev/usb2/wlan/if_ural2_var.h b/sys/dev/usb2/wlan/if_ural2_var.h new file mode 100644 index 000000000000..c91643c804c1 --- /dev/null +++ b/sys/dev/usb2/wlan/if_ural2_var.h @@ -0,0 +1,161 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005 + * Damien Bergamini + * + * 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 URAL_N_TRANSFER 4 + +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; + + int (*newstate) (struct ieee80211vap *, + enum ieee80211_state, int); +}; + +#define URAL_VAP(vap) ((struct ural_vap *)(vap)) + +struct ural_config_copy_chan { + uint32_t chan_to_ieee; + enum ieee80211_phymode chan_to_mode; + uint8_t chan_is_5ghz:1; + uint8_t chan_is_2ghz:1; + uint8_t chan_is_b:1; + uint8_t chan_is_a:1; + uint8_t chan_is_g:1; + uint8_t unused:3; +}; + +struct ural_config_copy_bss { + uint16_t ni_intval; + uint8_t ni_bssid[IEEE80211_ADDR_LEN]; + uint8_t fixed_rate_none; +}; + +struct ural_config_copy { + struct ural_config_copy_chan ic_curchan; + struct ural_config_copy_chan ic_bsschan; + struct ural_config_copy_bss iv_bss; + + enum ieee80211_opmode ic_opmode; + uint32_t ic_flags; + uint32_t if_flags; + + uint16_t ic_txpowlimit; + uint16_t ic_curmode; + + uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; + uint8_t if_broadcastaddr[IEEE80211_ADDR_LEN]; +}; + +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_bbp_prom { + uint8_t val; + uint8_t reg; +} __packed; + +struct ural_ifq { + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + uint16_t ifq_len; +}; + +struct ural_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct ural_ifq sc_tx_queue; + struct usb2_config_td sc_config_td; + struct ural_tx_desc sc_tx_desc; + struct ural_rx_desc sc_rx_desc; + struct mtx sc_mtx; + struct usb2_callout sc_watchdog; + struct ural_bbp_prom sc_bbp_prom[16]; + struct ural_rx_radiotap_header sc_rxtap; + struct ural_tx_radiotap_header sc_txtap; + + struct usb2_xfer *sc_xfer[URAL_N_TRANSFER]; + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + const struct ieee80211_rate_table *sc_rates; + + enum ieee80211_state sc_ns_state; + uint32_t sc_unit; + uint32_t sc_asic_rev; + uint32_t sc_rf_regs[4]; + int sc_ns_arg; + + uint16_t sc_flags; +#define URAL_FLAG_READ_STALL 0x0001 +#define URAL_FLAG_WRITE_STALL 0x0002 +#define URAL_FLAG_LL_READY 0x0004 +#define URAL_FLAG_HL_READY 0x0008 +#define URAL_FLAG_WAIT_COMMAND 0x0010 + uint16_t sc_txtap_len; + uint16_t sc_rxtap_len; + uint16_t sc_sta[11]; + + uint8_t sc_rf_rev; + uint8_t sc_txpow[14]; + uint8_t sc_led_mode; + uint8_t sc_hw_radio; + uint8_t sc_rx_ant; + uint8_t sc_tx_ant; + uint8_t sc_nb_ant; + uint8_t sc_amrr_timer; + uint8_t sc_myaddr[IEEE80211_ADDR_LEN]; + + char sc_name[32]; +}; diff --git a/sys/dev/usb2/wlan/if_zyd2.c b/sys/dev/usb2/wlan/if_zyd2.c new file mode 100644 index 000000000000..5e72593f17d8 --- /dev/null +++ b/sys/dev/usb2/wlan/if_zyd2.c @@ -0,0 +1,3297 @@ +/* $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 + * Copyright (c) 2006 by Florian Stoehr + * + * 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 +__FBSDID("$FreeBSD$"); + +/* + * ZyDAS ZD1211/ZD1211B USB WLAN driver + * + * NOTE: all function names beginning like "zyd_cfg_" can only + * be called from within the config thread function ! + */ + +#include +#include +#include +#include + +#define usb2_config_td_cc zyd_config_copy +#define usb2_config_td_softc zyd_softc + +#define USB_DEBUG_VAR zyd_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if USB_DEBUG +static int zyd_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); +SYSCTL_INT(_hw_usb2_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, + "zyd debug level"); +#endif + +#undef INDEXES +#define INDEXES(a) (sizeof(a) / sizeof((a)[0])) + +static device_probe_t zyd_probe; +static device_attach_t zyd_attach; +static device_detach_t zyd_detach; + +static usb2_callback_t zyd_intr_read_clear_stall_callback; +static usb2_callback_t zyd_intr_read_callback; +static usb2_callback_t zyd_intr_write_clear_stall_callback; +static usb2_callback_t zyd_intr_write_callback; +static usb2_callback_t zyd_bulk_read_clear_stall_callback; +static usb2_callback_t zyd_bulk_read_callback; +static usb2_callback_t zyd_bulk_write_clear_stall_callback; +static usb2_callback_t zyd_bulk_write_callback; + +static usb2_config_td_command_t zyd_cfg_first_time_setup; +static usb2_config_td_command_t zyd_cfg_update_promisc; +static usb2_config_td_command_t zyd_cfg_set_chan; +static usb2_config_td_command_t zyd_cfg_pre_init; +static usb2_config_td_command_t zyd_cfg_init; +static usb2_config_td_command_t zyd_cfg_pre_stop; +static usb2_config_td_command_t zyd_cfg_stop; +static usb2_config_td_command_t zyd_config_copy; +static usb2_config_td_command_t zyd_cfg_scan_start; +static usb2_config_td_command_t zyd_cfg_scan_end; +static usb2_config_td_command_t zyd_cfg_set_rxfilter; +static usb2_config_td_command_t zyd_cfg_amrr_timeout; + +static uint8_t zyd_plcp2ieee(uint8_t signal, uint8_t isofdm); +static void zyd_cfg_usbrequest(struct zyd_softc *sc, struct usb2_device_request *req, uint8_t *data); +static void zyd_cfg_usb2_intr_read(struct zyd_softc *sc, void *data, uint32_t size); +static void zyd_cfg_usb2_intr_write(struct zyd_softc *sc, const void *data, uint16_t code, uint32_t size); +static void zyd_cfg_read16(struct zyd_softc *sc, uint16_t addr, uint16_t *value); +static void zyd_cfg_read32(struct zyd_softc *sc, uint16_t addr, uint32_t *value); +static void zyd_cfg_write16(struct zyd_softc *sc, uint16_t addr, uint16_t value); +static void zyd_cfg_write32(struct zyd_softc *sc, uint16_t addr, uint32_t value); +static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value); +static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc, const uint8_t *fw_ptr, uint32_t fw_len); +static void zyd_cfg_lock_phy(struct zyd_softc *sc); +static void zyd_cfg_unlock_phy(struct zyd_softc *sc); +static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t interval); +static const char *zyd_rf_name(uint8_t type); +static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf); +static void zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *sc, uint8_t onoff); +static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); +static void zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *sc, uint8_t onoff); +static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf); +static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf); +static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); +static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf); +static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc); +static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr); +static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff); +static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr); +static void zyd_start_cb(struct ifnet *ifp); +static void zyd_init_cb(void *arg); +static int zyd_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); +static void zyd_watchdog(void *arg); +static void zyd_end_of_commands(struct zyd_softc *sc); +static void zyd_newassoc_cb(struct ieee80211_node *ni, int isnew); +static void zyd_scan_start_cb(struct ieee80211com *ic); +static void zyd_scan_end_cb(struct ieee80211com *ic); +static void zyd_set_channel_cb(struct ieee80211com *ic); +static void zyd_cfg_set_led(struct zyd_softc *sc, uint32_t which, uint8_t on); +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]); +static void zyd_vap_delete(struct ieee80211vap *); +static struct ieee80211_node *zyd_node_alloc_cb(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); +static void zyd_cfg_set_run(struct zyd_softc *sc, struct usb2_config_td_cc *cc); +static void zyd_fill_write_queue(struct zyd_softc *sc); +static void zyd_tx_clean_queue(struct zyd_softc *sc); +static void zyd_tx_freem(struct mbuf *m); +static void zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static struct ieee80211vap *zyd_get_vap(struct zyd_softc *sc); +static void zyd_tx_data(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni); +static int zyd_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); +static void zyd_setup_desc_and_tx(struct zyd_softc *sc, struct mbuf *m, uint16_t rate); +static int zyd_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg); +static void zyd_cfg_amrr_start(struct zyd_softc *sc); +static void zyd_update_mcast_cb(struct ifnet *ifp); +static void zyd_update_promisc_cb(struct ifnet *ifp); + +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 0 +#define ZYD_ZD1211B 1 + +static const struct usb2_device_id zyd_devs[] = { + /* ZYD_ZD1211 */ + {USB_VPI(USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GD, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54GZ, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_SENAO, USB_PRODUCT_SENAO_NUB8301, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_1, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_2, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_ALL0298V2, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_AG225H, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G200V2, ZYD_ZD1211)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G202, ZYD_ZD1211)}, + /* ZYD_ZD1211B */ + {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SMCWUSBG, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_A9T_WIFI, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050_V4000, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSBF54G, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_FIBERLINE, USB_PRODUCT_FIBERLINE_WL430U, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54L, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_SNU5600, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US54GXS, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG76NA, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UBC1, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_USR, USB_PRODUCT_USR_USR5423, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211B, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_M202, ZYD_ZD1211B)}, + {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G220V2, ZYD_ZD1211B)}, +}; + +static const struct usb2_config zyd_config[ZYD_N_TRANSFER] = { + [ZYD_TR_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = ZYD_MAX_TXBUFSZ, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &zyd_bulk_write_callback, + .ep_index = 0, + .mh.timeout = 10000, /* 10 seconds */ + }, + + [ZYD_TR_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = ZYX_MAX_RXBUFSZ, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &zyd_bulk_read_callback, + .ep_index = 0, + }, + + [ZYD_TR_BULK_CS_WR] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &zyd_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [ZYD_TR_BULK_CS_RD] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &zyd_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [ZYD_TR_INTR_DT_WR] = { + .type = UE_BULK_INTR, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = sizeof(struct zyd_cmd), + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &zyd_intr_write_callback, + .mh.timeout = 1000, /* 1 second */ + .ep_index = 1, + }, + + [ZYD_TR_INTR_DT_RD] = { + .type = UE_BULK_INTR, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = sizeof(struct zyd_cmd), + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &zyd_intr_read_callback, + .ep_index = 1, + }, + + [ZYD_TR_INTR_CS_WR] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &zyd_intr_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, + + [ZYD_TR_INTR_CS_RD] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.flags = {}, + .mh.callback = &zyd_intr_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ + }, +}; + +static devclass_t zyd_devclass; + +static device_method_t zyd_methods[] = { + DEVMETHOD(device_probe, zyd_probe), + DEVMETHOD(device_attach, zyd_attach), + DEVMETHOD(device_detach, zyd_detach), + {0, 0} +}; + +static driver_t zyd_driver = { + .name = "zyd", + .methods = zyd_methods, + .size = sizeof(struct zyd_softc), +}; + +DRIVER_MODULE(zyd, ushub, zyd_driver, zyd_devclass, NULL, 0); +MODULE_DEPEND(zyd, usb2_wlan, 1, 1, 1); +MODULE_DEPEND(zyd, usb2_core, 1, 1, 1); +MODULE_DEPEND(zyd, wlan, 1, 1, 1); +MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1); + +static uint8_t +zyd_plcp2ieee(uint8_t signal, uint8_t isofdm) +{ + if (isofdm) { + static const uint8_t ofdmrates[16] = + {0, 0, 0, 0, 0, 0, 0, 96, 48, 24, 12, 108, 72, 36, 18}; + + return ofdmrates[signal & 0xf]; + } else { + static const uint8_t cckrates[16] = + {0, 0, 0, 0, 4, 0, 0, 11, 0, 0, 2, 0, 0, 0, 22, 0}; + + return cckrates[signal & 0xf]; + } +} + +/* + * USB request basic wrapper + */ +static void +zyd_cfg_usbrequest(struct zyd_softc *sc, struct usb2_device_request *req, uint8_t *data) +{ + usb2_error_t err; + uint16_t length; + + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto error; + } + err = usb2_do_request_flags + (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); + + if (err) { + + DPRINTFN(0, "%s: device request failed, err=%s " + "(ignored)\n", sc->sc_name, usb2_errstr(err)); + +error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static void +zyd_intr_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_RD]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ZYD_FLAG_INTR_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/* + * Callback handler for interrupt transfer + */ +static void +zyd_intr_read_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct zyd_cmd *cmd = &sc->sc_intr_ibuf; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + actlen = xfer->actlen; + + DPRINTFN(3, "length=%d\n", actlen); + + if (actlen > sizeof(sc->sc_intr_ibuf)) { + actlen = sizeof(sc->sc_intr_ibuf); + } + usb2_copy_out(xfer->frbuffers, 0, + &sc->sc_intr_ibuf, actlen); + + switch (cmd->code) { + case htole16(ZYD_NOTIF_RETRYSTATUS): + goto handle_notif_retrystatus; + case htole16(ZYD_NOTIF_IORD): + goto handle_notif_iord; + default: + DPRINTFN(2, "unknown indication: 0x%04x\n", + le16toh(cmd->code)); + } + + /* fallthrough */ + + case USB_ST_SETUP: +tr_setup: + if (sc->sc_flags & ZYD_FLAG_INTR_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); + break; + } + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTFN(3, "error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ZYD_FLAG_INTR_READ_STALL; + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); + } + break; + } + return; + +handle_notif_retrystatus:{ + + struct zyd_notif_retry *retry = (void *)(cmd->data); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + DPRINTF("retry intr: rate=0x%x " + "addr=%02x:%02x:%02x:%02x:%02x:%02x count=%d (0x%x)\n", + le16toh(retry->rate), retry->macaddr[0], retry->macaddr[1], + retry->macaddr[2], retry->macaddr[3], retry->macaddr[4], + retry->macaddr[5], le16toh(retry->count) & 0xff, + le16toh(retry->count)); + + vap = zyd_get_vap(sc); + if ((vap != NULL) && (sc->sc_amrr_timer)) { + /* + * 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 (retry->count & htole16(0x100)) { + ifp->if_oerrors++; /* too many retries */ + } + goto tr_setup; + } + +handle_notif_iord: + + if (*(uint16_t *)cmd->data == htole16(ZYD_CR_INTERRUPT)) { + goto tr_setup; /* HMAC interrupt */ + } + if (actlen < 4) { + DPRINTFN(0, "too short, %u bytes\n", actlen); + goto tr_setup; /* too short */ + } + actlen -= 4; + + sc->sc_intr_ilen = actlen; + + if (sc->sc_intr_iwakeup) { + sc->sc_intr_iwakeup = 0; + usb2_cv_signal(&sc->sc_intr_cv); + } else { + sc->sc_intr_iwakeup = 1; + } + /* + * We pause reading data from the interrupt endpoint until the + * data has been picked up! + */ + return; +} + +/* + * Interrupt call reply transfer, read + */ +static void +zyd_cfg_usb2_intr_read(struct zyd_softc *sc, void *data, uint32_t size) +{ + uint16_t actlen; + uint16_t x; + + if (size > sizeof(sc->sc_intr_ibuf.data)) { + DPRINTFN(0, "truncating transfer size!\n"); + size = sizeof(sc->sc_intr_ibuf.data); + } + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + bzero(data, size); + goto done; + } + if (sc->sc_intr_iwakeup) { + DPRINTF("got data already!\n"); + sc->sc_intr_iwakeup = 0; + goto skip0; + } +repeat: + sc->sc_intr_iwakeup = 1; + + while (sc->sc_intr_iwakeup) { + + /* wait for data */ + + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); + + if (usb2_cv_timedwait(&sc->sc_intr_cv, + &sc->sc_mtx, hz / 2)) { + /* should not happen */ + } + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + bzero(data, size); + goto done; + } + } +skip0: + if (size != sc->sc_intr_ilen) { + DPRINTFN(0, "unexpected length %u != %u\n", + size, sc->sc_intr_ilen); + goto repeat; + } + actlen = sc->sc_intr_ilen; + actlen /= 4; + + /* verify register values */ + for (x = 0; x != actlen; x++) { + if (sc->sc_intr_obuf.data[(2 * x)] != + sc->sc_intr_ibuf.data[(4 * x)]) { + /* invalid register */ + DPRINTFN(0, "Invalid register (1) at %u!\n", x); + goto repeat; + } + if (sc->sc_intr_obuf.data[(2 * x) + 1] != + sc->sc_intr_ibuf.data[(4 * x) + 1]) { + /* invalid register */ + DPRINTFN(0, "Invalid register (2) at %u!\n", x); + goto repeat; + } + } + + bcopy(sc->sc_intr_ibuf.data, data, size); + + /* + * We have fetched the data from the shared buffer and it is + * safe to restart the interrupt transfer! + */ + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); +done: + return; +} + +static void +zyd_intr_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_WR]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ZYD_FLAG_INTR_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +zyd_intr_write_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(3, "length=%d\n", xfer->actlen); + goto wakeup; + + case USB_ST_SETUP: + + if (sc->sc_flags & ZYD_FLAG_INTR_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); + goto wakeup; + } + if (sc->sc_intr_owakeup) { + usb2_copy_in(xfer->frbuffers, 0, &sc->sc_intr_obuf, + sc->sc_intr_olen); + + xfer->frlengths[0] = sc->sc_intr_olen; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + DPRINTFN(3, "error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ZYD_FLAG_INTR_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); + } + goto wakeup; + } + return; + +wakeup: + if (sc->sc_intr_owakeup) { + sc->sc_intr_owakeup = 0; + usb2_cv_signal(&sc->sc_intr_cv); + } + return; +} + +/* + * Interrupt transfer, write. + * + * Not always an "interrupt transfer". If operating in + * full speed mode, EP4 is bulk out, not interrupt out. + */ +static void +zyd_cfg_usb2_intr_write(struct zyd_softc *sc, const void *data, + uint16_t code, uint32_t size) +{ + if (size > sizeof(sc->sc_intr_obuf.data)) { + DPRINTFN(0, "truncating transfer size!\n"); + size = sizeof(sc->sc_intr_obuf.data); + } + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + goto done; + } + sc->sc_intr_olen = size + 2; + sc->sc_intr_owakeup = 1; + + sc->sc_intr_obuf.code = htole16(code); + bcopy(data, sc->sc_intr_obuf.data, size); + + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_WR]); + + while (sc->sc_intr_owakeup) { + if (usb2_cv_timedwait(&sc->sc_intr_cv, + &sc->sc_mtx, hz / 2)) { + /* should not happen */ + } + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + sc->sc_intr_owakeup = 0; + goto done; + } + } +done: + return; +} + +static void +zyd_cfg_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, uint16_t ilen, + void *odata, uint16_t olen, uint16_t flags) +{ + zyd_cfg_usb2_intr_write(sc, idata, code, ilen); + + if (flags & ZYD_CMD_FLAG_READ) { + zyd_cfg_usb2_intr_read(sc, odata, olen); + } + return; +} + +static void +zyd_cfg_read16(struct zyd_softc *sc, uint16_t addr, uint16_t *value) +{ + struct zyd_pair tmp[1]; + + addr = htole16(addr); + zyd_cfg_cmd(sc, ZYD_CMD_IORD, &addr, sizeof(addr), + tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); + *value = le16toh(tmp[0].val); + return; +} + +static void +zyd_cfg_read32(struct zyd_softc *sc, uint16_t addr, uint32_t *value) +{ + struct zyd_pair tmp[2]; + uint16_t regs[2]; + + regs[0] = ZYD_REG32_HI(addr); + regs[1] = ZYD_REG32_LO(addr); + regs[0] = htole16(regs[0]); + regs[1] = htole16(regs[1]); + + zyd_cfg_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), + tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); + *value = (le16toh(tmp[0].val) << 16) | le16toh(tmp[1].val); + return; +} + +static void +zyd_cfg_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) +{ + struct zyd_pair pair[1]; + + pair[0].reg = htole16(reg); + pair[0].val = htole16(val); + + zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); + return; +} + +static void +zyd_cfg_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); + + zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); + return; +} + +/*------------------------------------------------------------------------* + * zyd_cfg_rfwrite - write RF registers + *------------------------------------------------------------------------*/ +static void +zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value) +{ + struct zyd_rf *rf = &sc->sc_rf; + struct zyd_rfwrite req; + uint16_t cr203; + uint16_t i; + + zyd_cfg_read16(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 (value & (1 << (rf->width - 1 - i))) + req.bit[i] |= htole16(ZYD_RF_DATA); + } + zyd_cfg_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + (2 * rf->width), NULL, 0, 0); + return; +} + +static void +zyd_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_RD]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ZYD_FLAG_BULK_READ_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +static void +zyd_bulk_read_callback_sub(struct usb2_xfer *xfer, struct zyd_ifq *mq, + uint32_t offset, uint16_t len) +{ + enum { + ZYD_OVERHEAD = (ZYD_HW_PADDING + IEEE80211_CRC_LEN), + }; + struct zyd_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct zyd_plcphdr plcp; + struct zyd_rx_stat stat; + struct mbuf *m; + + if (len < ZYD_OVERHEAD) { + DPRINTF("frame too " + "short (length=%d)\n", len); + ifp->if_ierrors++; + return; + } + usb2_copy_out(xfer->frbuffers, offset, &plcp, sizeof(plcp)); + usb2_copy_out(xfer->frbuffers, offset + len - sizeof(stat), + &stat, sizeof(stat)); + + if (stat.flags & ZYD_RX_ERROR) { + DPRINTF("RX status indicated " + "error (0x%02x)\n", stat.flags); + ifp->if_ierrors++; + return; + } + /* compute actual frame length */ + len -= ZYD_OVERHEAD; + + /* allocate a mbuf to store the frame */ + if (len > MCLBYTES) { + DPRINTF("too large frame, " + "%u bytes\n", len); + return; + } else if (len > MHLEN) + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_DONTWAIT, MT_DATA); + + if (m == NULL) { + DPRINTF("could not allocate rx mbuf\n"); + ifp->if_ierrors++; + return; + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + m->m_len = len; + + usb2_copy_out(xfer->frbuffers, offset + + sizeof(plcp), m->m_data, len); + + 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 = + zyd_plcp2ieee(plcp.signal, stat.flags & ZYD_RX_OFDM); + tap->wr_antsignal = stat.rssi + -95; + tap->wr_antnoise = -95; /* XXX */ + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + if (sizeof(m->m_hdr.pad) > 0) { + m->m_hdr.pad[0] = stat.rssi; /* XXX hack */ + } + _IF_ENQUEUE(mq, m); + + return; +} + +static void +zyd_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_node *ni; + struct zyd_rx_desc rx_desc; + struct zyd_ifq mq = {NULL, NULL, 0}; + struct mbuf *m; + uint32_t offset; + uint16_t len16; + uint8_t x; + uint8_t rssi; + int8_t nf; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < MAX(sizeof(rx_desc), ZYD_MIN_FRAGSZ)) { + DPRINTFN(0, "xfer too short, %d bytes\n", xfer->actlen); + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(rx_desc), + &rx_desc, sizeof(rx_desc)); + + if (UGETW(rx_desc.tag) == ZYD_TAG_MULTIFRAME) { + + offset = 0; + + DPRINTFN(4, "received multi-frame transfer, " + "%u bytes\n", xfer->actlen); + + for (x = 0; x < ZYD_MAX_RXFRAMECNT; x++) { + len16 = UGETW(rx_desc.len[x]); + + if ((len16 == 0) || (len16 > xfer->actlen)) { + break; + } + zyd_bulk_read_callback_sub(xfer, &mq, offset, len16); + + /* + * next frame is aligned on a 32-bit + * boundary + */ + len16 = (len16 + 3) & ~3; + offset += len16; + if (len16 > xfer->actlen) { + break; + } + xfer->actlen -= len16; + } + } else { + DPRINTFN(4, "received single-frame transfer, " + "%u bytes\n", xfer->actlen); + zyd_bulk_read_callback_sub(xfer, &mq, 0, xfer->actlen); + } + + case USB_ST_SETUP: +tr_setup: + DPRINTF("setup\n"); + + if (sc->sc_flags & ZYD_FLAG_BULK_READ_STALL) { + usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + if (mq.ifq_head) { + + mtx_unlock(&sc->sc_mtx); + + while (1) { + + _IF_DEQUEUE(&mq, m); + + if (m == NULL) + break; + + rssi = m->m_hdr.pad[0]; /* XXX hack */ + + rssi = (rssi > 63) ? 127 : 2 * rssi; + nf = -95; /* XXX */ + + ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + if (ieee80211_input(ni, m, rssi, nf, 0)) { + /* ignore */ + } + ieee80211_free_node(ni); + } else { + if (ieee80211_input_all(ic, m, rssi, nf, 0)) { + /* ignore */ + } + } + } + + mtx_lock(&sc->sc_mtx); + } + break; + + default: /* Error */ + DPRINTF("frame error: %s\n", usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ZYD_FLAG_BULK_READ_STALL; + usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); + } + break; + } + return; +} + +/*------------------------------------------------------------------------* + * zyd_cfg_uploadfirmware + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +zyd_cfg_uploadfirmware(struct zyd_softc *sc, const uint8_t *fw_ptr, + uint32_t fw_len) +{ + struct usb2_device_request req; + uint16_t temp; + uint16_t addr; + uint8_t stat; + + DPRINTF("firmware %p size=%u\n", fw_ptr, fw_len); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = ZYD_DOWNLOADREQ; + USETW(req.wIndex, 0); + + temp = 64; + + addr = ZYD_FIRMWARE_START_ADDR; + while (fw_len > 0) { + + if (fw_len < 64) { + temp = fw_len; + } + DPRINTF("firmware block: fw_len=%u\n", fw_len); + + USETW(req.wValue, addr); + USETW(req.wLength, temp); + + zyd_cfg_usbrequest(sc, &req, + USB_ADD_BYTES(fw_ptr, 0)); + + addr += (temp / 2); + fw_len -= temp; + fw_ptr += temp; + } + + /* 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)); + + zyd_cfg_usbrequest(sc, &req, &stat); + + return ((stat & 0x80) ? 1 : 0); +} + +/* + * Driver OS interface + */ + +/* + * Probe for a ZD1211-containing product + */ +static int +zyd_probe(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != 0) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do + * setup and ethernet/BPF attach. + */ +static int +zyd_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct zyd_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + + if (sc == NULL) { + return (ENOMEM); + } + if (uaa->info.bcdDevice < 0x4330) { + device_printf(dev, "device version mismatch: 0x%X " + "(only >= 43.30 supported)\n", + uaa->info.bcdDevice); + return (EINVAL); + } + device_set_usb2_desc(dev); + + snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", + device_get_nameunit(dev)); + + sc->sc_unit = device_get_unit(dev); + sc->sc_udev = uaa->device; + sc->sc_mac_rev = USB_GET_DRIVER_INFO(uaa); + + mtx_init(&sc->sc_mtx, "zyd lock", MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + + usb2_cv_init(&sc->sc_intr_cv, "IWAIT"); + + usb2_callout_init_mtx(&sc->sc_watchdog, + &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + + /* + * Endpoint 1 = Bulk out (512b @ high speed / 64b @ full speed) + * Endpoint 2 = Bulk in (512b @ high speed / 64b @ full speed) + * Endpoint 3 = Intr in (64b) + * Endpoint 4 = Intr out @ high speed / bulk out @ full speed (64b) + */ + iface_index = ZYD_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, zyd_config, ZYD_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB " + "transfers: %s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, + &zyd_end_of_commands, sizeof(struct usb2_config_td_cc), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + mtx_lock(&sc->sc_mtx); + + /* start setup */ + + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, &zyd_cfg_first_time_setup, 0, 0); + + /* start watchdog (will exit mutex) */ + + zyd_watchdog(sc); + + return (0); + +detach: + zyd_detach(dev); + return (ENXIO); +} + +/* + * Lock PHY registers + */ +static void +zyd_cfg_lock_phy(struct zyd_softc *sc) +{ + uint32_t temp; + + zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); + temp &= ~ZYD_UNLOCK_PHY_REGS; + zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); +} + +/* + * Unlock PHY registers + */ +static void +zyd_cfg_unlock_phy(struct zyd_softc *sc) +{ + uint32_t temp; + + zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); + temp |= ZYD_UNLOCK_PHY_REGS; + zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); +} + +static void +zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t bintval) +{ + /* XXX this is probably broken.. */ + zyd_cfg_write32(sc, ZYD_CR_ATIM_WND_PERIOD, bintval - 2); + zyd_cfg_write32(sc, ZYD_CR_PRE_TBTT, bintval - 1); + zyd_cfg_write32(sc, ZYD_CR_BCN_INTERVAL, bintval); + return; +} + +/* + * Get RF name + */ +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", + "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", + "PHILIPS" + }; + + return (zyd_rfs[(type > 15) ? 0 : type]); +} + +/* + * RF driver: Init for RFMD chip + */ +static void +zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; + static const uint32_t rfini[] = ZYD_RFMD_RF; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + + /* init RFMD radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + return; +} + +/* + * RF driver: Switch radio on/off for RFMD chip + */ +static void +zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + zyd_cfg_write16(sc, ZYD_CR10, on ? 0x89 : 0x15); + zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x81); + return; +} + +/* + * RF driver: Channel setting for RFMD chip + */ +static void +zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_RFMD_CHANTABLE; + + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); + return; +} + +/* + * RF driver: Switch radio on/off for AL2230 chip + */ +static void +zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + uint8_t on251 = (sc->sc_mac_rev == ZYD_ZD1211) ? 0x3f : 0x7f; + + zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_cfg_write16(sc, ZYD_CR251, on ? on251 : 0x2f); + return; +} + +/* + * RF driver: Init for AL2230 chip + */ +static void +zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; + static const uint32_t rfini[] = ZYD_AL2230_RF; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + + /* init AL2230 radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + return; +} + +static void +zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; + static const uint32_t rfini[] = ZYD_AL2230_RF_B; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + + /* init AL2230 radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + return; +} + +/* + * RF driver: Channel setting for AL2230 chip + */ +static void +zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE; + + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r3); + + zyd_cfg_write16(sc, ZYD_CR138, 0x28); + zyd_cfg_write16(sc, ZYD_CR203, 0x06); + return; +} + +/* + * AL7230B RF methods. + */ +static void +zyd_cfg_rf_al7230b_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_cfg_write16(sc, ZYD_CR251, on ? 0x3f : 0x2f); + return; +} + +static void +zyd_cfg_rf_al7230b_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + 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; + uint32_t i; + + /* for AL7230B, PHY and RF need to be initialized in "phases" */ + + /* init RF-dependent PHY registers, part one */ + for (i = 0; i != INDEXES(phyini_1); i++) { + zyd_cfg_write16(sc, phyini_1[i].reg, phyini_1[i].val); + } + /* init AL7230B radio, part one */ + for (i = 0; i != INDEXES(rfini_1); i++) { + zyd_cfg_rfwrite(sc, rfini_1[i]); + } + /* init RF-dependent PHY registers, part two */ + for (i = 0; i != INDEXES(phyini_2); i++) { + zyd_cfg_write16(sc, phyini_2[i].reg, phyini_2[i].val); + } + /* init AL7230B radio, part two */ + for (i = 0; i != INDEXES(rfini_2); i++) { + zyd_cfg_rfwrite(sc, rfini_2[i]); + } + /* init RF-dependent PHY registers, part three */ + for (i = 0; i != INDEXES(phyini_3); i++) { + zyd_cfg_write16(sc, phyini_3[i].reg, phyini_3[i].val); + } + return; +} + +static void +zyd_cfg_rf_al7230b_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_AL7230B_CHANTABLE; + static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; + uint32_t i; + + zyd_cfg_write16(sc, ZYD_CR240, 0x57); + zyd_cfg_write16(sc, ZYD_CR251, 0x2f); + + for (i = 0; i != INDEXES(rfsc); i++) { + zyd_cfg_rfwrite(sc, rfsc[i]); + } + + zyd_cfg_write16(sc, ZYD_CR128, 0x14); + zyd_cfg_write16(sc, ZYD_CR129, 0x12); + zyd_cfg_write16(sc, ZYD_CR130, 0x10); + zyd_cfg_write16(sc, ZYD_CR38, 0x38); + zyd_cfg_write16(sc, ZYD_CR136, 0xdf); + + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); + zyd_cfg_rfwrite(sc, 0x3c9000); + + zyd_cfg_write16(sc, ZYD_CR251, 0x3f); + zyd_cfg_write16(sc, ZYD_CR203, 0x06); + zyd_cfg_write16(sc, ZYD_CR240, 0x08); + + return; +} + +/* + * AL2210 RF methods. + */ +static void +zyd_cfg_rf_al2210_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + +} + +static void +zyd_cfg_rf_al2210_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; + static const uint32_t rfini[] = ZYD_AL2210_RF; + uint32_t tmp; + uint32_t i; + + zyd_cfg_write32(sc, ZYD_CR18, 2); + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + /* init AL2210 radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + zyd_cfg_write16(sc, ZYD_CR47, 0x1e); + zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); + zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); + zyd_cfg_write16(sc, ZYD_CR47, 0x1e); + zyd_cfg_write32(sc, ZYD_CR18, 3); + + return; +} + +static void +zyd_cfg_rf_al2210_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; + uint32_t tmp; + + zyd_cfg_write32(sc, ZYD_CR18, 2); + zyd_cfg_write16(sc, ZYD_CR47, 0x1e); + zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); + + zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); + zyd_cfg_write16(sc, ZYD_CR47, 0x1e); + + /* actually set the channel */ + zyd_cfg_rfwrite(sc, rfprog[channel - 1]); + + zyd_cfg_write32(sc, ZYD_CR18, 3); + return; +} + +/* + * GCT RF methods. + */ +static void +zyd_cfg_rf_gct_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + /* vendor driver does nothing for this RF chip */ + + return; +} + +static void +zyd_cfg_rf_gct_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; + static const uint32_t rfini[] = ZYD_GCT_RF; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + /* init cgt radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + return; +} + +static void +zyd_cfg_rf_gct_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; + + zyd_cfg_rfwrite(sc, 0x1c0000); + zyd_cfg_rfwrite(sc, rfprog[channel - 1]); + zyd_cfg_rfwrite(sc, 0x1c0008); + + return; +} + +/* + * Maxim RF methods. + */ +static void +zyd_cfg_rf_maxim_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + /* vendor driver does nothing for this RF chip */ + + return; +} + +static void +zyd_cfg_rf_maxim_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; + static const uint32_t rfini[] = ZYD_MAXIM_RF; + uint16_t tmp; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* init maxim radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); + + return; +} + +static void +zyd_cfg_rf_maxim_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + 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; + uint32_t i; + + /* + * 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 != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* first two values taken from the chantables */ + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); + + /* init maxim radio - skipping the two first values */ + if (INDEXES(rfini) > 2) { + for (i = 2; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); + + return; +} + +/* + * Maxim2 RF methods. + */ +static void +zyd_cfg_rf_maxim2_switch_radio(struct zyd_softc *sc, uint8_t on) +{ + /* vendor driver does nothing for this RF chip */ + return; +} + +static void +zyd_cfg_rf_maxim2_init(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; + static const uint32_t rfini[] = ZYD_MAXIM2_RF; + uint16_t tmp; + uint32_t i; + + /* init RF-dependent PHY registers */ + for (i = 0; i != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* init maxim2 radio */ + for (i = 0; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); + return; +} + +static void +zyd_cfg_rf_maxim2_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, + uint8_t channel) +{ + 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; + uint32_t i; + + /* + * 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 != INDEXES(phyini); i++) { + zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* first two values taken from the chantables */ + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); + zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); + + /* init maxim2 radio - skipping the two first values */ + if (INDEXES(rfini) > 2) { + for (i = 2; i != INDEXES(rfini); i++) { + zyd_cfg_rfwrite(sc, rfini[i]); + } + } + zyd_cfg_read16(sc, ZYD_CR203, &tmp); + zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); + return; +} + +/* + * Assign drivers and init the RF + */ +static uint8_t +zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf) +{ + ; /* fix for indent */ + + switch (sc->sc_rf_rev) { + case ZYD_RF_RFMD: + rf->cfg_init_hw = zyd_cfg_rf_rfmd_init; + rf->cfg_switch_radio = zyd_cfg_rf_rfmd_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_rfmd_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2230: + if (sc->sc_mac_rev == ZYD_ZD1211B) + rf->cfg_init_hw = zyd_cfg_rf_al2230_init_b; + else + rf->cfg_init_hw = zyd_cfg_rf_al2230_init; + rf->cfg_switch_radio = zyd_cfg_rf_al2230_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL7230B: + rf->cfg_init_hw = zyd_cfg_rf_al7230b_init; + rf->cfg_switch_radio = zyd_cfg_rf_al7230b_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_al7230b_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2210: + rf->cfg_init_hw = zyd_cfg_rf_al2210_init; + rf->cfg_switch_radio = zyd_cfg_rf_al2210_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_al2210_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_GCT: + rf->cfg_init_hw = zyd_cfg_rf_gct_init; + rf->cfg_switch_radio = zyd_cfg_rf_gct_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_gct_set_channel; + rf->width = 21; /* 21-bit RF values */ + break; + case ZYD_RF_MAXIM_NEW: + rf->cfg_init_hw = zyd_cfg_rf_maxim_init; + rf->cfg_switch_radio = zyd_cfg_rf_maxim_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_maxim_set_channel; + rf->width = 18; /* 18-bit RF values */ + break; + case ZYD_RF_MAXIM_NEW2: + rf->cfg_init_hw = zyd_cfg_rf_maxim2_init; + rf->cfg_switch_radio = zyd_cfg_rf_maxim2_switch_radio; + rf->cfg_set_channel = zyd_cfg_rf_maxim2_set_channel; + rf->width = 18; /* 18-bit RF values */ + break; + default: + DPRINTFN(0, "%s: Sorry, radio %s is not supported yet\n", + sc->sc_name, zyd_rf_name(sc->sc_rf_rev)); + return (1); + } + + zyd_cfg_lock_phy(sc); + (rf->cfg_init_hw) (sc, rf); + zyd_cfg_unlock_phy(sc); + + return (0); /* success */ +} + +/* + * Init the hardware + */ +static uint8_t +zyd_cfg_hw_init(struct zyd_softc *sc) +{ + const struct zyd_phy_pair *phyp; + uint32_t tmp; + + /* specify that the plug and play is finished */ + zyd_cfg_write32(sc, ZYD_MAC_AFTER_PNP, 1); + + zyd_cfg_read16(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_firmware_base); + DPRINTF("firmware base address=0x%04x\n", sc->sc_firmware_base); + + /* retrieve firmware revision number */ + zyd_cfg_read16(sc, sc->sc_firmware_base + ZYD_FW_FIRMWARE_REV, &sc->sc_fw_rev); + + zyd_cfg_write32(sc, ZYD_CR_GPI_EN, 0); + zyd_cfg_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); + + /* disable interrupts */ + zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); + + /* PHY init */ + zyd_cfg_lock_phy(sc); + phyp = (sc->sc_mac_rev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; + for (; phyp->reg != 0; phyp++) { + zyd_cfg_write16(sc, phyp->reg, phyp->val); + } + if (sc->sc_fix_cr157) { + zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); + zyd_cfg_write32(sc, ZYD_CR157, tmp >> 8); + } + zyd_cfg_unlock_phy(sc); + + /* HMAC init */ + zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020); + zyd_cfg_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); + + if (sc->sc_mac_rev == ZYD_ZD1211) { + zyd_cfg_write32(sc, ZYD_MAC_RETRY, 0x00000002); + } else { + zyd_cfg_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); + zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); + zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); + zyd_cfg_write32(sc, ZYD_MACB_TXOP, 0x01800824); + } + + zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, 0x00000000); + zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0x00000000); + zyd_cfg_write32(sc, ZYD_MAC_GHTBL, 0x00000000); + zyd_cfg_write32(sc, ZYD_MAC_GHTBH, 0x80000000); + zyd_cfg_write32(sc, ZYD_MAC_MISC, 0x000000a4); + zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); + zyd_cfg_write32(sc, ZYD_MAC_BCNCFG, 0x00f00401); + zyd_cfg_write32(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); + zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080); + zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); + zyd_cfg_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); + zyd_cfg_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032); + zyd_cfg_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); + zyd_cfg_write32(sc, ZYD_CR_PS_CTRL, 0x10000000); + zyd_cfg_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); + zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); + zyd_cfg_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); + + /* init beacon interval to 100ms */ + zyd_cfg_set_beacon_interval(sc, 100); + + return (0); /* success */ +} + +/* + * Read information from EEPROM + */ +static void +zyd_cfg_read_eeprom(struct zyd_softc *sc) +{ + uint32_t tmp; + uint16_t i; + uint16_t val; + + /* read MAC address */ + zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P1, &tmp); + sc->sc_myaddr[0] = tmp & 0xff; + sc->sc_myaddr[1] = tmp >> 8; + sc->sc_myaddr[2] = tmp >> 16; + sc->sc_myaddr[3] = tmp >> 24; + zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp); + sc->sc_myaddr[4] = tmp & 0xff; + sc->sc_myaddr[5] = tmp >> 8; + + zyd_cfg_read32(sc, ZYD_EEPROM_POD, &tmp); + sc->sc_rf_rev = tmp & 0x0f; + sc->sc_fix_cr47 = (tmp >> 8) & 0x01; + sc->sc_fix_cr157 = (tmp >> 13) & 0x01; + sc->sc_pa_rev = (tmp >> 16) & 0x0f; + + /* read regulatory domain (currently unused) */ + zyd_cfg_read32(sc, ZYD_EEPROM_SUBID, &tmp); + sc->sc_regdomain = tmp >> 16; + DPRINTF("regulatory domain %x\n", sc->sc_regdomain); + + /* read Tx power calibration tables */ + for (i = 0; i < 7; i++) { + zyd_cfg_read16(sc, ZYD_EEPROM_PWR_CAL + i, &val); + sc->sc_pwr_cal[(i * 2)] = val >> 8; + sc->sc_pwr_cal[(i * 2) + 1] = val & 0xff; + + zyd_cfg_read16(sc, ZYD_EEPROM_PWR_INT + i, &val); + sc->sc_pwr_int[(i * 2)] = val >> 8; + sc->sc_pwr_int[(i * 2) + 1] = val & 0xff; + + zyd_cfg_read16(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_cfg_read16(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_cfg_read16(sc, ZYD_EEPROM_54M_CAL + i, &val); + sc->sc_ofdm54_cal[(i * 2)] = val >> 8; + sc->sc_ofdm54_cal[(i * 2) + 1] = val & 0xff; + } + return; +} + +static void +zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr) +{ + uint32_t tmp; + + tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + zyd_cfg_write32(sc, ZYD_MAC_MACADRL, tmp); + + tmp = (addr[5] << 8) | addr[4]; + zyd_cfg_write32(sc, ZYD_MAC_MACADRH, tmp); + return; +} + +/* + * Switch radio on/off + */ +static void +zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff) +{ + zyd_cfg_lock_phy(sc); + (sc->sc_rf.cfg_switch_radio) (sc, onoff); + zyd_cfg_unlock_phy(sc); + + return; +} + +/* + * Set BSSID + */ +static void +zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr) +{ + uint32_t tmp; + + tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + zyd_cfg_write32(sc, ZYD_MAC_BSSADRL, tmp); + + tmp = (addr[5] << 8) | addr[4]; + zyd_cfg_write32(sc, ZYD_MAC_BSSADRH, tmp); + return; +} + +/* + * Complete the attach process + */ +static void +zyd_cfg_first_time_setup(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct usb2_config_descriptor *cd; + struct ieee80211com *ic; + struct ifnet *ifp; + const uint8_t *fw_ptr; + uint32_t fw_len; + uint8_t bands; + usb2_error_t err; + + /* setup RX tap header */ + 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); + + /* setup TX tap header */ + 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 (sc->sc_mac_rev == ZYD_ZD1211) { + fw_ptr = zd1211_firmware; + fw_len = sizeof(zd1211_firmware); + } else { + fw_ptr = zd1211b_firmware; + fw_len = sizeof(zd1211b_firmware); + } + + if (zyd_cfg_uploadfirmware(sc, fw_ptr, fw_len)) { + DPRINTFN(0, "%s: could not " + "upload firmware!\n", sc->sc_name); + return; + } + cd = usb2_get_config_descriptor(sc->sc_udev); + + /* reset device */ + err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) { + DPRINTF("reset failed (ignored)\n"); + } + /* Read MAC and other stuff rom EEPROM */ + zyd_cfg_read_eeprom(sc); + + /* Init hardware */ + if (zyd_cfg_hw_init(sc)) { + DPRINTFN(0, "%s: HW init failed!\n", sc->sc_name); + return; + } + /* Now init the RF chip */ + if (zyd_cfg_rf_init_hw(sc, &sc->sc_rf)) { + DPRINTFN(0, "%s: RF init failed!\n", sc->sc_name); + return; + } + printf("%s: HMAC ZD1211%s, FW %02x.%02x, RF %s, PA %x, address %02x:%02x:%02x:%02x:%02x:%02x\n", + sc->sc_name, (sc->sc_mac_rev == ZYD_ZD1211) ? "" : "B", + sc->sc_fw_rev >> 8, sc->sc_fw_rev & 0xff, zyd_rf_name(sc->sc_rf_rev), + sc->sc_pa_rev, sc->sc_myaddr[0], + sc->sc_myaddr[1], sc->sc_myaddr[2], + sc->sc_myaddr[3], sc->sc_myaddr[4], + sc->sc_myaddr[5]); + + mtx_unlock(&sc->sc_mtx); + + ifp = if_alloc(IFT_IEEE80211); + + mtx_lock(&sc->sc_mtx); + + if (ifp == NULL) { + DPRINTFN(0, "%s: could not if_alloc()!\n", + sc->sc_name); + goto done; + } + sc->sc_evilhack = ifp; + sc->sc_ifp = ifp; + ic = ifp->if_l2com; + + ifp->if_softc = sc; + if_initname(ifp, "zyd", sc->sc_unit); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = &zyd_init_cb; + ifp->if_ioctl = &zyd_ioctl_cb; + ifp->if_start = &zyd_start_cb; + ifp->if_watchdog = NULL; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); + + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; + ic->ic_opmode = IEEE80211_M_STA; + + /* Set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | 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); + + mtx_unlock(&sc->sc_mtx); + + ieee80211_ifattach(ic); + + mtx_lock(&sc->sc_mtx); + + ic->ic_node_alloc = &zyd_node_alloc_cb; + ic->ic_raw_xmit = &zyd_raw_xmit_cb; + ic->ic_newassoc = &zyd_newassoc_cb; + + ic->ic_scan_start = &zyd_scan_start_cb; + ic->ic_scan_end = &zyd_scan_end_cb; + ic->ic_set_channel = &zyd_set_channel_cb; + ic->ic_vap_create = &zyd_vap_create; + ic->ic_vap_delete = &zyd_vap_delete; + ic->ic_update_mcast = &zyd_update_mcast_cb; + ic->ic_update_promisc = &zyd_update_promisc_cb; + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + mtx_unlock(&sc->sc_mtx); + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + + sizeof(sc->sc_txtap)); + + mtx_lock(&sc->sc_mtx); + + if (bootverbose) { + ieee80211_announce(ic); + } + usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); +done: + return; +} + +/* + * Detach device + */ +static int +zyd_detach(device_t dev) +{ + struct zyd_softc *sc = device_get_softc(dev); + struct ieee80211com *ic; + struct ifnet *ifp; + + usb2_config_td_drain(&sc->sc_config_td); + + mtx_lock(&sc->sc_mtx); + + usb2_callout_stop(&sc->sc_watchdog); + + zyd_cfg_pre_stop(sc, NULL, 0); + + ifp = sc->sc_ifp; + ic = ifp->if_l2com; + + mtx_unlock(&sc->sc_mtx); + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); + + /* get rid of any late children */ + bus_generic_detach(dev); + + if (ifp) { + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + usb2_config_td_unsetup(&sc->sc_config_td); + + usb2_callout_drain(&sc->sc_watchdog); + + usb2_cv_destroy(&sc->sc_intr_cv); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +zyd_cfg_newstate(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct zyd_vap *uvp = ZYD_VAP(vap); + enum ieee80211_state ostate; + enum ieee80211_state nstate; + int arg; + + ostate = vap->iv_state; + nstate = sc->sc_ns_state; + arg = sc->sc_ns_arg; + + switch (nstate) { + case IEEE80211_S_INIT: + break; + + case IEEE80211_S_RUN: + zyd_cfg_set_run(sc, cc); + break; + + default: + break; + } + + mtx_unlock(&sc->sc_mtx); + IEEE80211_LOCK(ic); + uvp->newstate(vap, nstate, arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + mtx_lock(&sc->sc_mtx); + return; +} + +static void +zyd_cfg_set_run(struct zyd_softc *sc, + struct usb2_config_td_cc *cc) +{ + zyd_cfg_set_chan(sc, cc, 0); + + if (cc->ic_opmode != IEEE80211_M_MONITOR) { + /* turn link LED on */ + zyd_cfg_set_led(sc, ZYD_LED1, 1); + + /* make data LED blink upon Tx */ + zyd_cfg_write32(sc, sc->sc_firmware_base + ZYD_FW_LINK_STATUS, 1); + + zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + } + if (cc->iv_bss.fixed_rate_none) { + /* enable automatic rate adaptation */ + zyd_cfg_amrr_start(sc); + } + return; +} + +static int +zyd_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct zyd_vap *uvp = ZYD_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF("setting new state: %d\n", nstate); + + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_is_gone(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + /* Special case which happens at detach. */ + if (nstate == IEEE80211_S_INIT) { + (uvp->newstate) (vap, nstate, arg); + } + return (0); /* nothing to do */ + } + /* store next state */ + sc->sc_ns_state = nstate; + sc->sc_ns_arg = arg; + + /* stop timers */ + sc->sc_amrr_timer = 0; + + /* + * USB configuration can only be done from the USB configuration + * thread: + */ + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_config_copy, + &zyd_cfg_newstate, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return EINPROGRESS; +} + +static void +zyd_cfg_update_promisc(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t low; + uint32_t high; + + if ((cc->ic_opmode == IEEE80211_M_MONITOR) || + (cc->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { + low = 0xffffffff; + high = 0xffffffff; + } else { + low = cc->zyd_multi_low; + high = cc->zyd_multi_high; + } + + /* reprogram multicast global hash table */ + zyd_cfg_write32(sc, ZYD_MAC_GHTBL, low); + zyd_cfg_write32(sc, ZYD_MAC_GHTBH, high); + return; +} + +/* + * Rate-to-bit-converter (Field "rate" in zyd_controlsetformat) + */ +static uint8_t +zyd_plcp_signal(uint8_t rate) +{ + ; /* fix for indent */ + + switch (rate) { + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + + /* 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); + + /* XXX unsupported/unknown rate */ + default: + return (0xff); + } +} + +static void +zyd_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) +{ + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_config_copy, func, 0, 0); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +zyd_scan_start_cb(struct ieee80211com *ic) +{ + zyd_std_command(ic, &zyd_cfg_scan_start); + return; +} + +static void +zyd_scan_end_cb(struct ieee80211com *ic) +{ + zyd_std_command(ic, &zyd_cfg_scan_end); + return; +} + +static void +zyd_set_channel_cb(struct ieee80211com *ic) +{ + zyd_std_command(ic, &zyd_cfg_set_chan); + return; +} + +/*========================================================================* + * configure sub-routines, zyd_cfg_xxx + *========================================================================*/ + +static void +zyd_cfg_scan_start(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + zyd_cfg_set_bssid(sc, cc->if_broadcastaddr); + return; +} + +static void +zyd_cfg_scan_end(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); + return; +} + +static void +zyd_cfg_set_chan(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t chan; + uint32_t tmp; + + chan = cc->ic_curchan.chan_to_ieee; + + DPRINTF("Will try %d\n", chan); + + if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { + DPRINTF("0 or ANY, exiting\n"); + return; + } + zyd_cfg_lock_phy(sc); + + (sc->sc_rf.cfg_set_channel) (sc, &sc->sc_rf, chan); + + /* update Tx power */ + zyd_cfg_write16(sc, ZYD_CR31, sc->sc_pwr_int[chan - 1]); + + if (sc->sc_mac_rev == ZYD_ZD1211B) { + zyd_cfg_write16(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); + zyd_cfg_write16(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); + zyd_cfg_write16(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); + + zyd_cfg_write16(sc, ZYD_CR68, sc->sc_pwr_cal[chan - 1]); + + zyd_cfg_write16(sc, ZYD_CR69, 0x28); + zyd_cfg_write16(sc, ZYD_CR69, 0x2a); + } + if (sc->sc_fix_cr47) { + /* set CCK baseband gain from EEPROM */ + zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); + zyd_cfg_write16(sc, ZYD_CR47, tmp & 0xff); + } + zyd_cfg_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0); + + zyd_cfg_unlock_phy(sc); + + sc->sc_rxtap.wr_chan_freq = + sc->sc_txtap.wt_chan_freq = + htole16(cc->ic_curchan.ic_freq); + + sc->sc_rxtap.wr_chan_flags = + sc->sc_txtap.wt_chan_flags = + htole16(cc->ic_flags); + + return; +} + +/* + * Interface: init + */ + +/* immediate configuration */ + +static void +zyd_cfg_pre_init(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + zyd_cfg_pre_stop(sc, cc, 0); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + sc->sc_flags |= ZYD_FLAG_HL_READY; + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + + return; +} + +/* delayed configuration */ + +static void +zyd_cfg_init(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + zyd_cfg_stop(sc, cc, 0); + + /* Do initial setup */ + + zyd_cfg_set_mac_addr(sc, cc->ic_myaddr); + + zyd_cfg_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); + + /* promiscuous mode */ + zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, + (cc->ic_opmode == IEEE80211_M_MONITOR) ? 1 : 0); + + /* multicast setup */ + zyd_cfg_update_promisc(sc, cc, refcount); + + zyd_cfg_set_rxfilter(sc, cc, refcount); + + /* switch radio transmitter ON */ + zyd_cfg_switch_radio(sc, 1); + + /* XXX wrong, can't set here */ + /* set basic rates */ + if (cc->ic_curmode == IEEE80211_MODE_11B) + zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x0003); + else if (cc->ic_curmode == IEEE80211_MODE_11A) + zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x1500); + else /* assumes 802.11b/g */ + zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x000f); + + /* set mandatory rates */ + if (cc->ic_curmode == IEEE80211_MODE_11B) + zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x000f); + else if (cc->ic_curmode == IEEE80211_MODE_11A) + zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x1500); + else /* assumes 802.11b/g */ + zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x150f); + + /* set default BSS channel */ + zyd_cfg_set_chan(sc, cc, 0); + + /* enable interrupts */ + zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); + + /* make sure that the transfers get started */ + sc->sc_flags |= ( + ZYD_FLAG_BULK_READ_STALL | + ZYD_FLAG_BULK_WRITE_STALL | + ZYD_FLAG_LL_READY); + + if ((sc->sc_flags & ZYD_FLAG_LL_READY) && + (sc->sc_flags & ZYD_FLAG_HL_READY)) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[1]); + usb2_transfer_start(sc->sc_xfer[0]); + + /* + * start IEEE802.11 layer + */ + mtx_unlock(&sc->sc_mtx); + ieee80211_start_all(ic); + mtx_lock(&sc->sc_mtx); + } + return; +} + +/* immediate configuration */ + +static void +zyd_cfg_pre_stop(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ifnet *ifp = sc->sc_ifp; + + if (cc) { + /* copy the needed configuration */ + zyd_config_copy(sc, cc, refcount); + } + if (ifp) { + /* clear flags */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + sc->sc_flags &= ~(ZYD_FLAG_HL_READY | + ZYD_FLAG_LL_READY); + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); + usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); + + /* clean up transmission */ + zyd_tx_clean_queue(sc); + return; +} + +/* delayed configuration */ + +static void +zyd_cfg_stop(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + /* switch radio transmitter OFF */ + zyd_cfg_switch_radio(sc, 0); + + /* disable Rx */ + zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0); + + /* disable interrupts */ + zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); + + return; +} + +static void +zyd_update_mcast_cb(struct ifnet *ifp) +{ + struct zyd_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_config_copy, + &zyd_cfg_update_promisc, 0, 0); + mtx_unlock(&sc->sc_mtx); + return; +} + +static void +zyd_update_promisc_cb(struct ifnet *ifp) +{ + struct zyd_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_config_copy, + &zyd_cfg_update_promisc, 0, 0); + mtx_unlock(&sc->sc_mtx); + return; +} + +static void +zyd_cfg_set_rxfilter(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + uint32_t rxfilter; + + switch (cc->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; + } + zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, rxfilter); + return; +} + +static void +zyd_cfg_set_led(struct zyd_softc *sc, uint32_t which, uint8_t on) +{ + uint32_t tmp; + + zyd_cfg_read32(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); + if (on) + tmp |= which; + else + tmp &= ~which; + + zyd_cfg_write32(sc, ZYD_MAC_TX_PE_CONTROL, tmp); + return; +} + +static void +zyd_start_cb(struct ifnet *ifp) +{ + struct zyd_softc *sc = ifp->if_softc; + + mtx_lock(&sc->sc_mtx); + usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); + mtx_unlock(&sc->sc_mtx); + return; +} + +static void +zyd_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_WR]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~ZYD_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(xfer_other); + } + return; +} + +/* + * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that + * should be freed, when "zyd_setup_desc_and_tx" is called. + */ +static void +zyd_setup_desc_and_tx(struct zyd_softc *sc, struct mbuf *m, + uint16_t rate) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct mbuf *mm; + enum ieee80211_phytype phytype; + uint16_t len; + uint16_t totlen; + uint16_t pktlen; + uint8_t remainder; + + if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { + /* free packet */ + zyd_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (!((sc->sc_flags & ZYD_FLAG_LL_READY) && + (sc->sc_flags & ZYD_FLAG_HL_READY))) { + /* free packet */ + zyd_tx_freem(m); + ifp->if_oerrors++; + return; + } + if (rate < 2) { + DPRINTF("rate < 2!\n"); + + /* avoid division by zero */ + rate = 2; + } + ic->ic_lastdata = ticks; + + 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, m); + } + len = m->m_pkthdr.len; + totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + phytype = ieee80211_rate2phytype(sc->sc_rates, rate); + + sc->sc_tx_desc.len = htole16(totlen); + sc->sc_tx_desc.phy = zyd_plcp_signal(rate); + if (phytype == IEEE80211_T_OFDM) { + sc->sc_tx_desc.phy |= ZYD_TX_PHY_OFDM; + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + sc->sc_tx_desc.phy |= ZYD_TX_PHY_5GHZ; + } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + sc->sc_tx_desc.phy |= ZYD_TX_PHY_SHPREAMBLE; + + /* actual transmit length (XXX why +10?) */ + pktlen = sizeof(struct zyd_tx_desc) + 10; + if (sc->sc_mac_rev == ZYD_ZD1211) + pktlen += totlen; + sc->sc_tx_desc.pktlen = htole16(pktlen); + + sc->sc_tx_desc.plcp_length = ((16 * totlen) + rate - 1) / rate; + sc->sc_tx_desc.plcp_service = 0; + if (rate == 22) { + remainder = (16 * totlen) % 22; + if ((remainder != 0) && (remainder < 7)) + sc->sc_tx_desc.plcp_service |= ZYD_PLCP_LENGEXT; + } + if (sizeof(sc->sc_tx_desc) > MHLEN) { + DPRINTF("No room for header structure!\n"); + zyd_tx_freem(m); + return; + } + mm = m_gethdr(M_NOWAIT, MT_DATA); + if (mm == NULL) { + DPRINTF("Could not allocate header mbuf!\n"); + zyd_tx_freem(m); + return; + } + bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); + mm->m_len = sizeof(sc->sc_tx_desc); + + mm->m_next = m; + mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; + mm->m_pkthdr.rcvif = NULL; + + /* start write transfer, if not started */ + _IF_ENQUEUE(&sc->sc_tx_queue, mm); + + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +zyd_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + uint16_t temp_len; + + DPRINTF("\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + + ifp->if_opackets++; + + case USB_ST_SETUP: + if (sc->sc_flags & ZYD_FLAG_BULK_WRITE_STALL) { + usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); + DPRINTFN(11, "write stalled\n"); + break; + } + if (sc->sc_flags & ZYD_FLAG_WAIT_COMMAND) { + /* + * don't send anything while a command is pending ! + */ + DPRINTFN(11, "wait command\n"); + break; + } + zyd_fill_write_queue(sc); + + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (m) { + if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; + } + usb2_m_copy_in(xfer->frbuffers, 0, + m, 0, m->m_pkthdr.len); + + /* get transfer length */ + temp_len = m->m_pkthdr.len; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, temp_len); + + xfer->frlengths[0] = temp_len; + + usb2_start_hardware(xfer); + + /* free mbuf and node */ + zyd_tx_freem(m); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + sc->sc_flags |= ZYD_FLAG_BULK_WRITE_STALL; + usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); + } + ifp->if_oerrors++; + break; + } + return; +} + +static void +zyd_init_cb(void *arg) +{ + struct zyd_softc *sc = arg; + + mtx_lock(&sc->sc_mtx); + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_cfg_pre_init, + &zyd_cfg_init, 0, 0); + mtx_unlock(&sc->sc_mtx); + + return; +} + +static int +zyd_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct zyd_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + int error; + + switch (cmd) { + case SIOCSIFFLAGS: + mtx_lock(&sc->sc_mtx); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_cfg_pre_init, + &zyd_cfg_init, 0, 0); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + usb2_config_td_queue_command + (&sc->sc_config_td, &zyd_cfg_pre_stop, + &zyd_cfg_stop, 0, 0); + } + } + mtx_unlock(&sc->sc_mtx); + error = 0; + break; + + case SIOCGIFMEDIA: + case SIOCADDMULTI: + case SIOCDELMULTI: + error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return (error); +} + +static void +zyd_watchdog(void *arg) +{ + struct zyd_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + if (sc->sc_amrr_timer) { + usb2_config_td_queue_command + (&sc->sc_config_td, NULL, + &zyd_cfg_amrr_timeout, 0, 0); + } + usb2_callout_reset(&sc->sc_watchdog, + hz, &zyd_watchdog, sc); + + mtx_unlock(&sc->sc_mtx); + + return; +} + +static void +zyd_config_copy_chan(struct zyd_config_copy_chan *cc, + struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (!c) + return; + cc->chan_to_ieee = + ieee80211_chan2ieee(ic, c); + if (c != IEEE80211_CHAN_ANYC) { + cc->chan_to_mode = + ieee80211_chan2mode(c); + cc->ic_freq = c->ic_freq; + if (IEEE80211_IS_CHAN_B(c)) + cc->chan_is_b = 1; + if (IEEE80211_IS_CHAN_A(c)) + cc->chan_is_a = 1; + if (IEEE80211_IS_CHAN_2GHZ(c)) + cc->chan_is_2ghz = 1; + if (IEEE80211_IS_CHAN_5GHZ(c)) + cc->chan_is_5ghz = 1; + if (IEEE80211_IS_CHAN_ANYG(c)) + cc->chan_is_g = 1; + } + return; +} + +static void +zyd_config_copy(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + const struct ieee80211_txparam *tp; + struct ieee80211vap *vap; + struct ifmultiaddr *ifma; + struct ieee80211_node *ni; + struct ieee80211com *ic; + struct ifnet *ifp; + + bzero(cc, sizeof(*cc)); + + ifp = sc->sc_ifp; + if (ifp) { + cc->if_flags = ifp->if_flags; + bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, + sizeof(cc->if_broadcastaddr)); + + cc->zyd_multi_low = 0x00000000; + cc->zyd_multi_high = 0x80000000; + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + uint8_t v; + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + v = ((uint8_t *)LLADDR((struct sockaddr_dl *) + ifma->ifma_addr))[5] >> 2; + if (v < 32) + cc->zyd_multi_low |= 1 << v; + else + cc->zyd_multi_high |= 1 << (v - 32); + } + IF_ADDR_UNLOCK(ifp); + + ic = ifp->if_l2com; + if (ic) { + zyd_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); + zyd_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); + vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap) { + ni = vap->iv_bss; + if (ni) { + cc->iv_bss.ni_intval = ni->ni_intval; + bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, + sizeof(cc->iv_bss.ni_bssid)); + } + tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { + cc->iv_bss.fixed_rate_none = 1; + } + } + cc->ic_opmode = ic->ic_opmode; + cc->ic_flags = ic->ic_flags; + cc->ic_txpowlimit = ic->ic_txpowlimit; + cc->ic_curmode = ic->ic_curmode; + + bcopy(ic->ic_myaddr, cc->ic_myaddr, + sizeof(cc->ic_myaddr)); + } + } + sc->sc_flags |= ZYD_FLAG_WAIT_COMMAND; + return; +} + +static void +zyd_end_of_commands(struct zyd_softc *sc) +{ + sc->sc_flags &= ~ZYD_FLAG_WAIT_COMMAND; + + /* start write transfer, if not started */ + usb2_transfer_start(sc->sc_xfer[0]); + return; +} + +static void +zyd_newassoc_cb(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); + return; +} + +static void +zyd_cfg_amrr_timeout(struct zyd_softc *sc, + struct usb2_config_td_cc *cc, uint16_t refcount) +{ + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + vap = zyd_get_vap(sc); + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + if ((sc->sc_flags & ZYD_FLAG_LL_READY) && + (sc->sc_flags & ZYD_FLAG_HL_READY)) { + + if (sc->sc_amrr_timer) { + + if (ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn)) { + /* ignore */ + } + } + } + return; +} + +static void +zyd_cfg_amrr_start(struct zyd_softc *sc) +{ + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + vap = zyd_get_vap(sc); + + if (vap == NULL) { + return; + } + ni = vap->iv_bss; + if (ni == NULL) { + return; + } + /* init AMRR */ + + ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); + + /* enable AMRR timer */ + + sc->sc_amrr_timer = 1; + return; +} + +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; + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + mtx_unlock(&sc->sc_mtx); + /* config thread is gone */ + return (NULL); + } + mtx_unlock(&sc->sc_mtx); + + 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_cb; + + 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); + struct zyd_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + /* Need to sync with config thread: */ + mtx_lock(&sc->sc_mtx); + if (usb2_config_td_sync(&sc->sc_config_td)) { + /* ignore */ + } + mtx_unlock(&sc->sc_mtx); + + ieee80211_amrr_cleanup(&zvp->amrr); + ieee80211_vap_detach(vap); + free(zvp, M_80211_VAP); + return; +} + +/* ARGUSED */ +static struct ieee80211_node * +zyd_node_alloc_cb(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_fill_write_queue(struct zyd_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + + /* + * We only fill up half of the queue with data frames. The rest is + * reserved for other kinds of frames. + */ + + while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + ni = (void *)(m->m_pkthdr.rcvif); + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + continue; + } + zyd_tx_data(sc, m, ni); + } + return; +} + +static void +zyd_tx_clean_queue(struct zyd_softc *sc) +{ + struct mbuf *m; + + for (;;) { + _IF_DEQUEUE(&sc->sc_tx_queue, m); + + if (!m) { + break; + } + zyd_tx_freem(m); + } + + return; +} + +static void +zyd_tx_freem(struct mbuf *m) +{ + struct ieee80211_node *ni; + + while (m) { + ni = (void *)(m->m_pkthdr.rcvif); + if (!ni) { + m = m_free(m); + continue; + } + if (m->m_flags & M_TXCB) { + ieee80211_process_callback(ni, m, 0); + } + m_freem(m); + ieee80211_free_node(ni); + + break; + } + return; +} + +static void +zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint16_t totlen; + uint16_t rate; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + rate = tp->mgmtrate; + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + wh = mtod(m, struct ieee80211_frame *); + } + /* fill Tx descriptor */ + + sc->sc_tx_desc.flags = ZYD_TX_FLAG_BACKOFF; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* get total length */ + totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + /* multicast frames are not sent at OFDM rates in 802.11b/g */ + if (totlen > vap->iv_rtsthreshold) { + sc->sc_tx_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) + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; + } + } else + sc->sc_tx_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)) + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); + + m->m_pkthdr.rcvif = (void *)ni; + zyd_setup_desc_and_tx(sc, m, rate); + return; +} + +static void +zyd_tx_data(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint16_t rate; + + wh = mtod(m, struct ieee80211_frame *); + + sc->sc_tx_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; + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_MULTICAST; + } 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, m); + if (k == NULL) { + m_freem(m); + ieee80211_free_node(ni); + return; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m, struct ieee80211_frame *); + } + /* fill Tx descriptor */ + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + uint16_t totlen; + + totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + + /* multicast frames are not sent at OFDM rates in 802.11b/g */ + if (totlen > vap->iv_rtsthreshold) { + sc->sc_tx_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) + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + sc->sc_tx_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)) + sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); + + m->m_pkthdr.rcvif = (void *)ni; + zyd_setup_desc_and_tx(sc, m, rate); + return; +} + +static int +zyd_raw_xmit_cb(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; + + mtx_lock(&sc->sc_mtx); + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + zyd_tx_mgt(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + zyd_tx_mgt(sc, m, ni); /* XXX zyd_tx_raw() */ + } + mtx_unlock(&sc->sc_mtx); + return (0); +} + +static struct ieee80211vap * +zyd_get_vap(struct zyd_softc *sc) +{ + struct ifnet *ifp; + struct ieee80211com *ic; + + if (sc == NULL) { + return NULL; + } + ifp = sc->sc_ifp; + if (ifp == NULL) { + return NULL; + } + ic = ifp->if_l2com; + if (ic == NULL) { + return NULL; + } + return TAILQ_FIRST(&ic->ic_vaps); +} diff --git a/sys/dev/usb2/wlan/if_zyd2_fw.h b/sys/dev/usb2/wlan/if_zyd2_fw.h new file mode 100644 index 000000000000..3c176e45576d --- /dev/null +++ b/sys/dev/usb2/wlan/if_zyd2_fw.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$ */ + +static const 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 + +static const 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/dev/usb2/wlan/if_zyd2_reg.h b/sys/dev/usb2/wlan/if_zyd2_reg.h new file mode 100644 index 000000000000..bdf1bc0e6f42 --- /dev/null +++ b/sys/dev/usb2/wlan/if_zyd2_reg.h @@ -0,0 +1,1280 @@ +/* $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 + * Copyright (c) 2006 by Florian Stoehr + * + * 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_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_PHY_REG 0xf831 /* PHY registers */ +#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ +#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_PV2000 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, 0x08 }, { 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, 0x30 }, { 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_CR123, 0x27 }, { 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, 0x00 }, { ZYD_CR49, 0x00 }, { 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_CR106, 0x24 }, \ + { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, { ZYD_CR110, 0x1f }, \ + { ZYD_CR111, 0x1f }, { 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, 0x00 }, { ZYD_CR253, 0x00 } \ +} + +#define ZYD_AL2230_RF \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ + 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f, 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_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_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 + +/* 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_RSP | ZYD_FILTER_REASS_RSP | \ + ZYD_FILTER_PRB_RSP | ZYD_FILTER_BCN | ZYD_FILTER_DEASS | \ + ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH) +#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 { + 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_HW_PADDING 10 /* bytes */ + +#define ZYD_CONFIG_INDEX 0 +#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 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_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_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 */ + void (*cfg_init_hw) (struct zyd_softc *, struct zyd_rf *); + void (*cfg_switch_radio) (struct zyd_softc *, uint8_t on); + void (*cfg_set_channel) (struct zyd_softc *, struct zyd_rf *, uint8_t); + uint8_t width; +}; + +enum { + ZYD_TR_BULK_DT_WR, + ZYD_TR_BULK_DT_RD, + ZYD_TR_BULK_CS_WR, + ZYD_TR_BULK_CS_RD, + ZYD_TR_INTR_DT_WR, + ZYD_TR_INTR_DT_RD, + ZYD_TR_INTR_CS_WR, + ZYD_TR_INTR_CS_RD, + ZYD_N_TRANSFER, +}; + +struct zyd_ifq { + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + uint16_t ifq_len; +}; + +struct zyd_node { + struct ieee80211_node ni; + struct ieee80211_amrr_node amn; +}; + +#define ZYD_NODE(ni) ((struct zyd_node *)(ni)) + +struct zyd_vap { + struct ieee80211vap vap; + struct ieee80211_beacon_offsets bo; + struct ieee80211_amrr amrr; + + int (*newstate) (struct ieee80211vap *, + enum ieee80211_state, int); +}; + +#define ZYD_VAP(vap) ((struct zyd_vap *)(vap)) + +struct zyd_config_copy_chan { + uint32_t chan_to_ieee; + enum ieee80211_phymode chan_to_mode; + uint16_t ic_freq; + uint8_t chan_is_5ghz:1; + uint8_t chan_is_2ghz:1; + uint8_t chan_is_b:1; + uint8_t chan_is_a:1; + uint8_t chan_is_g:1; + uint8_t unused:3; +}; + +struct zyd_config_copy_bss { + uint16_t ni_intval; + uint8_t ni_bssid[IEEE80211_ADDR_LEN]; + uint8_t fixed_rate_none; +}; + +struct zyd_config_copy { + struct zyd_config_copy_chan ic_curchan; + struct zyd_config_copy_chan ic_bsschan; + struct zyd_config_copy_bss iv_bss; + + enum ieee80211_opmode ic_opmode; + + uint32_t ic_flags; + uint32_t if_flags; + + uint32_t zyd_multi_low; + uint32_t zyd_multi_high; + + uint16_t ic_txpowlimit; + uint16_t ic_curmode; + + uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; + uint8_t if_broadcastaddr[IEEE80211_ADDR_LEN]; +}; + +struct zyd_softc { + void *sc_evilhack; /* XXX this pointer must be first */ + + struct zyd_rf sc_rf; + struct usb2_callout sc_watchdog; + struct mtx sc_mtx; + struct usb2_config_td sc_config_td; + struct zyd_rx_radiotap_header sc_rxtap; + struct zyd_tx_radiotap_header sc_txtap; + struct zyd_cmd sc_intr_ibuf; + struct zyd_cmd sc_intr_obuf; + struct zyd_tx_desc sc_tx_desc; + struct zyd_ifq sc_tx_queue; + struct cv sc_intr_cv; + + struct ifnet *sc_ifp; + struct usb2_device *sc_udev; + struct usb2_xfer *sc_xfer[ZYD_N_TRANSFER]; + const struct ieee80211_rate_table *sc_rates; + + enum ieee80211_state sc_ns_state; + uint32_t sc_rxtap_len; + uint32_t sc_txtap_len; + uint32_t sc_unit; + int sc_ns_arg; + + uint16_t sc_firmware_base; + uint16_t sc_fw_ver; + uint16_t sc_fwbase; + uint16_t sc_fw_rev; + + uint8_t sc_intr_iwakeup; + uint8_t sc_intr_owakeup; + uint8_t sc_intr_ilen; + uint8_t sc_intr_olen; + uint8_t sc_regdomain; + uint8_t sc_mac_rev; + uint8_t sc_rf_rev; + uint8_t sc_pa_rev; + uint8_t sc_fix_cr47; + uint8_t sc_fix_cr157; + uint8_t sc_pwr_cal[14]; + uint8_t sc_pwr_int[14]; + uint8_t sc_ofdm36_cal[14]; + uint8_t sc_ofdm48_cal[14]; + uint8_t sc_ofdm54_cal[14]; + + uint8_t sc_flags; +#define ZYD_FLAG_INTR_READ_STALL 0x01 +#define ZYD_FLAG_INTR_WRITE_STALL 0x02 +#define ZYD_FLAG_BULK_READ_STALL 0x04 +#define ZYD_FLAG_BULK_WRITE_STALL 0x08 +#define ZYD_FLAG_TX_BEACON 0x10 +#define ZYD_FLAG_HL_READY 0x20 +#define ZYD_FLAG_LL_READY 0x40 +#define ZYD_FLAG_WAIT_COMMAND 0x80 + + uint8_t sc_amrr_timer; + + uint8_t sc_myaddr[IEEE80211_ADDR_LEN]; + + char sc_name[16]; +}; diff --git a/sys/dev/usb2/wlan/usb2_wlan.c b/sys/dev/usb2/wlan/usb2_wlan.c new file mode 100644 index 000000000000..35c9cbb7231a --- /dev/null +++ b/sys/dev/usb2/wlan/usb2_wlan.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include + +MODULE_VERSION(usb2_wlan, 1); +MODULE_DEPEND(usb2_wlan, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/wlan/usb2_wlan.h b/sys/dev/usb2/wlan/usb2_wlan.h new file mode 100644 index 000000000000..9db120e572ee --- /dev/null +++ b/sys/dev/usb2/wlan/usb2_wlan.h @@ -0,0 +1,57 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +#ifndef _USB2_WLAN_H_ +#define _USB2_WLAN_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#endif /* _USB2_WLAN_H_ */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 2d4300d2ed9d..403874ad0458 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -302,6 +302,7 @@ SUBDIR= ${_3dfx} \ ural \ urio \ usb \ + usb2 \ uscanner \ uslcom \ utopia \ diff --git a/sys/modules/usb2/Makefile b/sys/modules/usb2/Makefile new file mode 100644 index 000000000000..183ebede4c29 --- /dev/null +++ b/sys/modules/usb2/Makefile @@ -0,0 +1,89 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +SUBDIR = +SUBDIR += bluetooth +SUBDIR += bluetooth_ng +SUBDIR += bluetooth_fw +SUBDIR += controller +SUBDIR += controller_at91dci +SUBDIR += controller_ehci +SUBDIR += controller_musb +SUBDIR += controller_ohci +SUBDIR += controller_uhci +SUBDIR += controller_uss820dci +SUBDIR += core +SUBDIR += ethernet +SUBDIR += ethernet_aue +SUBDIR += ethernet_axe +SUBDIR += ethernet_cdce +SUBDIR += ethernet_cue +SUBDIR += ethernet_dav +SUBDIR += ethernet_kue +SUBDIR += ethernet_rue +SUBDIR += image +SUBDIR += input +SUBDIR += input_hid +SUBDIR += input_kbd +SUBDIR += input_ms +SUBDIR += misc +SUBDIR += misc_dbp +SUBDIR += misc_fm +SUBDIR += ndis +SUBDIR += quirk +SUBDIR += scanner +SUBDIR += serial +SUBDIR += serial_ark +SUBDIR += serial_bsa +SUBDIR += serial_bser +SUBDIR += serial_chcom +SUBDIR += serial_cycom +SUBDIR += serial_foma +SUBDIR += serial_ftdi +SUBDIR += serial_gensa +SUBDIR += serial_ipaq +SUBDIR += serial_lpt +SUBDIR += serial_mct +SUBDIR += serial_modem +SUBDIR += serial_moscom +SUBDIR += serial_plcom +SUBDIR += serial_visor +SUBDIR += serial_vscom +SUBDIR += sound +SUBDIR += storage +SUBDIR += storage_ata +SUBDIR += storage_fs +SUBDIR += storage_mass +SUBDIR += storage_rio +SUBDIR += template +SUBDIR += wlan +SUBDIR += wlan_ral +SUBDIR += wlan_rum +SUBDIR += wlan_zyd + +.include + diff --git a/sys/modules/usb2/bluetooth/Makefile b/sys/modules/usb2/bluetooth/Makefile new file mode 100644 index 000000000000..b52a526d1d2e --- /dev/null +++ b/sys/modules/usb2/bluetooth/Makefile @@ -0,0 +1,15 @@ +# +# $FreeBSD$ +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/bluetooth + +KMOD= usb2_bluetooth +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= usb2_bluetooth.c + +.include diff --git a/sys/modules/usb2/bluetooth_fw/Makefile b/sys/modules/usb2/bluetooth_fw/Makefile new file mode 100644 index 000000000000..8b24300b5ba1 --- /dev/null +++ b/sys/modules/usb2/bluetooth_fw/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/bluetooth + +KMOD=usb2_bluetooth_fw +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= ubtbcmfw2.c + +.include diff --git a/sys/modules/usb2/bluetooth_ng/Makefile b/sys/modules/usb2/bluetooth_ng/Makefile new file mode 100644 index 000000000000..d40f9fc5f2f6 --- /dev/null +++ b/sys/modules/usb2/bluetooth_ng/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/bluetooth + +KMOD=usb2_bluetooth_ng +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= ng_ubt2.c + +.include diff --git a/sys/modules/usb2/controller/Makefile b/sys/modules/usb2/controller/Makefile new file mode 100644 index 000000000000..4c141663b94c --- /dev/null +++ b/sys/modules/usb2/controller/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD= usb2_controller +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= usb2_controller.c + +.include diff --git a/sys/modules/usb2/controller_at91dci/Makefile b/sys/modules/usb2/controller_at91dci/Makefile new file mode 100644 index 000000000000..9810ac1f4117 --- /dev/null +++ b/sys/modules/usb2/controller_at91dci/Makefile @@ -0,0 +1,41 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_at91dci +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= at91dci.c +.if defined(HAS_ATMELARM) +SRCS+= at91dci_atmelarm.c +.endif + +.include diff --git a/sys/modules/usb2/controller_ehci/Makefile b/sys/modules/usb2/controller_ehci/Makefile new file mode 100644 index 000000000000..765b31034b3f --- /dev/null +++ b/sys/modules/usb2/controller_ehci/Makefile @@ -0,0 +1,39 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_ehci +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= ehci2.c +SRCS+= ehci2_pci.c + +.include diff --git a/sys/modules/usb2/controller_musb/Makefile b/sys/modules/usb2/controller_musb/Makefile new file mode 100644 index 000000000000..1f720f88e3d4 --- /dev/null +++ b/sys/modules/usb2/controller_musb/Makefile @@ -0,0 +1,41 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_musb +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= musb2_otg.c +.if defined(HAS_ATMELARM) +SRCS+= musb2_otg_atmelarm.c +.endif + +.include diff --git a/sys/modules/usb2/controller_ohci/Makefile b/sys/modules/usb2/controller_ohci/Makefile new file mode 100644 index 000000000000..78773b9aff4f --- /dev/null +++ b/sys/modules/usb2/controller_ohci/Makefile @@ -0,0 +1,42 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_ohci +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= ohci2.c +SRCS+= ohci2_pci.c +.if defined(HAS_ATMELARM) +SRCS+= ohci2_atmelarm.c +.endif + +.include diff --git a/sys/modules/usb2/controller_uhci/Makefile b/sys/modules/usb2/controller_uhci/Makefile new file mode 100644 index 000000000000..5d91c36c0817 --- /dev/null +++ b/sys/modules/usb2/controller_uhci/Makefile @@ -0,0 +1,39 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_uhci +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= uhci2.c +SRCS+= uhci2_pci.c + +.include diff --git a/sys/modules/usb2/controller_uss820dci/Makefile b/sys/modules/usb2/controller_uss820dci/Makefile new file mode 100644 index 000000000000..91e10954b3d4 --- /dev/null +++ b/sys/modules/usb2/controller_uss820dci/Makefile @@ -0,0 +1,41 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/controller + +KMOD=usb2_controller_uss820dci +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h pci_if.h opt_bus.h card_if.h +SRCS+= uss820dci.c +.if defined(HAS_ATMELARM) +SRCS+= uss820dci_atmelarm.c +.endif + +.include diff --git a/sys/modules/usb2/core/Makefile b/sys/modules/usb2/core/Makefile new file mode 100644 index 000000000000..f9e95dba213c --- /dev/null +++ b/sys/modules/usb2/core/Makefile @@ -0,0 +1,60 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/core + +KMOD= usb2_core +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h opt_usb.h opt_bus.h +SRCS+= usb2_if.c +SRCS+= usb2_busdma.c +SRCS+= usb2_compat_linux.c +SRCS+= usb2_config_td.c +SRCS+= usb2_core.c +SRCS+= usb2_debug.c +SRCS+= usb2_dev.c +SRCS+= usb2_device.c +SRCS+= usb2_dynamic.c +SRCS+= usb2_error.c +SRCS+= usb2_generic.c +SRCS+= usb2_handle_request.c +SRCS+= usb2_hid.c +SRCS+= usb2_hub.c +SRCS+= usb2_lookup.c +SRCS+= usb2_mbuf.c +SRCS+= usb2_msctest.c +SRCS+= usb2_parse.c +SRCS+= usb2_process.c +SRCS+= usb2_request.c +SRCS+= usb2_sw_transfer.c +SRCS+= usb2_transfer.c +SRCS+= usb2_util.c + +.include + diff --git a/sys/modules/usb2/ethernet/Makefile b/sys/modules/usb2/ethernet/Makefile new file mode 100644 index 000000000000..604aba4a4852 --- /dev/null +++ b/sys/modules/usb2/ethernet/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD= usb2_ethernet +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= usb2_ethernet.c + +.include diff --git a/sys/modules/usb2/ethernet_aue/Makefile b/sys/modules/usb2/ethernet_aue/Makefile new file mode 100644 index 000000000000..c328055f8e1c --- /dev/null +++ b/sys/modules/usb2/ethernet_aue/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_aue +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_aue2.c + +.include diff --git a/sys/modules/usb2/ethernet_axe/Makefile b/sys/modules/usb2/ethernet_axe/Makefile new file mode 100644 index 000000000000..96590124f4b1 --- /dev/null +++ b/sys/modules/usb2/ethernet_axe/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_axe +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_axe2.c + +.include diff --git a/sys/modules/usb2/ethernet_cdce/Makefile b/sys/modules/usb2/ethernet_cdce/Makefile new file mode 100644 index 000000000000..329e1a68edf2 --- /dev/null +++ b/sys/modules/usb2/ethernet_cdce/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_cdce +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_cdce2.c + +.include diff --git a/sys/modules/usb2/ethernet_cue/Makefile b/sys/modules/usb2/ethernet_cue/Makefile new file mode 100644 index 000000000000..96af01b66bce --- /dev/null +++ b/sys/modules/usb2/ethernet_cue/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_cue +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_cue2.c + +.include diff --git a/sys/modules/usb2/ethernet_dav/Makefile b/sys/modules/usb2/ethernet_dav/Makefile new file mode 100644 index 000000000000..0fd94684dd58 --- /dev/null +++ b/sys/modules/usb2/ethernet_dav/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_dav +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_udav2.c + +.include diff --git a/sys/modules/usb2/ethernet_kue/Makefile b/sys/modules/usb2/ethernet_kue/Makefile new file mode 100644 index 000000000000..28e253116c50 --- /dev/null +++ b/sys/modules/usb2/ethernet_kue/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_kue +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_kue2.c + +.include diff --git a/sys/modules/usb2/ethernet_rue/Makefile b/sys/modules/usb2/ethernet_rue/Makefile new file mode 100644 index 000000000000..dcbd2022d016 --- /dev/null +++ b/sys/modules/usb2/ethernet_rue/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ethernet + +KMOD=usb2_ethernet_rue +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_rue2.c + +.include diff --git a/sys/modules/usb2/image/Makefile b/sys/modules/usb2/image/Makefile new file mode 100644 index 000000000000..24b2c80add9c --- /dev/null +++ b/sys/modules/usb2/image/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/image + +KMOD= usb2_image +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h +SRCS+= usb2_image.c + +.include diff --git a/sys/modules/usb2/input/Makefile b/sys/modules/usb2/input/Makefile new file mode 100644 index 000000000000..74a28ab05df2 --- /dev/null +++ b/sys/modules/usb2/input/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/input + +KMOD= usb2_input +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h opt_kbd.h opt_ukbd.h +SRCS+= usb2_input.c + +.include diff --git a/sys/modules/usb2/input_hid/Makefile b/sys/modules/usb2/input_hid/Makefile new file mode 100644 index 000000000000..27f5c5af78f1 --- /dev/null +++ b/sys/modules/usb2/input_hid/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/input + +KMOD=usb2_input_hid +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h opt_kbd.h opt_ukbd.h +SRCS+= uhid2.c + +.include diff --git a/sys/modules/usb2/input_kbd/Makefile b/sys/modules/usb2/input_kbd/Makefile new file mode 100644 index 000000000000..23ec6d8082bf --- /dev/null +++ b/sys/modules/usb2/input_kbd/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/input + +KMOD=usb2_input_kbd +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h opt_kbd.h opt_ukbd.h +SRCS+= ukbd2.c + +.include diff --git a/sys/modules/usb2/input_ms/Makefile b/sys/modules/usb2/input_ms/Makefile new file mode 100644 index 000000000000..a3ff3e9ffda3 --- /dev/null +++ b/sys/modules/usb2/input_ms/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/input + +KMOD=usb2_input_ms +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h opt_kbd.h opt_ukbd.h +SRCS+= ums2.c + +.include diff --git a/sys/modules/usb2/misc/Makefile b/sys/modules/usb2/misc/Makefile new file mode 100644 index 000000000000..71c18ccc88d0 --- /dev/null +++ b/sys/modules/usb2/misc/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/misc + +KMOD= usb2_misc +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= usb2_misc.c + +.include diff --git a/sys/modules/usb2/misc_dbp/Makefile b/sys/modules/usb2/misc_dbp/Makefile new file mode 100644 index 000000000000..44a0681d5e15 --- /dev/null +++ b/sys/modules/usb2/misc_dbp/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/misc + +KMOD=usb2_misc_dbp +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= udbp2.c + +.include diff --git a/sys/modules/usb2/misc_fm/Makefile b/sys/modules/usb2/misc_fm/Makefile new file mode 100644 index 000000000000..f5e377d53fd3 --- /dev/null +++ b/sys/modules/usb2/misc_fm/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/misc + +KMOD=usb2_misc_fm +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_netgraph.h +SRCS+= ufm2.c + +.include diff --git a/sys/modules/usb2/ndis/Makefile b/sys/modules/usb2/ndis/Makefile new file mode 100644 index 000000000000..89c87b18274a --- /dev/null +++ b/sys/modules/usb2/ndis/Makefile @@ -0,0 +1,40 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/ndis + +KMOD= usb2_ndis +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h + +SRCS+= usb2_ndis.c +SRCS+= if_ndis_usb2.c + +.include diff --git a/sys/modules/usb2/quirk/Makefile b/sys/modules/usb2/quirk/Makefile new file mode 100644 index 000000000000..aae5dda648d9 --- /dev/null +++ b/sys/modules/usb2/quirk/Makefile @@ -0,0 +1,37 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/quirk + +KMOD= usb2_quirk +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h opt_usb.h opt_bus.h +SRCS+= usb2_quirk.c + +.include diff --git a/sys/modules/usb2/scanner/Makefile b/sys/modules/usb2/scanner/Makefile new file mode 100644 index 000000000000..db82e48bd001 --- /dev/null +++ b/sys/modules/usb2/scanner/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/image + +KMOD=usb2_scanner +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h +SRCS+= uscanner2.c + +.include diff --git a/sys/modules/usb2/serial/Makefile b/sys/modules/usb2/serial/Makefile new file mode 100644 index 000000000000..d37b3d51e47d --- /dev/null +++ b/sys/modules/usb2/serial/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD= usb2_serial +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= usb2_serial.c + +.include diff --git a/sys/modules/usb2/serial_ark/Makefile b/sys/modules/usb2/serial_ark/Makefile new file mode 100644 index 000000000000..e0919c4d732a --- /dev/null +++ b/sys/modules/usb2/serial_ark/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_ark +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uark2.c + +.include diff --git a/sys/modules/usb2/serial_bsa/Makefile b/sys/modules/usb2/serial_bsa/Makefile new file mode 100644 index 000000000000..782bef1f2cd1 --- /dev/null +++ b/sys/modules/usb2/serial_bsa/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_bsa +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ubsa2.c + +.include diff --git a/sys/modules/usb2/serial_bser/Makefile b/sys/modules/usb2/serial_bser/Makefile new file mode 100644 index 000000000000..7b2921c757f6 --- /dev/null +++ b/sys/modules/usb2/serial_bser/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_bser +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ubser2.c + +.include diff --git a/sys/modules/usb2/serial_chcom/Makefile b/sys/modules/usb2/serial_chcom/Makefile new file mode 100644 index 000000000000..726f711f6151 --- /dev/null +++ b/sys/modules/usb2/serial_chcom/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_chcom +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uchcom2.c + +.include diff --git a/sys/modules/usb2/serial_cycom/Makefile b/sys/modules/usb2/serial_cycom/Makefile new file mode 100644 index 000000000000..74a1865fb977 --- /dev/null +++ b/sys/modules/usb2/serial_cycom/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_cycom +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ucycom2.c + +.include diff --git a/sys/modules/usb2/serial_foma/Makefile b/sys/modules/usb2/serial_foma/Makefile new file mode 100644 index 000000000000..520abee8001f --- /dev/null +++ b/sys/modules/usb2/serial_foma/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_foma +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ufoma2.c + +.include diff --git a/sys/modules/usb2/serial_ftdi/Makefile b/sys/modules/usb2/serial_ftdi/Makefile new file mode 100644 index 000000000000..c6735e3c2e90 --- /dev/null +++ b/sys/modules/usb2/serial_ftdi/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_ftdi +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uftdi2.c + +.include diff --git a/sys/modules/usb2/serial_gensa/Makefile b/sys/modules/usb2/serial_gensa/Makefile new file mode 100644 index 000000000000..6511d860031f --- /dev/null +++ b/sys/modules/usb2/serial_gensa/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_gensa +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ugensa2.c + +.include diff --git a/sys/modules/usb2/serial_ipaq/Makefile b/sys/modules/usb2/serial_ipaq/Makefile new file mode 100644 index 000000000000..159eb724f356 --- /dev/null +++ b/sys/modules/usb2/serial_ipaq/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_ipaq +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uipaq2.c + +.include diff --git a/sys/modules/usb2/serial_lpt/Makefile b/sys/modules/usb2/serial_lpt/Makefile new file mode 100644 index 000000000000..2135e9f191b3 --- /dev/null +++ b/sys/modules/usb2/serial_lpt/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_lpt +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= ulpt2.c + +.include diff --git a/sys/modules/usb2/serial_mct/Makefile b/sys/modules/usb2/serial_mct/Makefile new file mode 100644 index 000000000000..76b2f3ed6107 --- /dev/null +++ b/sys/modules/usb2/serial_mct/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_mct +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= umct2.c + +.include diff --git a/sys/modules/usb2/serial_modem/Makefile b/sys/modules/usb2/serial_modem/Makefile new file mode 100644 index 000000000000..f1fbf45be320 --- /dev/null +++ b/sys/modules/usb2/serial_modem/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_modem +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= umodem2.c + +.include diff --git a/sys/modules/usb2/serial_moscom/Makefile b/sys/modules/usb2/serial_moscom/Makefile new file mode 100644 index 000000000000..46bf537a89a0 --- /dev/null +++ b/sys/modules/usb2/serial_moscom/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_moscom +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= umoscom2.c + +.include diff --git a/sys/modules/usb2/serial_plcom/Makefile b/sys/modules/usb2/serial_plcom/Makefile new file mode 100644 index 000000000000..5f4353ab2860 --- /dev/null +++ b/sys/modules/usb2/serial_plcom/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_plcom +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uplcom2.c + +.include diff --git a/sys/modules/usb2/serial_visor/Makefile b/sys/modules/usb2/serial_visor/Makefile new file mode 100644 index 000000000000..cbda332b7fe8 --- /dev/null +++ b/sys/modules/usb2/serial_visor/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_visor +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uvisor2.c + +.include diff --git a/sys/modules/usb2/serial_vscom/Makefile b/sys/modules/usb2/serial_vscom/Makefile new file mode 100644 index 000000000000..8c4114ae24e6 --- /dev/null +++ b/sys/modules/usb2/serial_vscom/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_vscom +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= uvscom2.c + +.include diff --git a/sys/modules/usb2/sound/Makefile b/sys/modules/usb2/sound/Makefile new file mode 100644 index 000000000000..0f1bbb99be19 --- /dev/null +++ b/sys/modules/usb2/sound/Makefile @@ -0,0 +1,42 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/sound + +KMOD= usb2_sound +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h feeder_if.h channel_if.h +SRCS+= mixer_if.h + +SRCS+= usb2_sound.c +SRCS+= uaudio2.c +SRCS+= uaudio2_pcm.c + +.include diff --git a/sys/modules/usb2/storage/Makefile b/sys/modules/usb2/storage/Makefile new file mode 100644 index 000000000000..16b657d34797 --- /dev/null +++ b/sys/modules/usb2/storage/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/storage + +KMOD= usb2_storage +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_cam.h opt_ata.h ata_if.h +SRCS+= usb2_storage.c + +.include diff --git a/sys/modules/usb2/storage_ata/Makefile b/sys/modules/usb2/storage_ata/Makefile new file mode 100644 index 000000000000..c4d7b592be65 --- /dev/null +++ b/sys/modules/usb2/storage_ata/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/storage + +KMOD=usb2_storage_ata +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_cam.h opt_ata.h ata_if.h +SRCS+= ata-usb2.c + +.include diff --git a/sys/modules/usb2/storage_fs/Makefile b/sys/modules/usb2/storage_fs/Makefile new file mode 100644 index 000000000000..3392862522d6 --- /dev/null +++ b/sys/modules/usb2/storage_fs/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/storage + +KMOD=usb2_storage_fs +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_cam.h opt_ata.h ata_if.h +SRCS+= ustorage2_fs.c + +.include diff --git a/sys/modules/usb2/storage_mass/Makefile b/sys/modules/usb2/storage_mass/Makefile new file mode 100644 index 000000000000..6ab6613cfd3f --- /dev/null +++ b/sys/modules/usb2/storage_mass/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/storage + +KMOD=usb2_storage_mass +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_cam.h opt_ata.h ata_if.h +SRCS+= umass2.c + +.include diff --git a/sys/modules/usb2/storage_rio/Makefile b/sys/modules/usb2/storage_rio/Makefile new file mode 100644 index 000000000000..74a1e9e95fd8 --- /dev/null +++ b/sys/modules/usb2/storage_rio/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/storage + +KMOD=usb2_storage_rio +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_cam.h opt_ata.h ata_if.h +SRCS+= urio2.c + +.include diff --git a/sys/modules/usb2/template/Makefile b/sys/modules/usb2/template/Makefile new file mode 100644 index 000000000000..64c8054a3b0d --- /dev/null +++ b/sys/modules/usb2/template/Makefile @@ -0,0 +1,40 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/template + +KMOD= usb2_template +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h opt_usb.h opt_bus.h +SRCS+= usb2_template.c +SRCS+= usb2_template_cdce.c +SRCS+= usb2_template_msc.c +SRCS+= usb2_template_mtp.c + +.include diff --git a/sys/modules/usb2/wlan/Makefile b/sys/modules/usb2/wlan/Makefile new file mode 100644 index 000000000000..482947c08e73 --- /dev/null +++ b/sys/modules/usb2/wlan/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S= ${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/wlan + +KMOD= usb2_wlan +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= usb2_wlan.c + +.include diff --git a/sys/modules/usb2/wlan_ral/Makefile b/sys/modules/usb2/wlan_ral/Makefile new file mode 100644 index 000000000000..bf653e882b66 --- /dev/null +++ b/sys/modules/usb2/wlan_ral/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/wlan + +KMOD=usb2_wlan_ral +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_ural2.c + +.include diff --git a/sys/modules/usb2/wlan_rum/Makefile b/sys/modules/usb2/wlan_rum/Makefile new file mode 100644 index 000000000000..0eed54ba5cd3 --- /dev/null +++ b/sys/modules/usb2/wlan_rum/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/wlan + +KMOD=usb2_wlan_rum +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_rum2.c + +.include diff --git a/sys/modules/usb2/wlan_zyd/Makefile b/sys/modules/usb2/wlan_zyd/Makefile new file mode 100644 index 000000000000..0a231ead2b79 --- /dev/null +++ b/sys/modules/usb2/wlan_zyd/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. 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. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/wlan + +KMOD=usb2_wlan_zyd +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h miibus_if.h opt_inet.h opt_bus.h +SRCS+= if_zyd2.c + +.include diff --git a/usr.sbin/usbconfig/Makefile b/usr.sbin/usbconfig/Makefile new file mode 100644 index 000000000000..6356963498db --- /dev/null +++ b/usr.sbin/usbconfig/Makefile @@ -0,0 +1,9 @@ +# +# $FreeBSD$ +# +PROG= usbconfig +MAN= usbconfig.8 +SRCS= usbconfig.c dump.c +LDADD+= -lusb20 + +.include diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c new file mode 100644 index 000000000000..89a47ac3f79e --- /dev/null +++ b/usr.sbin/usbconfig/dump.c @@ -0,0 +1,459 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dump.h" + +#define DUMP0(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP1(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP2(n,type,field,...) dump_field(pdev, " ", #field, n->field); +#define DUMP3(n,type,field,...) dump_field(pdev, " ", #field, n->field); + +const char * +dump_mode(uint8_t value) +{ + if (value == LIBUSB20_MODE_HOST) + return ("HOST"); + return ("DEVICE"); +} + +const char * +dump_speed(uint8_t value) +{ + ; /* style fix */ + switch (value) { + case LIBUSB20_SPEED_LOW: + return ("LOW (1.5Mbps)"); + case LIBUSB20_SPEED_FULL: + return ("FULL (12Mbps)"); + case LIBUSB20_SPEED_HIGH: + return ("HIGH (480Mbps)"); + case LIBUSB20_SPEED_VARIABLE: + return ("VARIABLE (52-480Mbps)"); + case LIBUSB20_SPEED_SUPER: + return ("SUPER (4.8Gbps)"); + default: + break; + } + return ("unknown"); +} + +const char * +dump_power_mode(uint8_t value) +{ + ; /* style fix */ + switch (value) { + case LIBUSB20_POWER_OFF: + return ("OFF"); + case LIBUSB20_POWER_ON: + return ("ON"); + case LIBUSB20_POWER_SAVE: + return ("SAVE"); + case LIBUSB20_POWER_SUSPEND: + return ("SUSPEND"); + case LIBUSB20_POWER_RESUME: + return ("RESUME"); + default: + return ("UNKNOWN"); + } +} + +static void +dump_field(struct libusb20_device *pdev, const char *plevel, + const char *field, uint32_t value) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + uint16_t lang_id; + uint8_t index; + uint8_t temp_string[256]; + + printf("%s%s = 0x%04x ", plevel, field, value); + + if ((field[0] != 'i') || (field[1] == 'd')) { + printf("\n"); + return; + } + if (value == 0) { + printf(" \n"); + return; + } + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + + lang_id = 0; + index = 0; + + req.bmRequestType = + LIBUSB20_REQUEST_TYPE_STANDARD | + LIBUSB20_RECIPIENT_DEVICE | + LIBUSB20_ENDPOINT_IN; + req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; + req.wValue = (256 * LIBUSB20_DT_STRING) | index; + req.wIndex = lang_id; + req.wLength = 4; /* bytes */ + + if (libusb20_dev_request_sync(pdev, &req, + temp_string, NULL, 1000, 0)) { + goto done; + } + lang_id = temp_string[2] | (temp_string[3] << 8); + + printf(" LangId:0x%04x <", lang_id); + + index = value; + + req.wValue = (256 * LIBUSB20_DT_STRING) | index; + req.wIndex = lang_id; + req.wLength = 4; /* bytes */ + + if (libusb20_dev_request_sync(pdev, &req, + temp_string, NULL, 1000, 0)) { + printf("ERROR>\n"); + goto done; + } + req.wValue = (256 * LIBUSB20_DT_STRING) | index; + req.wIndex = lang_id; + req.wLength = temp_string[0]; /* bytes */ + + if (libusb20_dev_request_sync(pdev, &req, + temp_string, NULL, 1000, 0)) { + printf("ERROR>\n"); + goto done; + } + req.wLength /= 2; + + for (index = 1; index != req.wLength; index++) { + if (isprint(temp_string[(2 * index) + 0])) { + printf("%c", temp_string[(2 * index) + 0]); + } else if (isprint(temp_string[(2 * index) + 1])) { + printf("%c", temp_string[(2 * index) + 1]); + } else { + printf("?"); + } + } + printf(">\n"); +done: + return; +} + +static void +dump_extra(struct libusb20_me_struct *str, const char *plevel) +{ + const uint8_t *ptr; + uint8_t x; + + ptr = NULL; + + while ((ptr = libusb20_desc_foreach(str, ptr))) { + printf("\n" "%sAdditional Descriptor\n\n", plevel); + printf("%sbLength = 0x%02x\n", plevel, ptr[0]); + printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]); + if (ptr[0] > 1) + printf("%sbDescriptorSubType = 0x%02x\n", + plevel, ptr[2]); + printf("%s RAW dump: ", plevel); + for (x = 0; x != ptr[0]; x++) { + if ((x % 8) == 0) { + printf("\n%s 0x%02x | ", plevel, x); + } + printf("0x%02x%s", ptr[x], + (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : ""); + } + printf("\n"); + } + return; +} + +static void +dump_endpoint(struct libusb20_device *pdev, + struct libusb20_endpoint *ep) +{ + struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc; + + edesc = &ep->desc; + LIBUSB20_ENDPOINT_DESC(DUMP3, edesc); + dump_extra(&ep->extra, " " " " " "); + return; +} + +static void +dump_iface(struct libusb20_device *pdev, + struct libusb20_interface *iface) +{ + struct LIBUSB20_INTERFACE_DESC_DECODED *idesc; + uint8_t z; + + idesc = &iface->desc; + LIBUSB20_INTERFACE_DESC(DUMP2, idesc); + dump_extra(&iface->extra, " " " " " "); + + for (z = 0; z != iface->num_endpoints; z++) { + printf("\n Endpoint %u\n", z); + dump_endpoint(pdev, iface->endpoints + z); + } + return; +} + +void +dump_device_info(struct libusb20_device *pdev) +{ + printf("%s, cfg=%u md=%s spd=%s pwr=%s\n", + libusb20_dev_get_desc(pdev), + libusb20_dev_get_config_index(pdev), + dump_mode(libusb20_dev_get_mode(pdev)), + dump_speed(libusb20_dev_get_speed(pdev)), + dump_power_mode(libusb20_dev_get_power_mode(pdev))); + return; +} + +void +dump_be_quirk_names(struct libusb20_backend *pbe) +{ + struct libusb20_quirk q; + uint16_t x; + int err; + + memset(&q, 0, sizeof(q)); + + printf("\nDumping list of supported quirks:\n\n"); + + for (x = 0; x != 0xFFFF; x++) { + + err = libusb20_be_get_quirk_name(pbe, x, &q); + if (err) { + if (x == 0) { + printf("No quirk names - maybe the USB quirk " + "module has not been loaded.\n"); + } + break; + } + if (strcmp(q.quirkname, "UQ_NONE")) + printf("%s\n", q.quirkname); + } + printf("\n"); + return; +} + +void +dump_be_dev_quirks(struct libusb20_backend *pbe) +{ + struct libusb20_quirk q; + uint16_t x; + int err; + + memset(&q, 0, sizeof(q)); + + printf("\nDumping current device quirks:\n\n"); + + for (x = 0; x != 0xFFFF; x++) { + + err = libusb20_be_get_dev_quirk(pbe, x, &q); + if (err) { + if (x == 0) { + printf("No device quirks - maybe the USB quirk " + "module has not been loaded.\n"); + } + break; + } + if (strcmp(q.quirkname, "UQ_NONE")) { + printf("VID=0x%04x PID=0x%04x REVLO=0x%04x " + "REVHI=0x%04x QUIRK=%s\n", + q.vid, q.pid, q.bcdDeviceLow, + q.bcdDeviceHigh, q.quirkname); + } + } + printf("\n"); + return; +} + +void +dump_be_access(struct libusb20_backend *pbe) +{ + struct group *gr; + struct passwd *pw; + const char *owner; + const char *group; + uid_t uid; + gid_t gid; + mode_t mode; + + if (libusb20_be_get_owner(pbe, &uid, &gid)) { + err(1, "could not get owner"); + } + if (libusb20_be_get_perm(pbe, &mode)) { + err(1, "could not get permission"); + } + owner = (pw = getpwuid(uid)) ? pw->pw_name : "UNKNOWN"; + group = (gr = getgrgid(gid)) ? gr->gr_name : "UNKNOWN"; + + if (mode || 1) { + printf("Global Access: %s:%s 0%o\n", owner, group, mode); + } else { + printf("Global Access: \n"); + } + return; +} + +void +dump_device_access(struct libusb20_device *pdev, uint8_t iface) +{ + struct group *gr; + struct passwd *pw; + const char *owner; + const char *group; + uid_t uid; + gid_t gid; + mode_t mode; + + if (libusb20_dev_get_owner(pdev, &uid, &gid)) { + err(1, "could not get owner"); + } + if (libusb20_dev_get_perm(pdev, &mode)) { + err(1, "could not get permission"); + } + if (mode) { + owner = (pw = getpwuid(uid)) ? pw->pw_name : "UNKNOWN"; + group = (gr = getgrgid(gid)) ? gr->gr_name : "UNKNOWN"; + + printf(" " "Device Access: %s:%s 0%o\n", owner, group, mode); + + } else { + printf(" " "Device Access: \n"); + } + + if (iface == 0xFF) { + for (iface = 0; iface != 0xFF; iface++) { + if (dump_device_iface_access(pdev, iface)) { + break; + } + } + } else { + if (dump_device_iface_access(pdev, iface)) { + err(1, "could not get interface access info"); + } + } + return; +} + +int +dump_device_iface_access(struct libusb20_device *pdev, uint8_t iface) +{ + struct group *gr; + struct passwd *pw; + const char *owner; + const char *group; + uid_t uid; + gid_t gid; + mode_t mode; + int error; + + if ((error = libusb20_dev_get_iface_owner(pdev, iface, &uid, &gid))) { + return (error); + } + if ((error = libusb20_dev_get_iface_perm(pdev, iface, &mode))) { + return (error); + } + if (mode) { + + owner = (pw = getpwuid(uid)) ? pw->pw_name : "UNKNOWN"; + group = (gr = getgrgid(gid)) ? gr->gr_name : "UNKNOWN"; + + printf(" " "Interface %u Access: %s:%s 0%o\n", iface, owner, group, mode); + } else { + printf(" " "Interface %u Access: \n", iface); + } + + return (0); +} + +void +dump_device_desc(struct libusb20_device *pdev) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + ddesc = libusb20_dev_get_device_desc(pdev); + LIBUSB20_DEVICE_DESC(DUMP0, ddesc); + return; +} + +void +dump_config(struct libusb20_device *pdev, uint8_t all_cfg) +{ + struct LIBUSB20_CONFIG_DESC_DECODED *cdesc; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + struct libusb20_config *pcfg = NULL; + uint8_t cfg_index; + uint8_t cfg_index_end; + uint8_t x; + uint8_t y; + + ddesc = libusb20_dev_get_device_desc(pdev); + + if (all_cfg) { + cfg_index = 0; + cfg_index_end = ddesc->bNumConfigurations; + } else { + cfg_index = libusb20_dev_get_config_index(pdev); + cfg_index_end = cfg_index + 1; + } + + for (; cfg_index != cfg_index_end; cfg_index++) { + + pcfg = libusb20_dev_alloc_config(pdev, cfg_index); + if (!pcfg) { + continue; + } + printf("\n Configuration index %u\n\n", cfg_index); + cdesc = &(pcfg->desc); + LIBUSB20_CONFIG_DESC(DUMP1, cdesc); + dump_extra(&(pcfg->extra), " " " "); + + for (x = 0; x != pcfg->num_interface; x++) { + printf("\n Interface %u\n", x); + dump_iface(pdev, pcfg->interface + x); + printf("\n"); + for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) { + printf("\n Interface %u Alt %u\n", x, y + 1); + dump_iface(pdev, + (pcfg->interface + x)->altsetting + y); + printf("\n"); + } + } + printf("\n"); + free(pcfg); + } + return; +} diff --git a/usr.sbin/usbconfig/dump.h b/usr.sbin/usbconfig/dump.h new file mode 100644 index 000000000000..95ef7209d0d0 --- /dev/null +++ b/usr.sbin/usbconfig/dump.h @@ -0,0 +1,37 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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. + */ + +const char *dump_mode(uint8_t value); +const char *dump_speed(uint8_t value); +const char *dump_power_mode(uint8_t value); +void dump_device_info(struct libusb20_device *pdev); +void dump_be_access(struct libusb20_backend *pbe); +void dump_be_quirk_names(struct libusb20_backend *pbe); +void dump_be_dev_quirks(struct libusb20_backend *pbe); +void dump_device_access(struct libusb20_device *pdev, uint8_t iface); +int dump_device_iface_access(struct libusb20_device *pdev, uint8_t iface); +void dump_device_desc(struct libusb20_device *pdev); +void dump_config(struct libusb20_device *pdev, uint8_t all_cfg); diff --git a/usr.sbin/usbconfig/usbconfig.8 b/usr.sbin/usbconfig/usbconfig.8 new file mode 100644 index 000000000000..ffdb4ce7112e --- /dev/null +++ b/usr.sbin/usbconfig/usbconfig.8 @@ -0,0 +1,53 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Hans Petter Selasky. 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. +.\" +.Dd Sep 28, 2008 +.Dt USBCONFIG 8 +.Os +.Sh NAME +.Nm usbconfig +.Nd configure the USB subsystem +.Sh SYNOPSIS +.Nm +.Op Fl u Ar unit +.Op Fl a Ar addr +.Op cmds... +.Sh DESCRIPTION +The +.Nm +utility is used to configure and dump information about the USB subsystem. +.Pp +The options are as follows: +.Bl -tag -width " " +.It Fl u Ar unit +Limit device range to USB devices connected to the given USBUS unit. +.It Fl a Ar addr +Limit device range to the given USB device index. +Should only be used in conjunction with the unit argument. +.It Fl h +Show help and available commands. +.El +.Sh SEE ALSO +.Xr usb2_core 4 diff --git a/usr.sbin/usbconfig/usbconfig.c b/usr.sbin/usbconfig/usbconfig.c new file mode 100644 index 000000000000..eb10b5f59bfc --- /dev/null +++ b/usr.sbin/usbconfig/usbconfig.c @@ -0,0 +1,683 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dump.h" + +struct options { + const char *quirkname; + gid_t gid; + uid_t uid; + mode_t mode; + uint32_t got_any; + uint16_t bus; + uint16_t addr; + uint16_t iface; + uint16_t vid; + uint16_t pid; + uint16_t lo_rev; /* inclusive */ + uint16_t hi_rev; /* inclusive */ + uint8_t config_index; + uint8_t alt_index; + uint8_t got_list:1; + uint8_t got_bus:1; + uint8_t got_addr:1; + uint8_t got_iface:1; + uint8_t got_set_config:1; + uint8_t got_set_alt:1; + uint8_t got_set_owner:1; + uint8_t got_set_perm:1; + uint8_t got_suspend:1; + uint8_t got_resume:1; + uint8_t got_reset:1; + uint8_t got_power_off:1; + uint8_t got_power_save:1; + uint8_t got_power_on:1; + uint8_t got_dump_device_quirks:1; + uint8_t got_dump_quirk_names:1; + uint8_t got_dump_device_desc:1; + uint8_t got_dump_curr_config:1; + uint8_t got_dump_all_config:1; + uint8_t got_dump_info:1; + uint8_t got_dump_access:1; + uint8_t got_remove_device_quirk:1; + uint8_t got_add_device_quirk:1; +}; + +struct token { + const char *name; + uint8_t value; + uint8_t narg; +}; + +enum { + T_UNIT, + T_ADDR, + T_IFACE, + T_SET_CONFIG, + T_SET_ALT, + T_SET_OWNER, + T_SET_PERM, + T_ADD_DEVICE_QUIRK, + T_REMOVE_DEVICE_QUIRK, + T_DUMP_QUIRK_NAMES, + T_DUMP_DEVICE_QUIRKS, + T_DUMP_DEVICE_DESC, + T_DUMP_CURR_CONFIG_DESC, + T_DUMP_ALL_CONFIG_DESC, + T_DUMP_ACCESS, + T_DUMP_INFO, + T_SUSPEND, + T_RESUME, + T_POWER_OFF, + T_POWER_SAVE, + T_POWER_ON, + T_RESET, + T_LIST, +}; + +static struct options options; + +static const struct token token[] = { + {"-u", T_UNIT, 1}, + {"-a", T_ADDR, 1}, + {"-i", T_IFACE, 1}, + {"set_config", T_SET_CONFIG, 1}, + {"set_alt", T_SET_ALT, 1}, + {"set_owner", T_SET_OWNER, 1}, + {"set_perm", T_SET_PERM, 1}, + {"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5}, + {"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5}, + {"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0}, + {"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0}, + {"dump_device_desc", T_DUMP_DEVICE_DESC, 0}, + {"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0}, + {"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0}, + {"dump_access", T_DUMP_ACCESS, 0}, + {"dump_info", T_DUMP_INFO, 0}, + {"suspend", T_SUSPEND, 0}, + {"resume", T_RESUME, 0}, + {"power_off", T_POWER_OFF, 0}, + {"power_save", T_POWER_SAVE, 0}, + {"power_on", T_POWER_ON, 0}, + {"reset", T_RESET, 0}, + {"list", T_LIST, 0}, +}; + +static void +be_dev_remove_quirk(struct libusb20_backend *pbe, + uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev, + const char *str) +{ + struct libusb20_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.vid = vid; + q.pid = pid; + q.bcdDeviceLow = lorev; + q.bcdDeviceHigh = hirev; + strlcpy(q.quirkname, str, sizeof(q.quirkname)); + + err = libusb20_be_remove_dev_quirk(pbe, &q); + if (err) { + printf("Removing quirk '%s' failed, continuing.\n", str); + } + return; +} + +static void +be_dev_add_quirk(struct libusb20_backend *pbe, + uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev, + const char *str) +{ + struct libusb20_quirk q; + int err; + + memset(&q, 0, sizeof(q)); + + q.vid = vid; + q.pid = pid; + q.bcdDeviceLow = lorev; + q.bcdDeviceHigh = hirev; + strlcpy(q.quirkname, str, sizeof(q.quirkname)); + + err = libusb20_be_add_dev_quirk(pbe, &q); + if (err) { + printf("Adding quirk '%s' failed, continuing.\n", str); + } + return; +} + +static uint8_t +get_token(const char *str, uint8_t narg) +{ + uint8_t n; + + for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) { + if (strcasecmp(str, token[n].name) == 0) { + if (token[n].narg > narg) { + /* to little arguments */ + break; + } + return (token[n].value); + } + } + return (0 - 1); +} + +static uid_t +num_id(const char *name, const char *type) +{ + uid_t val; + char *ep; + + errno = 0; + val = strtoul(name, &ep, 0); + if (errno) { + err(1, "%s", name); + } + if (*ep != '\0') { + errx(1, "%s: illegal %s name", name, type); + } + return (val); +} + +static gid_t +a_gid(const char *s) +{ + struct group *gr; + + if (*s == '\0') { + /* empty group ID */ + return ((gid_t)-1); + } + return ((gr = getgrnam(s)) ? gr->gr_gid : num_id(s, "group")); +} + +static uid_t +a_uid(const char *s) +{ + struct passwd *pw; + + if (*s == '\0') { + /* empty user ID */ + return ((uid_t)-1); + } + return ((pw = getpwnam(s)) ? pw->pw_uid : num_id(s, "user")); +} + +static mode_t +a_mode(const char *s) +{ + uint16_t val; + char *ep; + + errno = 0; + val = strtoul(s, &ep, 8); + if (errno) { + err(1, "%s", s); + } + if (*ep != '\0') { + errx(1, "illegal permissions: %s", s); + } + return val; +} + +static void +usage(void) +{ + printf("" + "usbconfig - configure the USB subsystem" "\n" + "usage: usbconfig -u -a -i [cmds...]" "\n" + "commands:" "\n" + " set_config " "\n" + " set_alt " "\n" + " set_owner " "\n" + " set_perm " "\n" + " add_dev_quirk_vplh " "\n" + " remove_dev_quirk_vplh " "\n" + " dump_quirk_names" "\n" + " dump_device_quirks" "\n" + " dump_device_desc" "\n" + " dump_curr_config_desc" "\n" + " dump_all_config_desc" "\n" + " dump_access" "\n" + " dump_info" "\n" + " suspend" "\n" + " resume" "\n" + " power_off" "\n" + " power_save" "\n" + " power_on" "\n" + " reset" "\n" + " list" "\n" + ); + exit(1); +} + +static void +reset_options(struct options *opt) +{ + memset(opt, 0, sizeof(*opt)); + return; +} + +static void +flush_command(struct libusb20_backend *pbe, struct options *opt) +{ + struct libusb20_device *pdev = NULL; + uint32_t matches = 0; + uint8_t dump_any; + + /* check for invalid option combinations */ + if ((opt->got_suspend + + opt->got_resume + + opt->got_reset + + opt->got_set_config + + opt->got_set_alt + + opt->got_power_save + + opt->got_power_on + + opt->got_power_off) > 1) { + err(1, "cannot only specify one of 'set_config', " + "'set_alt', 'reset', 'suspend', 'resume', " + "'power_save', 'power_on' and 'power_off' " + "at the same time!"); + } + if (opt->got_dump_access) { + opt->got_any--; + dump_be_access(pbe); + } + if (opt->got_dump_quirk_names) { + opt->got_any--; + dump_be_quirk_names(pbe); + } + if (opt->got_dump_device_quirks) { + opt->got_any--; + dump_be_dev_quirks(pbe); + } + if (opt->got_remove_device_quirk) { + opt->got_any--; + be_dev_remove_quirk(pbe, + opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname); + } + if (opt->got_add_device_quirk) { + opt->got_any--; + be_dev_add_quirk(pbe, + opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname); + } + if (opt->got_any == 0) { + /* + * do not scan through all the devices if there are no valid + * options + */ + goto done; + } + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + + if (opt->got_bus && + (libusb20_dev_get_bus_number(pdev) != opt->bus)) { + continue; + } + if (opt->got_addr && + (libusb20_dev_get_address(pdev) != opt->addr)) { + continue; + } + matches++; + + /* do owner and permissions first */ + + if (opt->got_bus && opt->got_addr && opt->got_iface) { + if (opt->got_set_owner) { + if (libusb20_dev_set_iface_owner(pdev, + opt->iface, opt->uid, opt->gid)) { + err(1, "setting owner and group failed\n"); + } + } + if (opt->got_set_perm) { + if (libusb20_dev_set_iface_perm(pdev, + opt->iface, opt->mode)) { + err(1, "setting mode failed\n"); + } + } + } else if (opt->got_bus && opt->got_addr) { + if (opt->got_set_owner) { + if (libusb20_dev_set_owner(pdev, + opt->uid, opt->gid)) { + err(1, "setting owner and group failed\n"); + } + } + if (opt->got_set_perm) { + if (libusb20_dev_set_perm(pdev, + opt->mode)) { + err(1, "setting mode failed\n"); + } + } + } else if (opt->got_bus) { + if (opt->got_set_owner) { + if (libusb20_bus_set_owner(pbe, opt->bus, + opt->uid, opt->gid)) { + err(1, "setting owner and group failed\n"); + } + } + if (opt->got_set_perm) { + if (libusb20_bus_set_perm(pbe, opt->bus, + opt->mode)) { + err(1, "setting mode failed\n"); + } + } + } else { + if (opt->got_set_owner) { + if (libusb20_be_set_owner(pbe, + opt->uid, opt->gid)) { + err(1, "setting owner and group failed\n"); + } + } + if (opt->got_set_perm) { + if (libusb20_be_set_perm(pbe, + opt->mode)) { + err(1, "setting mode failed\n"); + } + } + } + + if (libusb20_dev_open(pdev, 0)) { + err(1, "could not open device"); + } + if (opt->got_set_config) { + if (libusb20_dev_set_config_index(pdev, + opt->config_index)) { + err(1, "could not set config index"); + } + } + if (opt->got_set_alt) { + if (libusb20_dev_set_alt_index(pdev, opt->iface, opt->alt_index)) { + err(1, "could not set alternate setting"); + } + } + if (opt->got_reset) { + if (libusb20_dev_reset(pdev)) { + err(1, "could not reset device"); + } + } + if (opt->got_suspend) { + if (libusb20_dev_set_power_mode(pdev, LIBUSB20_POWER_SUSPEND)) { + err(1, "could not set suspend"); + } + } + if (opt->got_resume) { + if (libusb20_dev_set_power_mode(pdev, LIBUSB20_POWER_RESUME)) { + err(1, "could not set resume"); + } + } + if (opt->got_power_off) { + if (libusb20_dev_set_power_mode(pdev, LIBUSB20_POWER_OFF)) { + err(1, "could not set power OFF"); + } + } + if (opt->got_power_save) { + if (libusb20_dev_set_power_mode(pdev, LIBUSB20_POWER_SAVE)) { + err(1, "could not set power SAVE"); + } + } + if (opt->got_power_on) { + if (libusb20_dev_set_power_mode(pdev, LIBUSB20_POWER_ON)) { + err(1, "could not set power ON"); + } + } + dump_any = + (opt->got_dump_device_desc || + opt->got_dump_curr_config || + opt->got_dump_all_config || + opt->got_dump_info || + opt->got_dump_access); + + if (opt->got_list || dump_any) { + dump_device_info(pdev); + } + if (opt->got_dump_access) { + printf("\n"); + dump_device_access(pdev, opt->got_iface ? + opt->iface : 0xFF); + } + if (opt->got_dump_device_desc) { + printf("\n"); + dump_device_desc(pdev); + } + if (opt->got_dump_all_config) { + printf("\n"); + dump_config(pdev, 1); + } else if (opt->got_dump_curr_config) { + printf("\n"); + dump_config(pdev, 0); + } + if (dump_any) { + printf("\n"); + } + if (libusb20_dev_close(pdev)) { + err(1, "could not close device"); + } + } + + if (matches == 0) { + printf("No device match\n"); + } +done: + reset_options(opt); + + return; +} + +int +main(int argc, char **argv) +{ + struct libusb20_backend *pbe; + struct options *opt = &options; + char *cp; + int n; + int t; + + if (argc < 1) { + usage(); + } + pbe = libusb20_be_alloc_default(); + if (pbe == NULL) + err(1, "could not access USB backend\n"); + + for (n = 1; n != argc; n++) { + + /* get number of additional options */ + t = (argc - n - 1); + if (t > 255) + t = 255; + switch (get_token(argv[n], t)) { + case T_ADD_DEVICE_QUIRK: + if (opt->got_add_device_quirk) { + flush_command(pbe, opt); + } + opt->vid = num_id(argv[n + 1], "Vendor ID"); + opt->pid = num_id(argv[n + 2], "Product ID"); + opt->lo_rev = num_id(argv[n + 3], "Low Revision"); + opt->hi_rev = num_id(argv[n + 4], "High Revision"); + opt->quirkname = argv[n + 5]; + n += 5; + + opt->got_add_device_quirk = 1; + opt->got_any++; + break; + + case T_REMOVE_DEVICE_QUIRK: + if (opt->got_remove_device_quirk) { + flush_command(pbe, opt); + } + opt->vid = num_id(argv[n + 1], "Vendor ID"); + opt->pid = num_id(argv[n + 2], "Product ID"); + opt->lo_rev = num_id(argv[n + 3], "Low Revision"); + opt->hi_rev = num_id(argv[n + 4], "High Revision"); + opt->quirkname = argv[n + 5]; + n += 5; + opt->got_remove_device_quirk = 1; + opt->got_any++; + break; + + case T_DUMP_QUIRK_NAMES: + opt->got_dump_quirk_names = 1; + opt->got_any++; + break; + + case T_DUMP_DEVICE_QUIRKS: + opt->got_dump_device_quirks = 1; + opt->got_any++; + break; + + case T_UNIT: + if (opt->got_any) { + /* allow multiple commands on the same line */ + flush_command(pbe, opt); + } + opt->bus = num_id(argv[n + 1], "busnum"); + opt->got_bus = 1; + n++; + break; + case T_ADDR: + opt->addr = num_id(argv[n + 1], "addr"); + opt->got_addr = 1; + n++; + break; + case T_IFACE: + opt->iface = num_id(argv[n + 1], "iface"); + opt->got_iface = 1; + n++; + break; + case T_SET_CONFIG: + opt->config_index = num_id(argv[n + 1], "confindex"); + opt->got_set_config = 1; + opt->got_any++; + n++; + break; + case T_SET_ALT: + opt->alt_index = num_id(argv[n + 1], "confindex"); + opt->got_set_alt = 1; + opt->got_any++; + n++; + break; + case T_SET_OWNER: + cp = argv[n + 1]; + cp = strchr(cp, ':'); + if (cp == NULL) { + err(1, "missing colon in '%s'!", argv[n + 1]); + } + *(cp++) = '\0'; + opt->gid = a_gid(cp); + opt->uid = a_uid(argv[n + 1]); + opt->got_set_owner = 1; + opt->got_any++; + n++; + break; + case T_SET_PERM: + opt->mode = a_mode(argv[n + 1]); + opt->got_set_perm = 1; + opt->got_any++; + n++; + break; + case T_DUMP_DEVICE_DESC: + opt->got_dump_device_desc = 1; + opt->got_any++; + break; + case T_DUMP_CURR_CONFIG_DESC: + opt->got_dump_curr_config = 1; + opt->got_any++; + break; + case T_DUMP_ALL_CONFIG_DESC: + opt->got_dump_all_config = 1; + opt->got_any++; + break; + case T_DUMP_INFO: + opt->got_dump_info = 1; + opt->got_any++; + break; + case T_DUMP_ACCESS: + opt->got_dump_access = 1; + opt->got_any++; + break; + case T_SUSPEND: + opt->got_suspend = 1; + opt->got_any++; + break; + case T_RESUME: + opt->got_resume = 1; + opt->got_any++; + break; + case T_POWER_OFF: + opt->got_power_off = 1; + opt->got_any++; + break; + case T_POWER_SAVE: + opt->got_power_save = 1; + opt->got_any++; + break; + case T_POWER_ON: + opt->got_power_on = 1; + opt->got_any++; + break; + case T_RESET: + opt->got_reset = 1; + opt->got_any++; + break; + case T_LIST: + opt->got_list = 1; + opt->got_any++; + break; + default: + usage(); + break; + } + } + if (opt->got_any) { + /* flush out last command */ + flush_command(pbe, opt); + } else { + /* list all the devices */ + opt->got_list = 1; + opt->got_any++; + flush_command(pbe, opt); + } + /* release data */ + libusb20_be_free(pbe); + + return (0); +}