From 111a707d99983acd73c4212036000aa3e88425b6 Mon Sep 17 00:00:00 2001 From: thompsa Date: Mon, 23 Feb 2009 18:31:00 +0000 Subject: Move the new USB stack into its new home. --- sys/dev/usb/README.TXT | 411 +++ sys/dev/usb/bluetooth/TODO.TXT | 18 + sys/dev/usb/bluetooth/ng_ubt.c | 1718 +++++++++++ sys/dev/usb/bluetooth/ng_ubt_var.h | 131 + sys/dev/usb/bluetooth/ubtbcmfw.c | 430 +++ sys/dev/usb/controller/at91dci.c | 2467 ++++++++++++++++ sys/dev/usb/controller/at91dci.h | 245 ++ sys/dev/usb/controller/at91dci_atmelarm.c | 347 +++ sys/dev/usb/controller/atmegadci.c | 2327 +++++++++++++++ sys/dev/usb/controller/atmegadci.h | 273 ++ sys/dev/usb/controller/atmegadci_atmelarm.c | 27 + sys/dev/usb/controller/ehci.c | 3965 ++++++++++++++++++++++++++ sys/dev/usb/controller/ehci.h | 532 ++++ sys/dev/usb/controller/ehci_ixp4xx.c | 348 +++ sys/dev/usb/controller/ehci_mbus.c | 364 +++ sys/dev/usb/controller/ehci_pci.c | 486 ++++ sys/dev/usb/controller/musb_otg.c | 2875 +++++++++++++++++++ sys/dev/usb/controller/musb_otg.h | 407 +++ sys/dev/usb/controller/musb_otg_atmelarm.c | 239 ++ sys/dev/usb/controller/ohci.c | 2862 +++++++++++++++++++ sys/dev/usb/controller/ohci.h | 366 +++ sys/dev/usb/controller/ohci_atmelarm.c | 223 ++ sys/dev/usb/controller/ohci_pci.c | 387 +++ sys/dev/usb/controller/uhci.c | 3381 ++++++++++++++++++++++ sys/dev/usb/controller/uhci.h | 321 +++ sys/dev/usb/controller/uhci_pci.c | 443 +++ sys/dev/usb/controller/usb_controller.c | 620 ++++ sys/dev/usb/controller/uss820dci.c | 2489 ++++++++++++++++ sys/dev/usb/controller/uss820dci.h | 377 +++ sys/dev/usb/controller/uss820dci_atmelarm.c | 238 ++ sys/dev/usb/image/uscanner.c | 643 +++++ sys/dev/usb/input/uhid.c | 789 +++++ sys/dev/usb/input/ukbd.c | 1489 ++++++++++ sys/dev/usb/input/ums.c | 901 ++++++ sys/dev/usb/input/usb_rdesc.h | 276 ++ sys/dev/usb/misc/udbp.c | 853 ++++++ sys/dev/usb/misc/udbp.h | 80 + sys/dev/usb/misc/ufm.c | 329 +++ sys/dev/usb/net/if_aue.c | 1054 +++++++ sys/dev/usb/net/if_auereg.h | 220 ++ sys/dev/usb/net/if_axe.c | 1076 +++++++ sys/dev/usb/net/if_axereg.h | 196 ++ sys/dev/usb/net/if_cdce.c | 771 +++++ sys/dev/usb/net/if_cdcereg.h | 68 + sys/dev/usb/net/if_cue.c | 645 +++++ sys/dev/usb/net/if_cuereg.h | 132 + sys/dev/usb/net/if_kue.c | 704 +++++ sys/dev/usb/net/if_kuefw.h | 685 +++++ sys/dev/usb/net/if_kuereg.h | 141 + sys/dev/usb/net/if_rue.c | 913 ++++++ sys/dev/usb/net/if_ruereg.h | 183 ++ sys/dev/usb/net/if_udav.c | 856 ++++++ sys/dev/usb/net/if_udavreg.h | 166 ++ sys/dev/usb/net/usb_ethernet.c | 587 ++++ sys/dev/usb/net/usb_ethernet.h | 122 + sys/dev/usb/quirk/usb_quirk.c | 397 +++ sys/dev/usb/quirk/usb_quirk.h | 59 + sys/dev/usb/serial/u3g.c | 583 ++++ sys/dev/usb/serial/uark.c | 407 +++ sys/dev/usb/serial/ubsa.c | 634 ++++ sys/dev/usb/serial/ubser.c | 518 ++++ sys/dev/usb/serial/uchcom.c | 883 ++++++ sys/dev/usb/serial/ucycom.c | 564 ++++ sys/dev/usb/serial/ufoma.c | 1212 ++++++++ sys/dev/usb/serial/uftdi.c | 784 +++++ sys/dev/usb/serial/uftdi_reg.h | 340 +++ sys/dev/usb/serial/ugensa.c | 352 +++ sys/dev/usb/serial/uipaq.c | 1314 +++++++++ sys/dev/usb/serial/ulpt.c | 726 +++++ sys/dev/usb/serial/umct.c | 579 ++++ sys/dev/usb/serial/umodem.c | 788 +++++ sys/dev/usb/serial/umoscom.c | 672 +++++ sys/dev/usb/serial/uplcom.c | 831 ++++++ sys/dev/usb/serial/usb_serial.c | 1127 ++++++++ sys/dev/usb/serial/usb_serial.h | 198 ++ sys/dev/usb/serial/uslcom.c | 539 ++++ sys/dev/usb/serial/uvisor.c | 610 ++++ sys/dev/usb/serial/uvscom.c | 707 +++++ sys/dev/usb/sound/uaudio.c | 3750 ++++++++++++++++++++++++ sys/dev/usb/sound/uaudio.h | 63 + sys/dev/usb/sound/uaudio_pcm.c | 234 ++ sys/dev/usb/sound/uaudio_reg.h | 406 +++ sys/dev/usb/storage/ata-usb.c | 1102 +++++++ sys/dev/usb/storage/rio500_usb.h | 48 + sys/dev/usb/storage/umass.c | 3619 +++++++++++++++++++++++ sys/dev/usb/storage/urio.c | 479 ++++ sys/dev/usb/storage/ustorage_fs.c | 1897 ++++++++++++ sys/dev/usb/template/usb_template.c | 1312 +++++++++ sys/dev/usb/template/usb_template.h | 102 + sys/dev/usb/template/usb_template_cdce.c | 292 ++ sys/dev/usb/template/usb_template_msc.c | 199 ++ sys/dev/usb/template/usb_template_mtp.c | 262 ++ sys/dev/usb/ufm_ioctl.h | 39 + sys/dev/usb/usb.h | 619 ++++ sys/dev/usb/usb_bus.h | 104 + sys/dev/usb/usb_busdma.c | 1426 +++++++++ sys/dev/usb/usb_busdma.h | 183 ++ sys/dev/usb/usb_cdc.h | 191 ++ sys/dev/usb/usb_compat_linux.c | 1653 +++++++++++ sys/dev/usb/usb_compat_linux.h | 472 +++ sys/dev/usb/usb_controller.h | 199 ++ sys/dev/usb/usb_core.c | 40 + sys/dev/usb/usb_core.h | 467 +++ sys/dev/usb/usb_debug.c | 152 + sys/dev/usb/usb_debug.h | 70 + sys/dev/usb/usb_defs.h | 77 + sys/dev/usb/usb_dev.c | 2814 ++++++++++++++++++ sys/dev/usb/usb_dev.h | 168 ++ sys/dev/usb/usb_device.c | 2192 ++++++++++++++ sys/dev/usb/usb_device.h | 187 ++ sys/dev/usb/usb_dynamic.c | 155 + sys/dev/usb/usb_dynamic.h | 70 + sys/dev/usb/usb_endian.h | 119 + sys/dev/usb/usb_error.c | 73 + sys/dev/usb/usb_error.h | 63 + sys/dev/usb/usb_generic.c | 2195 ++++++++++++++ sys/dev/usb/usb_generic.h | 33 + sys/dev/usb/usb_handle_request.c | 756 +++++ sys/dev/usb/usb_handle_request.h | 30 + sys/dev/usb/usb_hid.c | 582 ++++ sys/dev/usb/usb_hid.h | 95 + sys/dev/usb/usb_hub.c | 1842 ++++++++++++ sys/dev/usb/usb_hub.h | 80 + sys/dev/usb/usb_if.m | 52 + sys/dev/usb/usb_ioctl.h | 292 ++ sys/dev/usb/usb_lookup.c | 134 + sys/dev/usb/usb_lookup.h | 122 + sys/dev/usb/usb_mbuf.c | 77 + sys/dev/usb/usb_mbuf.h | 102 + sys/dev/usb/usb_mfunc.h | 78 + sys/dev/usb/usb_msctest.c | 578 ++++ sys/dev/usb/usb_msctest.h | 33 + sys/dev/usb/usb_parse.c | 225 ++ sys/dev/usb/usb_parse.h | 41 + sys/dev/usb/usb_pci.h | 39 + sys/dev/usb/usb_process.c | 426 +++ sys/dev/usb/usb_process.h | 88 + sys/dev/usb/usb_request.c | 1486 ++++++++++ sys/dev/usb/usb_request.h | 103 + sys/dev/usb/usb_revision.h | 65 + sys/dev/usb/usb_sw_transfer.c | 170 ++ sys/dev/usb/usb_sw_transfer.h | 62 + sys/dev/usb/usb_transfer.c | 2826 ++++++++++++++++++ sys/dev/usb/usb_transfer.h | 129 + sys/dev/usb/usb_util.c | 346 +++ sys/dev/usb/usb_util.h | 57 + sys/dev/usb/usbdevs | 2527 ++++++++++++++++ sys/dev/usb/usbhid.h | 175 ++ sys/dev/usb/wlan/if_rum.c | 2435 ++++++++++++++++ sys/dev/usb/wlan/if_rumfw.h | 213 ++ sys/dev/usb/wlan/if_rumreg.h | 235 ++ sys/dev/usb/wlan/if_rumvar.h | 156 + sys/dev/usb/wlan/if_ural.c | 2364 +++++++++++++++ sys/dev/usb/wlan/if_uralreg.h | 211 ++ sys/dev/usb/wlan/if_uralvar.h | 155 + sys/dev/usb/wlan/if_zyd.c | 3121 ++++++++++++++++++++ sys/dev/usb/wlan/if_zydfw.h | 1144 ++++++++ sys/dev/usb/wlan/if_zydreg.h | 1338 +++++++++ sys/dev/usb/wlan/usb_wlan.h | 57 + sys/dev/usb2/bluetooth/TODO.TXT | 18 - sys/dev/usb2/bluetooth/ng_ubt2.c | 1720 ----------- sys/dev/usb2/bluetooth/ng_ubt2_var.h | 131 - sys/dev/usb2/bluetooth/ubtbcmfw2.c | 431 --- sys/dev/usb2/bluetooth/usb2_bluetooth.c | 31 - sys/dev/usb2/bluetooth/usb2_bluetooth.h | 30 - sys/dev/usb2/controller/at91dci.c | 2467 ---------------- sys/dev/usb2/controller/at91dci.h | 245 -- sys/dev/usb2/controller/at91dci_atmelarm.c | 347 --- sys/dev/usb2/controller/atmegadci.c | 2327 --------------- sys/dev/usb2/controller/atmegadci.h | 273 -- sys/dev/usb2/controller/atmegadci_atmelarm.c | 27 - sys/dev/usb2/controller/ehci2.c | 3965 -------------------------- sys/dev/usb2/controller/ehci2.h | 532 ---- sys/dev/usb2/controller/ehci2_ixp4xx.c | 349 --- sys/dev/usb2/controller/ehci2_mbus.c | 365 --- sys/dev/usb2/controller/ehci2_pci.c | 487 ---- sys/dev/usb2/controller/musb2_otg.c | 2875 ------------------- sys/dev/usb2/controller/musb2_otg.h | 407 --- sys/dev/usb2/controller/musb2_otg_atmelarm.c | 240 -- sys/dev/usb2/controller/ohci2.c | 2862 ------------------- sys/dev/usb2/controller/ohci2.h | 366 --- sys/dev/usb2/controller/ohci2_atmelarm.c | 224 -- sys/dev/usb2/controller/ohci2_pci.c | 388 --- sys/dev/usb2/controller/uhci2.c | 3381 ---------------------- sys/dev/usb2/controller/uhci2.h | 321 --- sys/dev/usb2/controller/uhci2_pci.c | 444 --- sys/dev/usb2/controller/usb2_bus.h | 104 - sys/dev/usb2/controller/usb2_controller.c | 623 ---- sys/dev/usb2/controller/usb2_controller.h | 199 -- sys/dev/usb2/controller/usb2_pci.h | 39 - sys/dev/usb2/controller/uss820dci.c | 2489 ---------------- sys/dev/usb2/controller/uss820dci.h | 377 --- sys/dev/usb2/controller/uss820dci_atmelarm.c | 239 -- sys/dev/usb2/core/README.TXT | 411 --- sys/dev/usb2/core/usb2_busdma.c | 1426 --------- sys/dev/usb2/core/usb2_busdma.h | 183 -- sys/dev/usb2/core/usb2_compat_linux.c | 1653 ----------- sys/dev/usb2/core/usb2_compat_linux.h | 472 --- sys/dev/usb2/core/usb2_core.c | 40 - sys/dev/usb2/core/usb2_core.h | 467 --- sys/dev/usb2/core/usb2_debug.c | 152 - sys/dev/usb2/core/usb2_debug.h | 70 - sys/dev/usb2/core/usb2_dev.c | 2814 ------------------ sys/dev/usb2/core/usb2_dev.h | 168 -- sys/dev/usb2/core/usb2_device.c | 2192 -------------- sys/dev/usb2/core/usb2_device.h | 187 -- sys/dev/usb2/core/usb2_dynamic.c | 155 - sys/dev/usb2/core/usb2_dynamic.h | 70 - sys/dev/usb2/core/usb2_error.c | 73 - sys/dev/usb2/core/usb2_generic.c | 2195 -------------- sys/dev/usb2/core/usb2_generic.h | 33 - sys/dev/usb2/core/usb2_handle_request.c | 756 ----- sys/dev/usb2/core/usb2_handle_request.h | 30 - sys/dev/usb2/core/usb2_hid.c | 582 ---- sys/dev/usb2/core/usb2_hid.h | 95 - sys/dev/usb2/core/usb2_hub.c | 1842 ------------ sys/dev/usb2/core/usb2_hub.h | 80 - sys/dev/usb2/core/usb2_if.m | 52 - sys/dev/usb2/core/usb2_lookup.c | 134 - sys/dev/usb2/core/usb2_lookup.h | 122 - sys/dev/usb2/core/usb2_mbuf.c | 77 - sys/dev/usb2/core/usb2_mbuf.h | 102 - sys/dev/usb2/core/usb2_msctest.c | 578 ---- sys/dev/usb2/core/usb2_msctest.h | 33 - sys/dev/usb2/core/usb2_parse.c | 225 -- sys/dev/usb2/core/usb2_parse.h | 41 - sys/dev/usb2/core/usb2_process.c | 426 --- sys/dev/usb2/core/usb2_process.h | 88 - sys/dev/usb2/core/usb2_request.c | 1486 ---------- sys/dev/usb2/core/usb2_request.h | 103 - sys/dev/usb2/core/usb2_sw_transfer.c | 170 -- sys/dev/usb2/core/usb2_sw_transfer.h | 62 - sys/dev/usb2/core/usb2_transfer.c | 2826 ------------------ sys/dev/usb2/core/usb2_transfer.h | 129 - sys/dev/usb2/core/usb2_util.c | 346 --- sys/dev/usb2/core/usb2_util.h | 57 - sys/dev/usb2/ethernet/if_aue2.c | 1054 ------- sys/dev/usb2/ethernet/if_auereg.h | 220 -- sys/dev/usb2/ethernet/if_axe2.c | 1076 ------- sys/dev/usb2/ethernet/if_axereg.h | 196 -- sys/dev/usb2/ethernet/if_cdce2.c | 771 ----- sys/dev/usb2/ethernet/if_cdcereg.h | 68 - sys/dev/usb2/ethernet/if_cue2.c | 645 ----- sys/dev/usb2/ethernet/if_cuereg.h | 132 - sys/dev/usb2/ethernet/if_kue2.c | 704 ----- sys/dev/usb2/ethernet/if_kuefw.h | 685 ----- sys/dev/usb2/ethernet/if_kuereg.h | 141 - sys/dev/usb2/ethernet/if_rue2.c | 913 ------ sys/dev/usb2/ethernet/if_ruereg.h | 183 -- sys/dev/usb2/ethernet/if_udav2.c | 856 ------ sys/dev/usb2/ethernet/if_udavreg.h | 166 -- sys/dev/usb2/ethernet/usb2_ethernet.c | 587 ---- sys/dev/usb2/ethernet/usb2_ethernet.h | 122 - sys/dev/usb2/image/usb2_image.c | 31 - sys/dev/usb2/image/usb2_image.h | 30 - sys/dev/usb2/image/uscanner2.c | 644 ----- sys/dev/usb2/include/ufm2_ioctl.h | 39 - sys/dev/usb2/include/usb2_cdc.h | 191 -- sys/dev/usb2/include/usb2_defs.h | 77 - sys/dev/usb2/include/usb2_endian.h | 119 - sys/dev/usb2/include/usb2_error.h | 63 - sys/dev/usb2/include/usb2_hid.h | 175 -- sys/dev/usb2/include/usb2_ioctl.h | 292 -- sys/dev/usb2/include/usb2_mfunc.h | 78 - sys/dev/usb2/include/usb2_revision.h | 65 - sys/dev/usb2/include/usb2_standard.h | 619 ---- sys/dev/usb2/input/uhid2.c | 791 ----- sys/dev/usb2/input/ukbd2.c | 1492 ---------- sys/dev/usb2/input/ums2.c | 904 ------ 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 | 854 ------ sys/dev/usb2/misc/udbp2.h | 80 - sys/dev/usb2/misc/ufm2.c | 330 --- 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 | 397 --- sys/dev/usb2/quirk/usb2_quirk.h | 59 - sys/dev/usb2/serial/u3g2.c | 583 ---- sys/dev/usb2/serial/uark2.c | 407 --- sys/dev/usb2/serial/ubsa2.c | 634 ---- sys/dev/usb2/serial/ubser2.c | 518 ---- sys/dev/usb2/serial/uchcom2.c | 883 ------ sys/dev/usb2/serial/ucycom2.c | 564 ---- sys/dev/usb2/serial/ufoma2.c | 1212 -------- sys/dev/usb2/serial/uftdi2.c | 784 ----- sys/dev/usb2/serial/uftdi2_reg.h | 340 --- sys/dev/usb2/serial/ugensa2.c | 352 --- sys/dev/usb2/serial/uipaq2.c | 1314 --------- sys/dev/usb2/serial/ulpt2.c | 726 ----- sys/dev/usb2/serial/umct2.c | 579 ---- sys/dev/usb2/serial/umodem2.c | 788 ----- sys/dev/usb2/serial/umoscom2.c | 672 ----- sys/dev/usb2/serial/uplcom2.c | 831 ------ sys/dev/usb2/serial/usb2_serial.c | 1127 -------- sys/dev/usb2/serial/usb2_serial.h | 198 -- sys/dev/usb2/serial/uslcom2.c | 539 ---- sys/dev/usb2/serial/uvisor2.c | 610 ---- sys/dev/usb2/serial/uvscom2.c | 707 ----- sys/dev/usb2/sound/uaudio2.c | 3751 ------------------------ sys/dev/usb2/sound/uaudio2.h | 63 - 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 | 1103 ------- sys/dev/usb2/storage/umass2.c | 3620 ----------------------- sys/dev/usb2/storage/urio2.c | 480 ---- sys/dev/usb2/storage/usb2_storage.c | 31 - sys/dev/usb2/storage/usb2_storage.h | 30 - sys/dev/usb2/storage/ustorage2_fs.c | 1898 ------------ sys/dev/usb2/template/usb2_template.c | 1312 --------- sys/dev/usb2/template/usb2_template.h | 102 - sys/dev/usb2/template/usb2_template_cdce.c | 292 -- 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 | 2436 ---------------- sys/dev/usb2/wlan/if_rumfw.h | 213 -- sys/dev/usb2/wlan/if_rumreg.h | 235 -- sys/dev/usb2/wlan/if_rumvar.h | 156 - sys/dev/usb2/wlan/if_ural2.c | 2365 --------------- sys/dev/usb2/wlan/if_uralreg.h | 211 -- sys/dev/usb2/wlan/if_uralvar.h | 155 - sys/dev/usb2/wlan/if_zyd2.c | 3122 -------------------- sys/dev/usb2/wlan/if_zydfw.h | 1144 -------- sys/dev/usb2/wlan/if_zydreg.h | 1338 --------- sys/dev/usb2/wlan/usb2_wlan.c | 31 - sys/dev/usb2/wlan/usb2_wlan.h | 57 - 332 files changed, 110593 insertions(+), 108653 deletions(-) create mode 100644 sys/dev/usb/README.TXT create mode 100644 sys/dev/usb/bluetooth/TODO.TXT create mode 100644 sys/dev/usb/bluetooth/ng_ubt.c create mode 100644 sys/dev/usb/bluetooth/ng_ubt_var.h create mode 100644 sys/dev/usb/bluetooth/ubtbcmfw.c create mode 100644 sys/dev/usb/controller/at91dci.c create mode 100644 sys/dev/usb/controller/at91dci.h create mode 100644 sys/dev/usb/controller/at91dci_atmelarm.c create mode 100644 sys/dev/usb/controller/atmegadci.c create mode 100644 sys/dev/usb/controller/atmegadci.h create mode 100644 sys/dev/usb/controller/atmegadci_atmelarm.c create mode 100644 sys/dev/usb/controller/ehci.c create mode 100644 sys/dev/usb/controller/ehci.h create mode 100644 sys/dev/usb/controller/ehci_ixp4xx.c create mode 100644 sys/dev/usb/controller/ehci_mbus.c create mode 100644 sys/dev/usb/controller/ehci_pci.c create mode 100644 sys/dev/usb/controller/musb_otg.c create mode 100644 sys/dev/usb/controller/musb_otg.h create mode 100644 sys/dev/usb/controller/musb_otg_atmelarm.c create mode 100644 sys/dev/usb/controller/ohci.c create mode 100644 sys/dev/usb/controller/ohci.h create mode 100644 sys/dev/usb/controller/ohci_atmelarm.c create mode 100644 sys/dev/usb/controller/ohci_pci.c create mode 100644 sys/dev/usb/controller/uhci.c create mode 100644 sys/dev/usb/controller/uhci.h create mode 100644 sys/dev/usb/controller/uhci_pci.c create mode 100644 sys/dev/usb/controller/usb_controller.c create mode 100644 sys/dev/usb/controller/uss820dci.c create mode 100644 sys/dev/usb/controller/uss820dci.h create mode 100644 sys/dev/usb/controller/uss820dci_atmelarm.c create mode 100644 sys/dev/usb/image/uscanner.c create mode 100644 sys/dev/usb/input/uhid.c create mode 100644 sys/dev/usb/input/ukbd.c create mode 100644 sys/dev/usb/input/ums.c create mode 100644 sys/dev/usb/input/usb_rdesc.h create mode 100644 sys/dev/usb/misc/udbp.c create mode 100644 sys/dev/usb/misc/udbp.h create mode 100644 sys/dev/usb/misc/ufm.c create mode 100644 sys/dev/usb/net/if_aue.c create mode 100644 sys/dev/usb/net/if_auereg.h create mode 100644 sys/dev/usb/net/if_axe.c create mode 100644 sys/dev/usb/net/if_axereg.h create mode 100644 sys/dev/usb/net/if_cdce.c create mode 100644 sys/dev/usb/net/if_cdcereg.h create mode 100644 sys/dev/usb/net/if_cue.c create mode 100644 sys/dev/usb/net/if_cuereg.h create mode 100644 sys/dev/usb/net/if_kue.c create mode 100644 sys/dev/usb/net/if_kuefw.h create mode 100644 sys/dev/usb/net/if_kuereg.h create mode 100644 sys/dev/usb/net/if_rue.c create mode 100644 sys/dev/usb/net/if_ruereg.h create mode 100644 sys/dev/usb/net/if_udav.c create mode 100644 sys/dev/usb/net/if_udavreg.h create mode 100644 sys/dev/usb/net/usb_ethernet.c create mode 100644 sys/dev/usb/net/usb_ethernet.h create mode 100644 sys/dev/usb/quirk/usb_quirk.c create mode 100644 sys/dev/usb/quirk/usb_quirk.h create mode 100644 sys/dev/usb/serial/u3g.c create mode 100644 sys/dev/usb/serial/uark.c create mode 100644 sys/dev/usb/serial/ubsa.c create mode 100644 sys/dev/usb/serial/ubser.c create mode 100644 sys/dev/usb/serial/uchcom.c create mode 100644 sys/dev/usb/serial/ucycom.c create mode 100644 sys/dev/usb/serial/ufoma.c create mode 100644 sys/dev/usb/serial/uftdi.c create mode 100644 sys/dev/usb/serial/uftdi_reg.h create mode 100644 sys/dev/usb/serial/ugensa.c create mode 100644 sys/dev/usb/serial/uipaq.c create mode 100644 sys/dev/usb/serial/ulpt.c create mode 100644 sys/dev/usb/serial/umct.c create mode 100644 sys/dev/usb/serial/umodem.c create mode 100644 sys/dev/usb/serial/umoscom.c create mode 100644 sys/dev/usb/serial/uplcom.c create mode 100644 sys/dev/usb/serial/usb_serial.c create mode 100644 sys/dev/usb/serial/usb_serial.h create mode 100644 sys/dev/usb/serial/uslcom.c create mode 100644 sys/dev/usb/serial/uvisor.c create mode 100644 sys/dev/usb/serial/uvscom.c create mode 100644 sys/dev/usb/sound/uaudio.c create mode 100644 sys/dev/usb/sound/uaudio.h create mode 100644 sys/dev/usb/sound/uaudio_pcm.c create mode 100644 sys/dev/usb/sound/uaudio_reg.h create mode 100644 sys/dev/usb/storage/ata-usb.c create mode 100644 sys/dev/usb/storage/rio500_usb.h create mode 100644 sys/dev/usb/storage/umass.c create mode 100644 sys/dev/usb/storage/urio.c create mode 100644 sys/dev/usb/storage/ustorage_fs.c create mode 100644 sys/dev/usb/template/usb_template.c create mode 100644 sys/dev/usb/template/usb_template.h create mode 100644 sys/dev/usb/template/usb_template_cdce.c create mode 100644 sys/dev/usb/template/usb_template_msc.c create mode 100644 sys/dev/usb/template/usb_template_mtp.c create mode 100644 sys/dev/usb/ufm_ioctl.h create mode 100644 sys/dev/usb/usb.h create mode 100644 sys/dev/usb/usb_bus.h create mode 100644 sys/dev/usb/usb_busdma.c create mode 100644 sys/dev/usb/usb_busdma.h create mode 100644 sys/dev/usb/usb_cdc.h create mode 100644 sys/dev/usb/usb_compat_linux.c create mode 100644 sys/dev/usb/usb_compat_linux.h create mode 100644 sys/dev/usb/usb_controller.h create mode 100644 sys/dev/usb/usb_core.c create mode 100644 sys/dev/usb/usb_core.h create mode 100644 sys/dev/usb/usb_debug.c create mode 100644 sys/dev/usb/usb_debug.h create mode 100644 sys/dev/usb/usb_defs.h create mode 100644 sys/dev/usb/usb_dev.c create mode 100644 sys/dev/usb/usb_dev.h create mode 100644 sys/dev/usb/usb_device.c create mode 100644 sys/dev/usb/usb_device.h create mode 100644 sys/dev/usb/usb_dynamic.c create mode 100644 sys/dev/usb/usb_dynamic.h create mode 100644 sys/dev/usb/usb_endian.h create mode 100644 sys/dev/usb/usb_error.c create mode 100644 sys/dev/usb/usb_error.h create mode 100644 sys/dev/usb/usb_generic.c create mode 100644 sys/dev/usb/usb_generic.h create mode 100644 sys/dev/usb/usb_handle_request.c create mode 100644 sys/dev/usb/usb_handle_request.h create mode 100644 sys/dev/usb/usb_hid.c create mode 100644 sys/dev/usb/usb_hid.h create mode 100644 sys/dev/usb/usb_hub.c create mode 100644 sys/dev/usb/usb_hub.h create mode 100644 sys/dev/usb/usb_if.m create mode 100644 sys/dev/usb/usb_ioctl.h create mode 100644 sys/dev/usb/usb_lookup.c create mode 100644 sys/dev/usb/usb_lookup.h create mode 100644 sys/dev/usb/usb_mbuf.c create mode 100644 sys/dev/usb/usb_mbuf.h create mode 100644 sys/dev/usb/usb_mfunc.h create mode 100644 sys/dev/usb/usb_msctest.c create mode 100644 sys/dev/usb/usb_msctest.h create mode 100644 sys/dev/usb/usb_parse.c create mode 100644 sys/dev/usb/usb_parse.h create mode 100644 sys/dev/usb/usb_pci.h create mode 100644 sys/dev/usb/usb_process.c create mode 100644 sys/dev/usb/usb_process.h create mode 100644 sys/dev/usb/usb_request.c create mode 100644 sys/dev/usb/usb_request.h create mode 100644 sys/dev/usb/usb_revision.h create mode 100644 sys/dev/usb/usb_sw_transfer.c create mode 100644 sys/dev/usb/usb_sw_transfer.h create mode 100644 sys/dev/usb/usb_transfer.c create mode 100644 sys/dev/usb/usb_transfer.h create mode 100644 sys/dev/usb/usb_util.c create mode 100644 sys/dev/usb/usb_util.h create mode 100644 sys/dev/usb/usbdevs create mode 100644 sys/dev/usb/usbhid.h create mode 100644 sys/dev/usb/wlan/if_rum.c create mode 100644 sys/dev/usb/wlan/if_rumfw.h create mode 100644 sys/dev/usb/wlan/if_rumreg.h create mode 100644 sys/dev/usb/wlan/if_rumvar.h create mode 100644 sys/dev/usb/wlan/if_ural.c create mode 100644 sys/dev/usb/wlan/if_uralreg.h create mode 100644 sys/dev/usb/wlan/if_uralvar.h create mode 100644 sys/dev/usb/wlan/if_zyd.c create mode 100644 sys/dev/usb/wlan/if_zydfw.h create mode 100644 sys/dev/usb/wlan/if_zydreg.h create mode 100644 sys/dev/usb/wlan/usb_wlan.h delete mode 100644 sys/dev/usb2/bluetooth/TODO.TXT delete mode 100644 sys/dev/usb2/bluetooth/ng_ubt2.c delete mode 100644 sys/dev/usb2/bluetooth/ng_ubt2_var.h delete mode 100644 sys/dev/usb2/bluetooth/ubtbcmfw2.c delete mode 100644 sys/dev/usb2/bluetooth/usb2_bluetooth.c delete mode 100644 sys/dev/usb2/bluetooth/usb2_bluetooth.h delete mode 100644 sys/dev/usb2/controller/at91dci.c delete mode 100644 sys/dev/usb2/controller/at91dci.h delete mode 100644 sys/dev/usb2/controller/at91dci_atmelarm.c delete mode 100644 sys/dev/usb2/controller/atmegadci.c delete mode 100644 sys/dev/usb2/controller/atmegadci.h delete mode 100644 sys/dev/usb2/controller/atmegadci_atmelarm.c delete mode 100644 sys/dev/usb2/controller/ehci2.c delete mode 100644 sys/dev/usb2/controller/ehci2.h delete mode 100644 sys/dev/usb2/controller/ehci2_ixp4xx.c delete mode 100644 sys/dev/usb2/controller/ehci2_mbus.c delete mode 100644 sys/dev/usb2/controller/ehci2_pci.c delete mode 100644 sys/dev/usb2/controller/musb2_otg.c delete mode 100644 sys/dev/usb2/controller/musb2_otg.h delete mode 100644 sys/dev/usb2/controller/musb2_otg_atmelarm.c delete mode 100644 sys/dev/usb2/controller/ohci2.c delete mode 100644 sys/dev/usb2/controller/ohci2.h delete mode 100644 sys/dev/usb2/controller/ohci2_atmelarm.c delete mode 100644 sys/dev/usb2/controller/ohci2_pci.c delete mode 100644 sys/dev/usb2/controller/uhci2.c delete mode 100644 sys/dev/usb2/controller/uhci2.h delete mode 100644 sys/dev/usb2/controller/uhci2_pci.c delete mode 100644 sys/dev/usb2/controller/usb2_bus.h delete mode 100644 sys/dev/usb2/controller/usb2_controller.c delete mode 100644 sys/dev/usb2/controller/usb2_controller.h delete mode 100644 sys/dev/usb2/controller/usb2_pci.h delete mode 100644 sys/dev/usb2/controller/uss820dci.c delete mode 100644 sys/dev/usb2/controller/uss820dci.h delete mode 100644 sys/dev/usb2/controller/uss820dci_atmelarm.c delete mode 100644 sys/dev/usb2/core/README.TXT delete mode 100644 sys/dev/usb2/core/usb2_busdma.c delete mode 100644 sys/dev/usb2/core/usb2_busdma.h delete mode 100644 sys/dev/usb2/core/usb2_compat_linux.c delete mode 100644 sys/dev/usb2/core/usb2_compat_linux.h delete mode 100644 sys/dev/usb2/core/usb2_core.c delete mode 100644 sys/dev/usb2/core/usb2_core.h delete mode 100644 sys/dev/usb2/core/usb2_debug.c delete mode 100644 sys/dev/usb2/core/usb2_debug.h delete mode 100644 sys/dev/usb2/core/usb2_dev.c delete mode 100644 sys/dev/usb2/core/usb2_dev.h delete mode 100644 sys/dev/usb2/core/usb2_device.c delete mode 100644 sys/dev/usb2/core/usb2_device.h delete mode 100644 sys/dev/usb2/core/usb2_dynamic.c delete mode 100644 sys/dev/usb2/core/usb2_dynamic.h delete mode 100644 sys/dev/usb2/core/usb2_error.c delete mode 100644 sys/dev/usb2/core/usb2_generic.c delete mode 100644 sys/dev/usb2/core/usb2_generic.h delete mode 100644 sys/dev/usb2/core/usb2_handle_request.c delete mode 100644 sys/dev/usb2/core/usb2_handle_request.h delete mode 100644 sys/dev/usb2/core/usb2_hid.c delete mode 100644 sys/dev/usb2/core/usb2_hid.h delete mode 100644 sys/dev/usb2/core/usb2_hub.c delete mode 100644 sys/dev/usb2/core/usb2_hub.h delete mode 100644 sys/dev/usb2/core/usb2_if.m delete mode 100644 sys/dev/usb2/core/usb2_lookup.c delete mode 100644 sys/dev/usb2/core/usb2_lookup.h delete mode 100644 sys/dev/usb2/core/usb2_mbuf.c delete mode 100644 sys/dev/usb2/core/usb2_mbuf.h delete mode 100644 sys/dev/usb2/core/usb2_msctest.c delete mode 100644 sys/dev/usb2/core/usb2_msctest.h delete mode 100644 sys/dev/usb2/core/usb2_parse.c delete mode 100644 sys/dev/usb2/core/usb2_parse.h delete mode 100644 sys/dev/usb2/core/usb2_process.c delete mode 100644 sys/dev/usb2/core/usb2_process.h delete mode 100644 sys/dev/usb2/core/usb2_request.c delete mode 100644 sys/dev/usb2/core/usb2_request.h delete mode 100644 sys/dev/usb2/core/usb2_sw_transfer.c delete mode 100644 sys/dev/usb2/core/usb2_sw_transfer.h delete mode 100644 sys/dev/usb2/core/usb2_transfer.c delete mode 100644 sys/dev/usb2/core/usb2_transfer.h delete mode 100644 sys/dev/usb2/core/usb2_util.c delete mode 100644 sys/dev/usb2/core/usb2_util.h delete mode 100644 sys/dev/usb2/ethernet/if_aue2.c delete mode 100644 sys/dev/usb2/ethernet/if_auereg.h delete mode 100644 sys/dev/usb2/ethernet/if_axe2.c delete mode 100644 sys/dev/usb2/ethernet/if_axereg.h delete mode 100644 sys/dev/usb2/ethernet/if_cdce2.c delete mode 100644 sys/dev/usb2/ethernet/if_cdcereg.h delete mode 100644 sys/dev/usb2/ethernet/if_cue2.c delete mode 100644 sys/dev/usb2/ethernet/if_cuereg.h delete mode 100644 sys/dev/usb2/ethernet/if_kue2.c delete mode 100644 sys/dev/usb2/ethernet/if_kuefw.h delete mode 100644 sys/dev/usb2/ethernet/if_kuereg.h delete mode 100644 sys/dev/usb2/ethernet/if_rue2.c delete mode 100644 sys/dev/usb2/ethernet/if_ruereg.h delete mode 100644 sys/dev/usb2/ethernet/if_udav2.c delete mode 100644 sys/dev/usb2/ethernet/if_udavreg.h delete mode 100644 sys/dev/usb2/ethernet/usb2_ethernet.c delete mode 100644 sys/dev/usb2/ethernet/usb2_ethernet.h delete mode 100644 sys/dev/usb2/image/usb2_image.c delete mode 100644 sys/dev/usb2/image/usb2_image.h delete mode 100644 sys/dev/usb2/image/uscanner2.c delete mode 100644 sys/dev/usb2/include/ufm2_ioctl.h delete mode 100644 sys/dev/usb2/include/usb2_cdc.h delete mode 100644 sys/dev/usb2/include/usb2_defs.h delete mode 100644 sys/dev/usb2/include/usb2_endian.h delete mode 100644 sys/dev/usb2/include/usb2_error.h delete mode 100644 sys/dev/usb2/include/usb2_hid.h delete mode 100644 sys/dev/usb2/include/usb2_ioctl.h delete mode 100644 sys/dev/usb2/include/usb2_mfunc.h delete mode 100644 sys/dev/usb2/include/usb2_revision.h delete mode 100644 sys/dev/usb2/include/usb2_standard.h delete mode 100644 sys/dev/usb2/input/uhid2.c delete mode 100644 sys/dev/usb2/input/ukbd2.c delete mode 100644 sys/dev/usb2/input/ums2.c delete mode 100644 sys/dev/usb2/input/usb2_input.c delete mode 100644 sys/dev/usb2/input/usb2_input.h delete mode 100644 sys/dev/usb2/input/usb2_rdesc.h delete mode 100644 sys/dev/usb2/misc/udbp2.c delete mode 100644 sys/dev/usb2/misc/udbp2.h delete mode 100644 sys/dev/usb2/misc/ufm2.c delete mode 100644 sys/dev/usb2/misc/usb2_misc.c delete mode 100644 sys/dev/usb2/misc/usb2_misc.h delete mode 100644 sys/dev/usb2/ndis/if_ndis_usb2.c delete mode 100644 sys/dev/usb2/ndis/usb2_ndis.c delete mode 100644 sys/dev/usb2/ndis/usb2_ndis.h delete mode 100644 sys/dev/usb2/quirk/usb2_quirk.c delete mode 100644 sys/dev/usb2/quirk/usb2_quirk.h delete mode 100644 sys/dev/usb2/serial/u3g2.c delete mode 100644 sys/dev/usb2/serial/uark2.c delete mode 100644 sys/dev/usb2/serial/ubsa2.c delete mode 100644 sys/dev/usb2/serial/ubser2.c delete mode 100644 sys/dev/usb2/serial/uchcom2.c delete mode 100644 sys/dev/usb2/serial/ucycom2.c delete mode 100644 sys/dev/usb2/serial/ufoma2.c delete mode 100644 sys/dev/usb2/serial/uftdi2.c delete mode 100644 sys/dev/usb2/serial/uftdi2_reg.h delete mode 100644 sys/dev/usb2/serial/ugensa2.c delete mode 100644 sys/dev/usb2/serial/uipaq2.c delete mode 100644 sys/dev/usb2/serial/ulpt2.c delete mode 100644 sys/dev/usb2/serial/umct2.c delete mode 100644 sys/dev/usb2/serial/umodem2.c delete mode 100644 sys/dev/usb2/serial/umoscom2.c delete mode 100644 sys/dev/usb2/serial/uplcom2.c delete mode 100644 sys/dev/usb2/serial/usb2_serial.c delete mode 100644 sys/dev/usb2/serial/usb2_serial.h delete mode 100644 sys/dev/usb2/serial/uslcom2.c delete mode 100644 sys/dev/usb2/serial/uvisor2.c delete mode 100644 sys/dev/usb2/serial/uvscom2.c delete mode 100644 sys/dev/usb2/sound/uaudio2.c delete mode 100644 sys/dev/usb2/sound/uaudio2.h delete mode 100644 sys/dev/usb2/sound/uaudio2_pcm.c delete mode 100644 sys/dev/usb2/sound/uaudio2_reg.h delete mode 100644 sys/dev/usb2/sound/usb2_sound.c delete mode 100644 sys/dev/usb2/sound/usb2_sound.h delete mode 100644 sys/dev/usb2/storage/ata-usb2.c delete mode 100644 sys/dev/usb2/storage/umass2.c delete mode 100644 sys/dev/usb2/storage/urio2.c delete mode 100644 sys/dev/usb2/storage/usb2_storage.c delete mode 100644 sys/dev/usb2/storage/usb2_storage.h delete mode 100644 sys/dev/usb2/storage/ustorage2_fs.c delete mode 100644 sys/dev/usb2/template/usb2_template.c delete mode 100644 sys/dev/usb2/template/usb2_template.h delete mode 100644 sys/dev/usb2/template/usb2_template_cdce.c delete mode 100644 sys/dev/usb2/template/usb2_template_msc.c delete mode 100644 sys/dev/usb2/template/usb2_template_mtp.c delete mode 100644 sys/dev/usb2/wlan/if_rum2.c delete mode 100644 sys/dev/usb2/wlan/if_rumfw.h delete mode 100644 sys/dev/usb2/wlan/if_rumreg.h delete mode 100644 sys/dev/usb2/wlan/if_rumvar.h delete mode 100644 sys/dev/usb2/wlan/if_ural2.c delete mode 100644 sys/dev/usb2/wlan/if_uralreg.h delete mode 100644 sys/dev/usb2/wlan/if_uralvar.h delete mode 100644 sys/dev/usb2/wlan/if_zyd2.c delete mode 100644 sys/dev/usb2/wlan/if_zydfw.h delete mode 100644 sys/dev/usb2/wlan/if_zydreg.h delete mode 100644 sys/dev/usb2/wlan/usb2_wlan.c delete mode 100644 sys/dev/usb2/wlan/usb2_wlan.h diff --git a/sys/dev/usb/README.TXT b/sys/dev/usb/README.TXT new file mode 100644 index 0000000..d24770c --- /dev/null +++ b/sys/dev/usb/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/usb/usb_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/usb/net/if_axe.c +/sys/dev/usb/net/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/usb/bluetooth/TODO.TXT b/sys/dev/usb/bluetooth/TODO.TXT new file mode 100644 index 0000000..b0d6695 --- /dev/null +++ b/sys/dev/usb/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/usb/bluetooth/ng_ubt.c b/sys/dev/usb/bluetooth/ng_ubt.c new file mode 100644 index 0000000..07ac2194 --- /dev/null +++ b/sys/dev/usb/bluetooth/ng_ubt.c @@ -0,0 +1,1718 @@ +/* + * ng_ubt.c + */ + +/*- + * Copyright (c) 2001-2009 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$ + */ + +/* + * NOTE: ng_ubt2 driver has a split personality. On one side it is + * a USB device driver and on the other it is a Netgraph node. This + * driver will *NOT* create traditional /dev/ enties, only Netgraph + * node. + * + * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) + * + * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used + * by USB for any USB request going over device's interface #0 and #1, + * i.e. interrupt, control, bulk and isoc. transfers. + * + * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph + * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook + * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, + * think of it as a spin lock. + * + * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. + * + * 1) USB context. This is where all the USB related stuff happens. All + * callbacks run in this context. All callbacks are called (by USB) with + * appropriate interface lock held. It is (generally) allowed to grab + * any additional locks. + * + * 2) Netgraph context. This is where all the Netgraph related stuff happens. + * Since we mark node as WRITER, the Netgraph node will be "locked" (from + * Netgraph point of view). Any variable that is only modified from the + * Netgraph context does not require any additonal locking. It is generally + * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* + * grab any lock in the Netgraph context that could cause de-scheduling of + * the Netgraph thread for significant amount of time. In fact, the only + * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. + * Also make sure that any code that is called from the Netgraph context + * follows the rule above. + * + * 3) Taskqueue context. This is where ubt_task runs. Since we are generally + * NOT allowed to grab any lock that could cause de-scheduling in the + * Netgraph context, and, USB requires us to grab interface lock before + * doing things with transfers, it is safer to transition from the Netgraph + * context to the Taskqueue context before we can call into USB subsystem. + * + * So, to put everything together, the rules are as follows. + * It is OK to call from the USB context or the Taskqueue context into + * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words + * it is allowed to call into the Netgraph context with locks held. + * Is it *NOT* OK to call from the Netgraph context into the USB context, + * because USB requires us to grab interface locks, and, it is safer to + * avoid it. So, to make things safer we set task flags to indicate which + * actions we want to perform and schedule ubt_task which would run in the + * Taskqueue context. + * Is is OK to call from the Taskqueue context into the USB context, + * and, ubt_task does just that (i.e. grabs appropriate interface locks + * before calling into USB). + * Access to the outgoing queues, task flags and hook pointer is + * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, + * sc_ng_mtx should really be a spin lock (and it is very likely to an + * equivalent of spin lock due to adaptive nature of freebsd mutexes). + * All USB callbacks accept softc pointer as a private data. USB ensures + * that this pointer is valid. + */ + +#include "usbdevs.h" +#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 + +static int ubt_modevent(module_t, int, void *); +static device_probe_t ubt_probe; +static device_attach_t ubt_attach; +static device_detach_t ubt_detach; + +static void ubt_task_schedule(ubt_softc_p, int); +static task_fn_t ubt_task; + +#define ubt_xfer_start(sc, i) usb2_transfer_start((sc)->sc_xfer[(i)]) + +/* 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 specific + **************************************************************************** + ****************************************************************************/ + +/* USB methods */ +static usb2_callback_t ubt_ctrl_write_callback; +static usb2_callback_t ubt_intr_read_callback; +static usb2_callback_t ubt_bulk_read_callback; +static usb2_callback_t ubt_bulk_write_callback; +static usb2_callback_t ubt_isoc_read_callback; +static usb2_callback_t ubt_isoc_write_callback; + +static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); +static int ubt_isoc_read_one_frame(struct usb2_xfer *, int); + +/* + * USB config + * + * The following desribes usb transfers that could be submitted on USB device. + * + * Interface 0 on the USB device must present the following endpoints + * 1) Interrupt endpoint to receive HCI events + * 2) Bulk IN endpoint to receive ACL data + * 3) Bulk OUT endpoint to send ACL data + * + * Interface 1 on the USB device must present the following endpoints + * 1) Isochronous IN endpoint to receive SCO data + * 2) Isochronous OUT endpoint to send SCO data + */ + +static const struct usb2_config ubt_config[UBT_N_TRANSFER] = +{ + /* + * Interface #0 + */ + + /* Outgoing bulk transfer - ACL packets */ + [UBT_IF_0_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 0, + .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, + .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, }, + .mh.callback = &ubt_bulk_write_callback, + }, + /* Incoming bulk transfer - ACL packets */ + [UBT_IF_0_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 0, + .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, + .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, + .mh.callback = &ubt_bulk_read_callback, + }, + /* Incoming interrupt transfer - HCI events */ + [UBT_IF_0_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 0, + .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, + .mh.bufsize = UBT_INTR_BUFFER_SIZE, + .mh.callback = &ubt_intr_read_callback, + }, + /* Outgoing control transfer - HCI commands */ + [UBT_IF_0_CTRL_DT_WR] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .if_index = 0, + .mh.bufsize = UBT_CTRL_BUFFER_SIZE, + .mh.callback = &ubt_ctrl_write_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + + /* + * Interface #1 + */ + + /* Incoming isochronous transfer #1 - SCO packets */ + [UBT_IF_1_ISOC_DT_RD1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 1, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_read_callback, + }, + /* Incoming isochronous transfer #2 - SCO packets */ + [UBT_IF_1_ISOC_DT_RD2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 1, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_read_callback, + }, + /* Outgoing isochronous transfer #1 - SCO packets */ + [UBT_IF_1_ISOC_DT_WR1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 1, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_write_callback, + }, + /* Outgoing isochronous transfer #2 - SCO packets */ + [UBT_IF_1_ISOC_DT_WR2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 1, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_write_callback, + }, +}; + +/* + * If for some reason device should not be attached then put + * VendorID/ProductID pair into the list below. The format is + * as follows: + * + * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, + * + * 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. + * USB context. + */ + +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 (uaa->use_generic == 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)); +} /* ubt_probe */ + +/* + * Attach the device. + * USB context. + */ + +static int +ubt_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubt_softc *sc = device_get_softc(dev); + struct usb2_endpoint_descriptor *ed; + uint16_t wMaxPacketSize; + uint8_t alt_index, i, j; + uint8_t iface_index[2] = { 0, 1 }; + + device_set_usb2_desc(dev); + + sc->sc_dev = dev; + sc->sc_debug = NG_UBT_WARN_LEVEL; + + /* + * Create Netgraph node + */ + + if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { + UBT_ALERT(sc, "could not create Netgraph node\n"); + return (ENXIO); + } + + /* Name Netgraph node */ + if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { + UBT_ALERT(sc, "could not name Netgraph node\n"); + NG_NODE_UNREF(sc->sc_node); + return (ENXIO); + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); + + /* + * Initialize device softc structure + */ + + /* initialize locks */ + mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); + mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); + + /* initialize packet queues */ + NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); + NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); + NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); + + /* initialize glue task */ + TASK_INIT(&sc->sc_task, 0, ubt_task, sc); + + /* + * 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. + */ + + /* + * For interface #1 search alternate settings, and find + * the descriptor with the largest wMaxPacketSize + */ + + wMaxPacketSize = 0; + alt_index = 0; + i = 0; + j = 0; + + /* Search through all the descriptors looking for bidir mode */ + while (1) { + uint16_t temp; + + ed = usb2_find_edesc(usb2_get_config_descriptor(uaa->device), + 1, i, j); + if (ed == NULL) { + if (j != 0) { + /* next interface */ + j = 0; + i ++; + continue; + } + + break; /* end of interfaces */ + } + + temp = UGETW(ed->wMaxPacketSize); + if (temp > wMaxPacketSize) { + wMaxPacketSize = temp; + alt_index = i; + } + + j ++; + } + + /* Set alt configuration on interface #1 only if we found it */ + if (wMaxPacketSize > 0 && + usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { + UBT_ALERT(sc, "could not set alternate setting %d " \ + "for interface 1!\n", alt_index); + goto detach; + } + + /* Setup transfers for both interfaces */ + if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer, + ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { + UBT_ALERT(sc, "could not allocate transfers\n"); + goto detach; + } + + /* Claim all interfaces on the device */ + for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++) + usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + + return (0); /* success */ + +detach: + ubt_detach(dev); + + return (ENXIO); +} /* ubt_attach */ + +/* + * Detach the device. + * USB context. + */ + +int +ubt_detach(device_t dev) +{ + struct ubt_softc *sc = device_get_softc(dev); + node_p node = sc->sc_node; + + /* Destroy Netgraph node */ + if (node != NULL) { + sc->sc_node = NULL; + NG_NODE_REALLY_DIE(node); + ng_rmnode_self(node); + } + + /* Make sure ubt_task in gone */ + taskqueue_drain(taskqueue_swi, &sc->sc_task); + + /* Free USB transfers, if any */ + usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); + + /* Destroy queues */ + UBT_NG_LOCK(sc); + NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); + NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); + NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); + UBT_NG_UNLOCK(sc); + + mtx_destroy(&sc->sc_if_mtx); + mtx_destroy(&sc->sc_ng_mtx); + + return (0); +} /* ubt_detach */ + +/* + * Called when outgoing control request (HCI command) has completed, i.e. + * HCI command was sent to the device. + * USB context. + */ + +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: + UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen); + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); + /* FALLTHROUGH */ + + case USB_ST_SETUP: +send_next: + /* Get next command mbuf, if any */ + UBT_NG_LOCK(sc); + NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); + UBT_NG_UNLOCK(sc); + + if (m == NULL) { + UBT_INFO(sc, "HCI command queue is empty\n"); + break; /* transfer complete */ + } + + /* 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); + + 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 = 2; + + NG_FREE_M(m); + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "control transfer failed: %s\n", + usb2_errstr(xfer->error)); + + UBT_STAT_OERROR(sc); + goto send_next; + } + + /* transfer cancelled */ + break; + } +} /* ubt_ctrl_write_callback */ + +/* + * Called when incoming interrupt transfer (HCI event) has completed, i.e. + * HCI event was received from the device. + * USB context. + */ + +static void +ubt_intr_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + ng_hci_event_pkt_t *hdr; + + m = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* Allocate a new mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + UBT_STAT_IERROR(sc); + goto submit_next; + } + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + goto submit_next; + } + + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; + m->m_pkthdr.len = m->m_len = 1; + + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; + + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, + xfer->actlen); + m->m_pkthdr.len += xfer->actlen; + m->m_len += xfer->actlen; + + UBT_INFO(sc, "got %d bytes from interrupt pipe\n", + xfer->actlen); + + /* Validate packet and send it up the stack */ + if (m->m_pkthdr.len < sizeof(*hdr)) { + UBT_INFO(sc, "HCI event packet is too short\n"); + + UBT_STAT_IERROR(sc); + goto submit_next; + } + + hdr = mtod(m, ng_hci_event_pkt_t *); + if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { + UBT_ERR(sc, "Invalid HCI event packet size, " \ + "length=%d, pktlen=%d\n", + hdr->length, m->m_pkthdr.len); + + UBT_STAT_IERROR(sc); + goto submit_next; + } + + UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, hdr->length); + + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); + + ubt_fwd_mbuf_up(sc, &m); + /* m == NULL at this point */ + /* FALLTHROUGH */ + + case USB_ST_SETUP: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ + + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "interrupt transfer failed: %s\n", + usb2_errstr(xfer->error)); + + /* Try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto submit_next; + } + /* transfer cancelled */ + break; + } +} /* ubt_intr_read_callback */ + +/* + * Called when incoming bulk transfer (ACL packet) has completed, i.e. + * ACL packet was received from the device. + * USB context. + */ + +static void +ubt_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + ng_hci_acldata_pkt_t *hdr; + uint16_t len; + + m = NULL; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* Allocate new mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + UBT_STAT_IERROR(sc); + goto submit_next; + } + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + goto submit_next; + } + + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; + m->m_pkthdr.len = m->m_len = 1; + + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; + + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, + xfer->actlen); + m->m_pkthdr.len += xfer->actlen; + m->m_len += xfer->actlen; + + UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", + xfer->actlen); + + /* Validate packet and send it up the stack */ + if (m->m_pkthdr.len < sizeof(*hdr)) { + UBT_INFO(sc, "HCI ACL packet is too short\n"); + + UBT_STAT_IERROR(sc); + goto submit_next; + } + + hdr = mtod(m, ng_hci_acldata_pkt_t *); + len = le16toh(hdr->length); + if (len != (m->m_pkthdr.len - sizeof(*hdr))) { + UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ + "pktlen=%d\n", len, m->m_pkthdr.len); + + UBT_STAT_IERROR(sc); + goto submit_next; + } + + UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, len); + + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); + + ubt_fwd_mbuf_up(sc, &m); + /* m == NULL at this point */ + /* FALLTHOUGH */ + + case USB_ST_SETUP: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ + + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "bulk-in transfer failed: %s\n", + usb2_errstr(xfer->error)); + + /* Try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto submit_next; + } + /* transfer cancelled */ + break; + } +} /* ubt_bulk_read_callback */ + +/* + * Called when outgoing bulk transfer (ACL packet) has completed, i.e. + * ACL packet was sent to the device. + * USB context. + */ + +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: + UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen); + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); + /* FALLTHROUGH */ + + case USB_ST_SETUP: +send_next: + /* Get next mbuf, if any */ + UBT_NG_LOCK(sc); + NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); + UBT_NG_UNLOCK(sc); + + if (m == NULL) { + UBT_INFO(sc, "ACL data queue is empty\n"); + break; /* transfer completed */ + } + + /* + * Copy ACL data frame back to a linear USB transfer buffer + * and schedule transfer + */ + + usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); + xfer->frlengths[0] = m->m_pkthdr.len; + + UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", + m->m_pkthdr.len); + + NG_FREE_M(m); + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "bulk-out transfer failed: %s\n", + usb2_errstr(xfer->error)); + + UBT_STAT_OERROR(sc); + + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto send_next; + } + /* transfer cancelled */ + break; + } +} /* ubt_bulk_write_callback */ + +/* + * Called when incoming isoc transfer (SCO packet) has completed, i.e. + * SCO packet was received from the device. + * USB context. + */ + +static void +ubt_isoc_read_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + int n; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + for (n = 0; n < xfer->nframes; n ++) + if (ubt_isoc_read_one_frame(xfer, n) < 0) + break; + /* FALLTHROUGH */ + + case USB_ST_SETUP: +read_next: + for (n = 0; n < xfer->nframes; n ++) + xfer->frlengths[n] = xfer->max_frame_size; + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_IERROR(sc); + goto read_next; + } + + /* transfer cancelled */ + break; + } +} /* ubt_isoc_read_callback */ + +/* + * Helper function. Called from ubt_isoc_read_callback() to read + * SCO data from one frame. + * USB context. + */ + +static int +ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + int len, want, got; + + /* Get existing SCO reassembly buffer */ + m = sc->sc_isoc_in_buffer; + sc->sc_isoc_in_buffer = NULL; + + /* While we have data in the frame */ + while ((len = xfer->frlengths[frame_no]) > 0) { + if (m == NULL) { + /* Start new reassembly buffer */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + UBT_STAT_IERROR(sc); + return (-1); /* XXX out of sync! */ + } + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + NG_FREE_M(m); + return (-1); /* XXX out of sync! */ + } + + /* Expect SCO header */ + *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; + m->m_pkthdr.len = m->m_len = got = 1; + want = sizeof(ng_hci_scodata_pkt_t); + } else { + /* + * Check if we have SCO header and if so + * adjust amount of data we want + */ + got = m->m_pkthdr.len; + want = sizeof(ng_hci_scodata_pkt_t); + + if (got >= want) + want += mtod(m, ng_hci_scodata_pkt_t *)->length; + } + + /* Append frame data to the SCO reassembly buffer */ + if (got + len > want) + len = want - got; + + usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, + mtod(m, uint8_t *) + m->m_pkthdr.len, len); + + m->m_pkthdr.len += len; + m->m_len += len; + xfer->frlengths[frame_no] -= len; + + /* Check if we got everything we wanted, if not - continue */ + if (got != want) + continue; + + /* If we got here then we got complete SCO frame */ + UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, + mtod(m, ng_hci_scodata_pkt_t *)->length); + + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); + + ubt_fwd_mbuf_up(sc, &m); + /* m == NULL at this point */ + } + + /* Put SCO reassembly buffer back */ + sc->sc_isoc_in_buffer = m; + + return (0); +} /* ubt_isoc_read_one_frame */ + +/* + * Called when outgoing isoc transfer (SCO packet) has completed, i.e. + * SCO packet was sent to the device. + * USB context. + */ + +static void +ubt_isoc_write_callback(struct usb2_xfer *xfer) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + int n, space, offset; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen); + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); + /* FALLTHROUGH */ + + case USB_ST_SETUP: +send_next: + offset = 0; + space = xfer->max_frame_size * xfer->nframes; + m = NULL; + + while (space > 0) { + if (m == NULL) { + UBT_NG_LOCK(sc); + NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + UBT_NG_UNLOCK(sc); + + if (m == NULL) + break; + } + + n = min(space, m->m_pkthdr.len); + if (n > 0) { + usb2_m_copy_in(xfer->frbuffers, offset, m,0, n); + m_adj(m, n); + + offset += n; + space -= n; + } + + if (m->m_pkthdr.len == 0) + NG_FREE_M(m); /* sets m = NULL */ + } + + /* Put whatever is left from mbuf back on queue */ + if (m != NULL) { + UBT_NG_LOCK(sc); + NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); + UBT_NG_UNLOCK(sc); + } + + /* + * Calculate sizes for isoc frames. + * Note that offset could be 0 at this point (i.e. we have + * nothing to send). That is fine, as we have isoc. transfers + * going in both directions all the time. In this case it + * would be just empty isoc. transfer. + */ + + for (n = 0; n < xfer->nframes; n ++) { + xfer->frlengths[n] = min(offset, xfer->max_frame_size); + offset -= xfer->frlengths[n]; + } + + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_OERROR(sc); + goto send_next; + } + + /* transfer cancelled */ + break; + } +} + +/* + * Utility function to forward provided mbuf upstream (i.e. up the stack). + * Modifies value of the mbuf pointer (sets it to NULL). + * Save to call from any context. + */ + +static int +ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) +{ + hook_p hook; + int error; + + /* + * Close the race with Netgraph hook newhook/disconnect methods. + * Save the hook pointer atomically. Two cases are possible: + * + * 1) The hook pointer is NULL. It means disconnect method got + * there first. In this case we are done. + * + * 2) The hook pointer is not NULL. It means that hook pointer + * could be either in valid or invalid (i.e. in the process + * of disconnect) state. In any case grab an extra reference + * to protect the hook pointer. + * + * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as + * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). + */ + + UBT_NG_LOCK(sc); + if ((hook = sc->sc_hook) != NULL) + NG_HOOK_REF(hook); + UBT_NG_UNLOCK(sc); + + if (hook == NULL) { + NG_FREE_M(*m); + return (ENETDOWN); + } + + NG_SEND_DATA_ONLY(error, hook, *m); + NG_HOOK_UNREF(hook); + + if (error != 0) + UBT_STAT_IERROR(sc); + + return (error); +} /* ubt_fwd_mbuf_up */ + +/**************************************************************************** + **************************************************************************** + ** Glue + **************************************************************************** + ****************************************************************************/ + +/* + * Schedule glue task. Should be called with sc_ng_mtx held. + * Netgraph context. + */ + +static void +ubt_task_schedule(ubt_softc_p sc, int action) +{ + mtx_assert(&sc->sc_ng_mtx, MA_OWNED); + + /* + * Try to handle corner case when "start all" and "stop all" + * actions can both be set before task is executed. + * + * The rules are + * + * sc_task_flags action new sc_task_flags + * ------------------------------------------------------ + * 0 start start + * 0 stop stop + * start start start + * start stop stop + * stop start stop|start + * stop stop stop + * stop|start start stop|start + * stop|start stop stop + */ + + if (action != 0) { + if ((action & UBT_FLAG_T_STOP_ALL) != 0) + sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; + + sc->sc_task_flags |= action; + } + + if (sc->sc_task_flags & UBT_FLAG_T_PENDING) + return; + + if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { + sc->sc_task_flags |= UBT_FLAG_T_PENDING; + return; + } + + /* XXX: i think this should never happen */ +} /* ubt_task_schedule */ + +/* + * Glue task. Examines sc_task_flags and does things depending on it. + * Taskqueue context. + */ + +static void +ubt_task(void *context, int pending) +{ + ubt_softc_p sc = context; + int task_flags, i; + + UBT_NG_LOCK(sc); + task_flags = sc->sc_task_flags; + sc->sc_task_flags = 0; + UBT_NG_UNLOCK(sc); + + /* + * Stop all USB transfers synchronously. + * Stop interface #0 and #1 transfers at the same time and in the + * same loop. usb2_transfer_drain() will do appropriate locking. + */ + + if (task_flags & UBT_FLAG_T_STOP_ALL) + for (i = 0; i < UBT_N_TRANSFER; i ++) + usb2_transfer_drain(sc->sc_xfer[i]); + + /* Start incoming interrupt and bulk, and all isoc. USB transfers */ + if (task_flags & UBT_FLAG_T_START_ALL) { + /* + * Interface #0 + */ + + mtx_lock(&sc->sc_if_mtx); + + ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); + ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); + + /* + * Interface #1 + * Start both read and write isoc. transfers by default. + * Get them going all the time even if we have nothing + * to send to avoid any delays. + */ + + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); + + mtx_unlock(&sc->sc_if_mtx); + } + + /* Start outgoing control transfer */ + if (task_flags & UBT_FLAG_T_START_CTRL) { + mtx_lock(&sc->sc_if_mtx); + ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); + mtx_unlock(&sc->sc_if_mtx); + } + + /* Start outgoing bulk transfer */ + if (task_flags & UBT_FLAG_T_START_BULK) { + mtx_lock(&sc->sc_if_mtx); + ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); + mtx_unlock(&sc->sc_if_mtx); + } +} /* ubt_task */ + +/**************************************************************************** + **************************************************************************** + ** Netgraph specific + **************************************************************************** + ****************************************************************************/ + +/* + * Netgraph node constructor. Do not allow to create node of this type. + * Netgraph context. + */ + +static int +ng_ubt_constructor(node_p node) +{ + return (EINVAL); +} /* ng_ubt_constructor */ + +/* + * Netgraph node destructor. Destroy node only when device has been detached. + * Netgraph context. + */ + +static int +ng_ubt_shutdown(node_p node) +{ + if (node->nd_flags & NGF_REALLY_DIE) { + /* + * We came here because the USB device is being + * detached, so stop being persistant. + */ + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + } else + NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ + + return (0); +} /* ng_ubt_shutdown */ + +/* + * Create new hook. There can only be one. + * Netgraph context. + */ + +static int +ng_ubt_newhook(node_p node, hook_p hook, char const *name) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + + if (strcmp(name, NG_UBT_HOOK) != 0) + return (EINVAL); + + UBT_NG_LOCK(sc); + if (sc->sc_hook != NULL) { + UBT_NG_UNLOCK(sc); + + return (EISCONN); + } + + sc->sc_hook = hook; + UBT_NG_UNLOCK(sc); + + return (0); +} /* ng_ubt_newhook */ + +/* + * Connect hook. Start incoming USB transfers. + * Netgraph context. + */ + +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)); + + UBT_NG_LOCK(sc); + ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); + UBT_NG_UNLOCK(sc); + + return (0); +} /* ng_ubt_connect */ + +/* + * Disconnect hook. + * Netgraph context. + */ + +static int +ng_ubt_disconnect(hook_p hook) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + UBT_NG_LOCK(sc); + + if (hook != sc->sc_hook) { + UBT_NG_UNLOCK(sc); + + return (EINVAL); + } + + sc->sc_hook = NULL; + + /* Kick off task to stop all USB xfers */ + ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); + + /* Drain queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); + + UBT_NG_UNLOCK(sc); + + return (0); +} /* ng_ubt_disconnect */ + +/* + * Process control message. + * Netgraph context. + */ + +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, *rsp = NULL; + struct ng_bt_mbufq *q; + int error = 0, queue, qlen; + + 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; + break; + } + + snprintf(rsp->data, NG_TEXTRESPONSE, + "Hook: %s\n" \ + "Task 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_task_flags, + sc->sc_debug, + sc->sc_cmdq.len, + sc->sc_cmdq.maxlen, + sc->sc_aclq.len, + sc->sc_aclq.maxlen, + sc->sc_scoq.len, + 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; + break; + } + + 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; + break; + } + + *((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; + break; + } + + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; + + 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: + error = EINVAL; + goto done; + /* NOT REACHED */ + } + + 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: + error = EINVAL; + goto done; + /* NOT REACHED */ + } + + 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; + break; + } + + bcopy(&sc->sc_stat, rsp->data, + sizeof(ng_ubt_node_stat_ep)); + break; + + case NGM_UBT_NODE_RESET_STAT: + UBT_STAT_RESET(sc); + break; + + default: + error = EINVAL; + break; + } + break; + + default: + error = EINVAL; + break; + } +done: + NG_RESPOND_MSG(error, node, item, rsp); + NG_FREE_MSG(msg); + + return (error); +} /* ng_ubt_rcvmsg */ + +/* + * Process data. + * Netgraph context. + */ + +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; + int action, error = 0; + + if (hook != sc->sc_hook) { + error = EINVAL; + goto done; + } + + /* Deatch mbuf and get HCI frame type */ + NGI_GET_M(item, m); + + /* + * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, + * 2 bytes connection handle and at least 1 byte of length. + * Panic on data frame that has size smaller than 4 bytes (it + * should not happen) + */ + + if (m->m_pkthdr.len < 4) + panic("HCI frame size is too small! pktlen=%d\n", + m->m_pkthdr.len); + + /* Process HCI frame */ + switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ + case NG_HCI_CMD_PKT: + if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) + panic("HCI command frame size is too big! " \ + "buffer size=%zd, packet len=%d\n", + UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); + + q = &sc->sc_cmdq; + action = UBT_FLAG_T_START_CTRL; + break; + + case NG_HCI_ACL_DATA_PKT: + if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) + panic("ACL data frame size is too big! " \ + "buffer size=%d, packet len=%d\n", + UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); + + q = &sc->sc_aclq; + action = UBT_FLAG_T_START_BULK; + break; + + case NG_HCI_SCO_DATA_PKT: + q = &sc->sc_scoq; + action = 0; + break; + + default: + 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; + /* NOT REACHED */ + } + + UBT_NG_LOCK(sc); + if (NG_BT_MBUFQ_FULL(q)) { + NG_BT_MBUFQ_DROP(q); + UBT_NG_UNLOCK(sc); + + 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 { + /* Loose HCI packet type, enqueue mbuf and kick off task */ + m_adj(m, sizeof(uint8_t)); + NG_BT_MBUFQ_ENQUEUE(q, m); + ubt_task_schedule(sc, action); + UBT_NG_UNLOCK(sc); + } +done: + NG_FREE_ITEM(item); + + return (error); +} /* ng_ubt_rcvdata */ + +/**************************************************************************** + **************************************************************************** + ** Module + **************************************************************************** + ****************************************************************************/ + +/* + * 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 */ + +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), +}; + +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, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt, usb, 1, 1, 1); + diff --git a/sys/dev/usb/bluetooth/ng_ubt_var.h b/sys/dev/usb/bluetooth/ng_ubt_var.h new file mode 100644 index 0000000..721e2f1 --- /dev/null +++ b/sys/dev/usb/bluetooth/ng_ubt_var.h @@ -0,0 +1,131 @@ +/* + * ng_ubt_var.h + */ + +/*- + * Copyright (c) 2001-2009 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_ 1 + +/* Debug printf's */ +#define UBT_DEBUG(level, sc, fmt, ...) \ +do { \ + if ((sc)->sc_debug >= (level)) \ + device_printf((sc)->sc_dev, "%s:%d: " fmt, \ + __FUNCTION__, __LINE__,## __VA_ARGS__); \ +} while (0) + +#define UBT_ALERT(...) UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__) +#define UBT_ERR(...) UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__) +#define UBT_WARN(...) UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__) +#define UBT_INFO(...) UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__) + +#define UBT_NG_LOCK(sc) mtx_lock(&(sc)->sc_ng_mtx) +#define UBT_NG_UNLOCK(sc) mtx_unlock(&(sc)->sc_ng_mtx) + +/* Bluetooth USB control request type */ +#define UBT_HCI_REQUEST 0x20 +#define UBT_DEFAULT_QLEN 64 +#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */ + +/* Bluetooth USB defines */ +enum { + /* Interface #0 transfers */ + UBT_IF_0_BULK_DT_WR = 0, + UBT_IF_0_BULK_DT_RD, + UBT_IF_0_INTR_DT_RD, + UBT_IF_0_CTRL_DT_WR, + + /* Interface #1 transfers */ + UBT_IF_1_ISOC_DT_RD1, + UBT_IF_1_ISOC_DT_RD2, + UBT_IF_1_ISOC_DT_WR1, + UBT_IF_1_ISOC_DT_WR2, + + UBT_N_TRANSFER, /* total number of transfers */ +}; + +/* USB device softc structure */ +struct ubt_softc { + device_t sc_dev; /* for debug printf */ + + /* State */ + ng_ubt_node_debug_ep sc_debug; /* debug level */ + + ng_ubt_node_stat_ep sc_stat; /* statistic */ +#define UBT_STAT_PCKTS_SENT(sc) (sc)->sc_stat.pckts_sent ++ +#define UBT_STAT_BYTES_SENT(sc, n) (sc)->sc_stat.bytes_sent += (n) +#define UBT_STAT_PCKTS_RECV(sc) (sc)->sc_stat.pckts_recv ++ +#define UBT_STAT_BYTES_RECV(sc, n) (sc)->sc_stat.bytes_recv += (n) +#define UBT_STAT_OERROR(sc) (sc)->sc_stat.oerrors ++ +#define UBT_STAT_IERROR(sc) (sc)->sc_stat.ierrors ++ +#define UBT_STAT_RESET(sc) bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat)) + + /* USB device specific */ + struct mtx sc_if_mtx; /* interfaces lock */ + struct usb2_xfer *sc_xfer[UBT_N_TRANSFER]; + + struct mtx sc_ng_mtx; /* lock for shared NG data */ + + /* HCI commands */ + struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ +#define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_device_request) + \ + sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) +#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ + + /* ACL data */ + struct ng_bt_mbufq sc_aclq; /* ACL data queue */ +#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ +#define UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES) + + /* SCO data */ + struct ng_bt_mbufq sc_scoq; /* SCO data queue */ + struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */ + + /* Netgraph specific */ + node_p sc_node; /* pointer back to node */ + hook_p sc_hook; /* upstream hook */ + + /* Glue */ + int sc_task_flags; /* task flags */ +#define UBT_FLAG_T_PENDING (1 << 0) /* task pending */ +#define UBT_FLAG_T_STOP_ALL (1 << 1) /* stop all xfers */ +#define UBT_FLAG_T_START_ALL (1 << 2) /* start all read and isoc + write xfers */ +#define UBT_FLAG_T_START_CTRL (1 << 3) /* start control xfer (write) */ +#define UBT_FLAG_T_START_BULK (1 << 4) /* start bulk xfer (write) */ + + struct task sc_task; +}; +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/usb/bluetooth/ubtbcmfw.c b/sys/dev/usb/bluetooth/ubtbcmfw.c new file mode 100644 index 0000000..3685f56 --- /dev/null +++ b/sys/dev/usb/bluetooth/ubtbcmfw.c @@ -0,0 +1,430 @@ +/* + * ubtbcmfw.c + */ + +/*- + * Copyright (c) 2003-2009 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 "usbdevs.h" +#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_BSIZE 1024 +#define UBTBCMFW_IFQ_MAXLEN 2 + +enum { + UBTBCMFW_BULK_DT_WR = 0, + UBTBCMFW_INTR_DT_RD, + UBTBCMFW_N_TRANSFER, +}; + +struct ubtbcmfw_softc { + struct usb2_device *sc_udev; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER]; + struct usb2_fifo_sc sc_fifo; +}; + +/* + * 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_read_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", +}; + +/* + * Device's config structure + */ + +static const struct usb2_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] = +{ + [UBTBCMFW_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = 0x02, /* fixed */ + .direction = UE_DIR_OUT, + .if_index = UBTBCMFW_IFACE_IDX, + .mh.bufsize = UBTBCMFW_BSIZE, + .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, + .proxy_buffer = 1, }, + .mh.callback = &ubtbcmfw_write_callback, + }, + + [UBTBCMFW_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = 0x01, /* fixed */ + .direction = UE_DIR_IN, + .if_index = UBTBCMFW_IFACE_IDX, + .mh.bufsize = UBTBCMFW_BSIZE, + .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, + .proxy_buffer = 1, }, + .mh.callback = &ubtbcmfw_read_callback, + }, +}; + +/* + * 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, usb, 1, 1, 1); + +/* + * Probe for a USB Bluetooth device + */ + +static int +ubtbcmfw_probe(device_t dev) +{ + const struct usb2_device_id devs[] = { + /* Broadcom BCM2033 devices only */ + { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) }, + }; + + 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); + + return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa)); +} /* ubtbcmfw_probe */ + +/* + * 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); + uint8_t iface_index; + int error; + + 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; + error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + ubtbcmfw_config, UBTBCMFW_N_TRANSFER, + sc, &sc->sc_mtx); + if (error != 0) { + device_printf(dev, "allocating USB transfers failed. %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, + &ubtbcmfw_fifo_methods, &sc->sc_fifo, + device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); + if (error != 0) { + device_printf(dev, "could not attach fifo. %s\n", + usb2_errstr(error)); + goto detach; + } + + return (0); /* success */ + +detach: + ubtbcmfw_detach(dev); + + return (ENXIO); /* failure */ +} /* ubtbcmfw_attach */ + +/* + * 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_N_TRANSFER); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} /* ubtbcmfw_detach */ + +/* + * USB write callback + */ + +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_TX]; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: +setup_next: + 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 */ + xfer->flags.stall_pipe = 1; + goto setup_next; + } + break; + } +} /* ubtbcmfw_write_callback */ + +/* + * USB read callback + */ + +static void +ubtbcmfw_read_callback(struct usb2_xfer *xfer) +{ + struct ubtbcmfw_softc *sc = xfer->priv_sc; + struct usb2_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1); + /* FALLTHROUGH */ + + case USB_ST_SETUP: +setup_next: + if (usb2_fifo_put_bytes_max(fifo) > 0) { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto setup_next; + } + break; + } +} /* ubtbcmfw_read_callback */ + +/* + * Called when we about to start read()ing from the device + */ + +static void +ubtbcmfw_start_read(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); +} /* ubtbcmfw_start_read */ + +/* + * Called when we about to stop reading (i.e. closing fifo) + */ + +static void +ubtbcmfw_stop_read(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); +} /* ubtbcmfw_stop_read */ + +/* + * Called when we about to start write()ing to the device, poll()ing + * for write or flushing fifo + */ + +static void +ubtbcmfw_start_write(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); +} /* ubtbcmfw_start_write */ + +/* + * Called when we about to stop writing (i.e. closing fifo) + */ + +static void +ubtbcmfw_stop_write(struct usb2_fifo *fifo) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); +} /* ubtbcmfw_stop_write */ + +/* + * Called when fifo is open + */ + +static int +ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + struct ubtbcmfw_softc *sc = fifo->priv_sc0; + struct usb2_xfer *xfer; + + /* + * f_open fifo method can only be called with either FREAD + * or FWRITE flag set at one time. + */ + + if (fflags & FREAD) + xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD]; + else if (fflags & FWRITE) + xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR]; + else + return (EINVAL); /* should not happen */ + + if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length, + UBTBCMFW_IFQ_MAXLEN) != 0) + return (ENOMEM); + + return (0); +} /* ubtbcmfw_open */ + +/* + * Called when fifo is closed + */ + +static void +ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) +{ + if (fflags & (FREAD | FWRITE)) + usb2_fifo_free_buffer(fifo); +} /* ubtbcmfw_close */ + +/* + * Process ioctl() on USB device + */ + +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: + memcpy(data, usb2_get_device_descriptor(sc->sc_udev), + sizeof(struct usb2_device_descriptor)); + break; + + default: + error = EINVAL; + break; + } + + return (error); +} /* ubtbcmfw_ioctl */ diff --git a/sys/dev/usb/controller/at91dci.c b/sys/dev/usb/controller/at91dci.c new file mode 100644 index 0000000..a7283b4 --- /dev/null +++ b/sys/dev/usb/controller/at91dci.c @@ -0,0 +1,2467 @@ +#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 + +#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 *, usb2_error_t); +static void at91dci_do_poll(struct usb2_bus *); +static void at91dci_root_ctrl_poll(struct at91dci_softc *); +static void at91dci_standard_done(struct usb2_xfer *); + +static usb2_sw_transfer_func_t at91dci_root_intr_done; +static usb2_sw_transfer_func_t at91dci_root_ctrl_done; + +/* + * 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; + } +} + +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); + } +} + +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; + } +} + +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); + } +} + +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); + } +} + +static void +at91dci_wakeup_peer(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); + uint8_t use_polling; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0; + + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR); + + /* wait 8 milliseconds */ + if (use_polling) { + /* polling */ + DELAY(8000); + } else { + /* Wait for reset to complete. */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); + } + + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0); +} + +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); +} + +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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + 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; + } + } +} + +void +at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on) +{ + DPRINTFN(5, "vbus = %u\n", is_on); + + USB_BUS_LOCK(&sc->sc_bus); + 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); + } + } + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +at91dci_interrupt(struct at91dci_softc *sc) +{ + uint32_t status; + + USB_BUS_LOCK(&sc->sc_bus); + + status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); + status &= AT91_UDP_INT_DEFAULT; + + if (!status) { + USB_BUS_UNLOCK(&sc->sc_bus); + 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); + } + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; +} + +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->xroot->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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + 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; + } +} + +static void +at91dci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + at91dci_device_done(xfer, USB_ERR_TIMEOUT); +} + +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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + 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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &at91dci_timeout, xfer->timeout); + } + } +} + +static void +at91dci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); + + DPRINTFN(9, "\n"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +/*------------------------------------------------------------------------* + * 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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + uint8_t ep_no; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +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; + + USB_BUS_LOCK_ASSERT(udev->bus, 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); +} + +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); +} + +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); + + USB_BUS_LOCK_ASSERT(udev->bus, 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))); +} + +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; + + USB_BUS_LOCK(&sc->sc_bus); + + /* 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.bus_mtx, hz / 1000); + + /* 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); + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch any lost interrupts */ + + at91dci_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +at91dci_uninit(struct at91dci_softc *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + /* 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); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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); + + USB_BUS_LOCK(&sc->sc_bus); + at91dci_interrupt_poll(sc); + at91dci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void +at91dci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); + 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); +} + +static void +at91dci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + at91dci_start_standard_chain(xfer); +} + +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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + at91dci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* + * 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) >> 8, + .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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +at91dci_root_ctrl_task(struct usb2_bus *bus) +{ + at91dci_root_ctrl_poll(AT9100_DCI_BUS2SC(bus)); +} + +static void +at91dci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); + uint16_t value; + uint16_t index; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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(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; + 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); +} + +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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + at91dci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +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; + + /* + * 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; +} + +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; + } + } +} + +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, + .roothub_exec = &at91dci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/at91dci.h b/sys/dev/usb/controller/at91dci.h new file mode 100644 index 0000000..d386307 --- /dev/null +++ b/sys/dev/usb/controller/at91dci.h @@ -0,0 +1,245 @@ +/* $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_MAX_DEVICES (USB_MIN_DEVICES + 1) + +#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_device *sc_devices[AT91_MAX_DEVICES]; + 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); +void at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on); + +#endif /* _AT9100_DCI_H_ */ diff --git a/sys/dev/usb/controller/at91dci_atmelarm.c b/sys/dev/usb/controller/at91dci_atmelarm.c new file mode 100644 index 0000000..71d2937 --- /dev/null +++ b/sys/dev/usb/controller/at91dci_atmelarm.c @@ -0,0 +1,347 @@ +#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 + +#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_poll(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); + at91dci_vbus_interrupt(&sc->sc_dci, vbus_val); +} + +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); +} + +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); +} + +static void +at91_udp_pull_up(void *arg) +{ + at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); +} + +static void +at91_udp_pull_down(void *arg) +{ + at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); +} + +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; + + /* 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; + + /* initialise some bus fields */ + sc->sc_dci.sc_bus.parent = dev; + sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; + sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES; + + /* 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, hz / 100); + + 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); + +#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_poll, 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_poll, 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_poll(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_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/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c new file mode 100644 index 0000000..8a68822 --- /dev/null +++ b/sys/dev/usb/controller/atmegadci.c @@ -0,0 +1,2327 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2009 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 ATMEGA series USB Device + * Controller + */ + +/* + * 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 atmegadci_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define ATMEGA_BUS2SC(bus) \ + ((struct atmegadci_softc *)(((uint8_t *)(bus)) - \ + USB_P2U(&(((struct atmegadci_softc *)0)->sc_bus)))) + +#define ATMEGA_PC2SC(pc) \ + ATMEGA_BUS2SC((pc)->tag_parent->info->bus) + +#if USB_DEBUG +static int atmegadci_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, atmegadci, CTLFLAG_RW, 0, "USB ATMEGA DCI"); +SYSCTL_INT(_hw_usb2_atmegadci, OID_AUTO, debug, CTLFLAG_RW, + &atmegadci_debug, 0, "ATMEGA DCI debug level"); +#endif + +#define ATMEGA_INTR_ENDPT 1 + +/* prototypes */ + +struct usb2_bus_methods atmegadci_bus_methods; +struct usb2_pipe_methods atmegadci_device_bulk_methods; +struct usb2_pipe_methods atmegadci_device_ctrl_methods; +struct usb2_pipe_methods atmegadci_device_intr_methods; +struct usb2_pipe_methods atmegadci_device_isoc_fs_methods; +struct usb2_pipe_methods atmegadci_root_ctrl_methods; +struct usb2_pipe_methods atmegadci_root_intr_methods; + +static atmegadci_cmd_t atmegadci_setup_rx; +static atmegadci_cmd_t atmegadci_data_rx; +static atmegadci_cmd_t atmegadci_data_tx; +static atmegadci_cmd_t atmegadci_data_tx_sync; +static void atmegadci_device_done(struct usb2_xfer *, usb2_error_t); +static void atmegadci_do_poll(struct usb2_bus *); +static void atmegadci_root_ctrl_poll(struct atmegadci_softc *); +static void atmegadci_standard_done(struct usb2_xfer *); + +static usb2_sw_transfer_func_t atmegadci_root_intr_done; +static usb2_sw_transfer_func_t atmegadci_root_ctrl_done; + +/* + * Here is a list of what the chip supports: + */ +static const struct usb2_hw_ep_profile + atmegadci_ep_profile[2] = { + + [0] = { + .max_in_frame_size = 64, + .max_out_frame_size = 64, + .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, + }, +}; + +static void +atmegadci_get_hw_ep_profile(struct usb2_device *udev, + const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) +{ + if (ep_addr == 0) + *ppf = atmegadci_ep_profile; + else if (ep_addr < ATMEGA_EP_MAX) + *ppf = atmegadci_ep_profile + 1; + else + *ppf = NULL; +} + +static void +atmegadci_clocks_on(struct atmegadci_softc *sc) +{ + if (sc->sc_flags.clocks_off && + sc->sc_flags.port_powered) { + + DPRINTFN(5, "\n"); + + /* turn on clocks */ + (sc->sc_clocks_on) (&sc->sc_bus); + + ATMEGA_WRITE_1(sc, ATMEGA_USBCON, + ATMEGA_USBCON_USBE | + ATMEGA_USBCON_OTGPADE | + ATMEGA_USBCON_VBUSTE); + + sc->sc_flags.clocks_off = 0; + + /* enable transceiver ? */ + } +} + +static void +atmegadci_clocks_off(struct atmegadci_softc *sc) +{ + if (!sc->sc_flags.clocks_off) { + + DPRINTFN(5, "\n"); + + /* disable Transceiver ? */ + + ATMEGA_WRITE_1(sc, ATMEGA_USBCON, + ATMEGA_USBCON_USBE | + ATMEGA_USBCON_OTGPADE | + ATMEGA_USBCON_FRZCLK | + ATMEGA_USBCON_VBUSTE); + + /* turn clocks off */ + (sc->sc_clocks_off) (&sc->sc_bus); + + sc->sc_flags.clocks_off = 1; + } +} + +static void +atmegadci_pull_up(struct atmegadci_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; + ATMEGA_WRITE_1(sc, ATMEGA_UDCON, 0); + } +} + +static void +atmegadci_pull_down(struct atmegadci_softc *sc) +{ + /* pulldown D+, if possible */ + + if (sc->sc_flags.d_pulled_up) { + sc->sc_flags.d_pulled_up = 0; + ATMEGA_WRITE_1(sc, ATMEGA_UDCON, ATMEGA_UDCON_DETACH); + } +} + +static void +atmegadci_wakeup_peer(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + uint8_t use_polling; + uint8_t temp; + + if (!sc->sc_flags.status_suspend) { + return; + } + use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0; + + temp = ATMEGA_READ_1(sc, ATMEGA_UDCON); + ATMEGA_WRITE_1(sc, ATMEGA_UDCON, temp | ATMEGA_UDCON_RMWKUP); + + /* wait 8 milliseconds */ + if (use_polling) { + /* polling */ + DELAY(8000); + } else { + /* Wait for reset to complete. */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); + } + + /* hardware should have cleared RMWKUP bit */ +} + +static void +atmegadci_set_address(struct atmegadci_softc *sc, uint8_t addr) +{ + DPRINTFN(5, "addr=%d\n", addr); + + ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr); + + addr |= ATMEGA_UDADDR_ADDEN; + + ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr); +} + +static uint8_t +atmegadci_setup_rx(struct atmegadci_td *td) +{ + struct atmegadci_softc *sc; + struct usb2_device_request req; + uint16_t count; + uint8_t temp; + + /* get pointer to softc */ + sc = ATMEGA_PC2SC(td->pc); + + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); + + /* check endpoint status */ + temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); + + DPRINTFN(5, "UEINTX=0x%02x\n", temp); + + if (!(temp & ATMEGA_UEINTX_RXSTPI)) { + /* abort any ongoing transfer */ + if (!td->did_stall) { + DPRINTFN(5, "stalling\n"); + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, + ATMEGA_UECONX_EPEN | + ATMEGA_UECONX_STALLRQ); + td->did_stall = 1; + } + goto not_complete; + } + /* get the packet byte count */ + count = + (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) | + (ATMEGA_READ_1(sc, ATMEGA_UEBCLX)); + + /* mask away undefined bits */ + count &= 0x7FF; + + /* 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 */ + ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX, + (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; + + /* 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; + } + + /* clear SETUP packet interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ~ATMEGA_UEINTX_RXSTPI); + return (0); /* complete */ + +not_complete: + /* we only want to know if there is a SETUP packet */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, ATMEGA_UEIENX_RXSTPE); + return (1); /* not complete */ +} + +static uint8_t +atmegadci_data_rx(struct atmegadci_td *td) +{ + struct atmegadci_softc *sc; + struct usb2_page_search buf_res; + uint16_t count; + uint8_t temp; + uint8_t to; + uint8_t got_short; + + to = 3; /* don't loop forever! */ + got_short = 0; + + /* get pointer to softc */ + sc = ATMEGA_PC2SC(td->pc); + + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); + +repeat: + /* check if any of the FIFO banks have data */ + /* check endpoint status */ + temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); + + DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder); + + if (temp & ATMEGA_UEINTX_RXSTPI) { + 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 status */ + if (!(temp & (ATMEGA_UEINTX_FIFOCON | + ATMEGA_UEINTX_RXOUTI))) { + /* no data */ + goto not_complete; + } + /* get the packet byte count */ + count = + (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) | + (ATMEGA_READ_1(sc, ATMEGA_UEBCLX)); + + /* mask away undefined bits */ + count &= 0x7FF; + + /* 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 */ + ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX, + buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* clear OUT packet interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_RXOUTI ^ 0xFF); + + /* release FIFO bank */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_FIFOCON ^ 0xFF); + + /* 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; + } +not_complete: + /* we only want to know if there is a SETUP packet or OUT packet */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, + ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_RXOUTE); + return (1); /* not complete */ +} + +static uint8_t +atmegadci_data_tx(struct atmegadci_td *td) +{ + struct atmegadci_softc *sc; + struct usb2_page_search buf_res; + uint16_t count; + uint8_t to; + uint8_t temp; + + to = 3; /* don't loop forever! */ + + /* get pointer to softc */ + sc = ATMEGA_PC2SC(td->pc); + + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); + +repeat: + + /* check endpoint status */ + temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); + + DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder); + + if (temp & ATMEGA_UEINTX_RXSTPI) { + /* + * The current transfer was aborted + * by the USB Host + */ + td->error = 1; + return (0); /* complete */ + } + if (!(temp & (ATMEGA_UEINTX_FIFOCON | + ATMEGA_UEINTX_TXINI))) { + /* cannot write any data */ + goto not_complete; + } + 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 */ + ATMEGA_WRITE_MULTI_1(sc, ATMEGA_UEDATX, + buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* clear IN packet interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_TXINI); + + /* allocate FIFO bank */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_FIFOCON); + + /* 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; + } +not_complete: + /* we only want to know if there is a SETUP packet or free IN packet */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, + ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE); + return (1); /* not complete */ +} + +static uint8_t +atmegadci_data_tx_sync(struct atmegadci_td *td) +{ + struct atmegadci_softc *sc; + uint8_t temp; + + /* get pointer to softc */ + sc = ATMEGA_PC2SC(td->pc); + + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); + + /* check endpoint status */ + temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); + + DPRINTFN(5, "temp=0x%02x\n", temp); + + if (temp & ATMEGA_UEINTX_RXSTPI) { + DPRINTFN(5, "faking complete\n"); + /* Race condition */ + return (0); /* complete */ + } + /* + * The control endpoint has only got one bank, so if that bank + * is free the packet has been transferred! + */ + if (!(temp & (ATMEGA_UEINTX_FIFOCON | + ATMEGA_UEINTX_TXINI))) { + /* cannot write any data */ + goto not_complete; + } + if (sc->sc_dv_addr != 0xFF) { + /* set new address */ + atmegadci_set_address(sc, sc->sc_dv_addr); + } + return (0); /* complete */ + +not_complete: + /* we only want to know if there is a SETUP packet or free IN packet */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, + ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE); + return (1); /* not complete */ +} + +static uint8_t +atmegadci_xfer_do_fifo(struct usb2_xfer *xfer) +{ + struct atmegadci_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 and transfer + * some flags to the next transfer descriptor + */ + td = td->obj_next; + xfer->td_transfer_cache = td; + } + return (1); /* not complete */ + +done: + /* compute all actual lengths */ + + atmegadci_standard_done(xfer); + return (0); /* complete */ +} + +static void +atmegadci_interrupt_poll(struct atmegadci_softc *sc) +{ + struct usb2_xfer *xfer; + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!atmegadci_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } +} + +static void +atmegadci_vbus_interrupt(struct atmegadci_softc *sc, uint8_t is_on) +{ + DPRINTFN(5, "vbus = %u\n", is_on); + + 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, + &atmegadci_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, + &atmegadci_root_intr_done); + } + } +} + +void +atmegadci_interrupt(struct atmegadci_softc *sc) +{ + uint8_t status; + + USB_BUS_LOCK(&sc->sc_bus); + + /* read interrupt status */ + status = ATMEGA_READ_1(sc, ATMEGA_UDINT); + + /* clear all set interrupts */ + ATMEGA_WRITE_1(sc, ATMEGA_UDINT, ~status); + + /* check for any bus state change interrupts */ + if (status & ATMEGA_UDINT_EORSTI) { + + DPRINTFN(5, "end of reset\n"); + + /* 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 */ + ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, + ATMEGA_UDINT_SUSPE | + ATMEGA_UDINT_EORSTE); + + /* complete root HUB interrupt endpoint */ + usb2_sw_transfer(&sc->sc_root_intr, + &atmegadci_root_intr_done); + } + /* + * 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 (status & ATMEGA_UDINT_EORSMI) { + + DPRINTFN(5, "resume interrupt\n"); + + if (sc->sc_flags.status_suspend) { + /* update status bits */ + sc->sc_flags.status_suspend = 0; + sc->sc_flags.change_suspend = 1; + + /* disable resume interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, + ATMEGA_UDINT_SUSPE | + ATMEGA_UDINT_EORSTE); + + /* complete root HUB interrupt endpoint */ + usb2_sw_transfer(&sc->sc_root_intr, + &atmegadci_root_intr_done); + } + } else if (status & ATMEGA_UDINT_SUSPI) { + + DPRINTFN(5, "suspend interrupt\n"); + + if (!sc->sc_flags.status_suspend) { + /* update status bits */ + sc->sc_flags.status_suspend = 1; + sc->sc_flags.change_suspend = 1; + + /* disable suspend interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, + ATMEGA_UDINT_EORSMI | + ATMEGA_UDINT_EORSTE); + + /* complete root HUB interrupt endpoint */ + usb2_sw_transfer(&sc->sc_root_intr, + &atmegadci_root_intr_done); + } + } + /* check VBUS */ + status = ATMEGA_READ_1(sc, ATMEGA_USBINT); + + /* clear all set interrupts */ + ATMEGA_WRITE_1(sc, ATMEGA_USBINT, ~status); + + if (status & ATMEGA_USBINT_VBUSTI) { + uint8_t temp; + + temp = ATMEGA_READ_1(sc, ATMEGA_USBSTA); + atmegadci_vbus_interrupt(sc, temp & ATMEGA_USBSTA_VBUS); + } + /* check for any endpoint interrupts */ + status = ATMEGA_READ_1(sc, ATMEGA_UEINT); + + /* clear all set interrupts */ + ATMEGA_WRITE_1(sc, ATMEGA_UEINT, ~status); + + if (status) { + + DPRINTFN(5, "real endpoint interrupt 0x%02x\n", status); + + atmegadci_interrupt_poll(sc); + } + USB_BUS_UNLOCK(&sc->sc_bus); +} + +static void +atmegadci_setup_standard_chain_sub(struct atmegadci_std_temp *temp) +{ + struct atmegadci_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; +} + +static void +atmegadci_setup_standard_chain(struct usb2_xfer *xfer) +{ + struct atmegadci_std_temp temp; + struct atmegadci_softc *sc; + struct atmegadci_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->xroot->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 = ATMEGA_BUS2SC(xfer->xroot->bus); + 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 = &atmegadci_setup_rx; + temp.len = xfer->frlengths[0]; + temp.pc = xfer->frbuffers + 0; + temp.short_pkt = temp.len ? 1 : 0; + + atmegadci_setup_standard_chain_sub(&temp); + } + x = 1; + } else { + x = 0; + } + + if (x != xfer->nframes) { + if (xfer->endpoint & UE_DIR_IN) { + temp.func = &atmegadci_data_tx; + need_sync = 1; + } else { + temp.func = &atmegadci_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; + } + + atmegadci_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 = &atmegadci_data_tx_sync; + temp.len = 0; + temp.short_pkt = 0; + + atmegadci_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 = &atmegadci_data_rx; + need_sync = 0; + } else { + temp.func = &atmegadci_data_tx; + need_sync = 1; + } + temp.len = 0; + temp.short_pkt = 0; + + atmegadci_setup_standard_chain_sub(&temp); + if (need_sync) { + /* we need a SYNC point after TX */ + temp.func = &atmegadci_data_tx_sync; + temp.len = 0; + temp.short_pkt = 0; + + atmegadci_setup_standard_chain_sub(&temp); + } + } + /* must have at least one frame! */ + td = temp.td; + xfer->td_transfer_last = td; +} + +static void +atmegadci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + atmegadci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +atmegadci_start_standard_chain(struct usb2_xfer *xfer) +{ + DPRINTFN(9, "\n"); + + /* poll one time - will turn on interrupts */ + if (atmegadci_xfer_do_fifo(xfer)) { + + /* put transfer on interrupt queue */ + usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &atmegadci_timeout, xfer->timeout); + } + } +} + +static void +atmegadci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + + DPRINTFN(9, "\n"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + + if (std->state != USB_SW_TR_PRE_DATA) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + atmegadci_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 +atmegadci_standard_done_sub(struct usb2_xfer *xfer) +{ + struct atmegadci_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 +atmegadci_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 = atmegadci_standard_done_sub(xfer); + } + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) { + goto done; + } + } + while (xfer->aframes != xfer->nframes) { + + err = atmegadci_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 = atmegadci_standard_done_sub(xfer); + } +done: + atmegadci_device_done(xfer, err); +} + +/*------------------------------------------------------------------------* + * atmegadci_device_done + * + * NOTE: this function can be called more than one time on the + * same USB transfer! + *------------------------------------------------------------------------*/ +static void +atmegadci_device_done(struct usb2_xfer *xfer, usb2_error_t error) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + uint8_t ep_no; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); + + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); + + /* disable endpoint interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0); + + DPRINTFN(15, "disabled interrupts!\n"); + } + /* dequeue transfer and start next transfer */ + usb2_transfer_done(xfer, error); +} + +static void +atmegadci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, + struct usb2_pipe *pipe) +{ + struct atmegadci_softc *sc; + uint8_t ep_no; + + USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); + + DPRINTFN(5, "pipe=%p\n", pipe); + + if (xfer) { + /* cancel any ongoing transfers */ + atmegadci_device_done(xfer, USB_ERR_STALLED); + } + sc = ATMEGA_BUS2SC(udev->bus); + /* get endpoint number */ + ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); + /* select endpoint number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); + /* set stall */ + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, + ATMEGA_UECONX_EPEN | + ATMEGA_UECONX_STALLRQ); +} + +static void +atmegadci_clear_stall_sub(struct atmegadci_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 number */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); + + /* set endpoint reset */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, ATMEGA_UERST_MASK(ep_no)); + + /* clear endpoint reset */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0); + + /* set stall */ + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, + ATMEGA_UECONX_EPEN | + ATMEGA_UECONX_STALLRQ); + + /* reset data toggle */ + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, + ATMEGA_UECONX_EPEN | + ATMEGA_UECONX_RSTDT); + + /* clear stall */ + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, + ATMEGA_UECONX_EPEN | + ATMEGA_UECONX_STALLRQC); + + if (ep_type == UE_CONTROL) { + /* one bank, 64-bytes wMaxPacket */ + ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X, + ATMEGA_UECFG0X_EPTYPE0); + ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X, + ATMEGA_UECFG1X_ALLOC | + ATMEGA_UECFG1X_EPBK0 | + ATMEGA_UECFG1X_EPSIZE(7)); + } else { + temp = 0; + if (ep_type == UE_BULK) { + temp |= ATMEGA_UECFG0X_EPTYPE2; + } else if (ep_type == UE_INTERRUPT) { + temp |= ATMEGA_UECFG0X_EPTYPE3; + } else { + temp |= ATMEGA_UECFG0X_EPTYPE1; + } + if (ep_dir & UE_DIR_IN) { + temp |= ATMEGA_UECFG0X_EPDIR; + } + /* two banks, 64-bytes wMaxPacket */ + ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X, temp); + ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X, + ATMEGA_UECFG1X_ALLOC | + ATMEGA_UECFG1X_EPBK1 | + ATMEGA_UECFG1X_EPSIZE(7)); + + temp = ATMEGA_READ_1(sc, ATMEGA_UESTA0X); + if (!(temp & ATMEGA_UESTA0X_CFGOK)) { + DPRINTFN(0, "Chip rejected configuration\n"); + } + } +} + +static void +atmegadci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + struct atmegadci_softc *sc; + struct usb2_endpoint_descriptor *ed; + + DPRINTFN(5, "pipe=%p\n", pipe); + + USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); + + /* check mode */ + if (udev->flags.usb2_mode != USB_MODE_DEVICE) { + /* not supported */ + return; + } + /* get softc */ + sc = ATMEGA_BUS2SC(udev->bus); + + /* get endpoint descriptor */ + ed = pipe->edesc; + + /* reset endpoint */ + atmegadci_clear_stall_sub(sc, + (ed->bEndpointAddress & UE_ADDR), + (ed->bmAttributes & UE_XFERTYPE), + (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); +} + +usb2_error_t +atmegadci_init(struct atmegadci_softc *sc) +{ + uint8_t n; + + DPRINTF("start\n"); + + /* set up the bus structure */ + sc->sc_bus.usbrev = USB_REV_1_1; + sc->sc_bus.methods = &atmegadci_bus_methods; + + USB_BUS_LOCK(&sc->sc_bus); + + /* enable USB PAD regulator */ + ATMEGA_WRITE_1(sc, ATMEGA_UHWCON, + ATMEGA_UHWCON_UVREGE); + + /* turn on clocks */ + (sc->sc_clocks_on) (&sc->sc_bus); + + /* wait a little for things to stabilise */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); + + /* enable interrupts */ + ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, + ATMEGA_UDINT_SUSPE | + ATMEGA_UDINT_EORSTE); + + /* reset all endpoints */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, + (1 << ATMEGA_EP_MAX) - 1); + + /* disable reset */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0); + + /* disable all endpoints */ + for (n = 1; n != ATMEGA_EP_MAX; n++) { + + /* select endpoint */ + ATMEGA_WRITE_1(sc, ATMEGA_UENUM, n); + + /* disable endpoint interrupt */ + ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0); + + /* disable endpoint */ + ATMEGA_WRITE_1(sc, ATMEGA_UECONX, 0); + } + + /* turn off clocks */ + + atmegadci_clocks_off(sc); + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch any lost interrupts */ + + atmegadci_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +atmegadci_uninit(struct atmegadci_softc *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + /* turn on clocks */ + (sc->sc_clocks_on) (&sc->sc_bus); + + /* disable interrupts */ + ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, 0); + + /* reset all endpoints */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, + (1 << ATMEGA_EP_MAX) - 1); + + /* disable reset */ + ATMEGA_WRITE_1(sc, ATMEGA_UERST, 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; + + atmegadci_pull_down(sc); + atmegadci_clocks_off(sc); + + /* disable USB PAD regulator */ + ATMEGA_WRITE_1(sc, ATMEGA_UHWCON, 0); + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +atmegadci_suspend(struct atmegadci_softc *sc) +{ + return; +} + +void +atmegadci_resume(struct atmegadci_softc *sc) +{ + return; +} + +static void +atmegadci_do_poll(struct usb2_bus *bus) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + atmegadci_interrupt_poll(sc); + atmegadci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/*------------------------------------------------------------------------* + * at91dci bulk support + *------------------------------------------------------------------------*/ +static void +atmegadci_device_bulk_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_bulk_close(struct usb2_xfer *xfer) +{ + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +atmegadci_device_bulk_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_bulk_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + atmegadci_setup_standard_chain(xfer); + atmegadci_start_standard_chain(xfer); +} + +struct usb2_pipe_methods atmegadci_device_bulk_methods = +{ + .open = atmegadci_device_bulk_open, + .close = atmegadci_device_bulk_close, + .enter = atmegadci_device_bulk_enter, + .start = atmegadci_device_bulk_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci control support + *------------------------------------------------------------------------*/ +static void +atmegadci_device_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_ctrl_close(struct usb2_xfer *xfer) +{ + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +atmegadci_device_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_ctrl_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + atmegadci_setup_standard_chain(xfer); + atmegadci_start_standard_chain(xfer); +} + +struct usb2_pipe_methods atmegadci_device_ctrl_methods = +{ + .open = atmegadci_device_ctrl_open, + .close = atmegadci_device_ctrl_close, + .enter = atmegadci_device_ctrl_enter, + .start = atmegadci_device_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci interrupt support + *------------------------------------------------------------------------*/ +static void +atmegadci_device_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_intr_close(struct usb2_xfer *xfer) +{ + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +atmegadci_device_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_intr_start(struct usb2_xfer *xfer) +{ + /* setup TDs */ + atmegadci_setup_standard_chain(xfer); + atmegadci_start_standard_chain(xfer); +} + +struct usb2_pipe_methods atmegadci_device_intr_methods = +{ + .open = atmegadci_device_intr_open, + .close = atmegadci_device_intr_close, + .enter = atmegadci_device_intr_enter, + .start = atmegadci_device_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +/*------------------------------------------------------------------------* + * at91dci full speed isochronous support + *------------------------------------------------------------------------*/ +static void +atmegadci_device_isoc_fs_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_device_isoc_fs_close(struct usb2_xfer *xfer) +{ + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +atmegadci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + 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 = + (ATMEGA_READ_1(sc, ATMEGA_UDFNUMH) << 8) | + (ATMEGA_READ_1(sc, ATMEGA_UDFNUML)); + + nframes &= ATMEGA_FRAME_MASK; + + /* + * check if the frame index is within the window where the frames + * will be inserted + */ + temp = (nframes - xfer->pipe->isoc_next) & ATMEGA_FRAME_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) & ATMEGA_FRAME_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) & ATMEGA_FRAME_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 */ + atmegadci_setup_standard_chain(xfer); +} + +static void +atmegadci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + atmegadci_start_standard_chain(xfer); +} + +struct usb2_pipe_methods atmegadci_device_isoc_fs_methods = +{ + .open = atmegadci_device_isoc_fs_open, + .close = atmegadci_device_isoc_fs_close, + .enter = atmegadci_device_isoc_fs_enter, + .start = atmegadci_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 +atmegadci_root_ctrl_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_root_ctrl_close(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* + * USB descriptors for the virtual Root HUB: + */ + +static const struct usb2_device_descriptor atmegadci_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 atmegadci_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 atmegadci_config_desc atmegadci_confd = { + .confd = { + .bLength = sizeof(struct usb2_config_descriptor), + .bDescriptorType = UDESC_CONFIG, + .wTotalLength[0] = sizeof(atmegadci_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 | ATMEGA_INTR_ENDPT), + .bmAttributes = UE_INTERRUPT, + .wMaxPacketSize[0] = 8, + .bInterval = 255, + }, +}; + +static const struct usb2_hub_descriptor_min atmegadci_hubd = { + .bDescLength = sizeof(atmegadci_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) >> 8, + .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, 'G', 0, 'A', 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, atmegadci_langtab); +USB_MAKE_STRING_DESC(STRING_VENDOR, atmegadci_vendor); +USB_MAKE_STRING_DESC(STRING_PRODUCT, atmegadci_product); + +static void +atmegadci_root_ctrl_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_root_ctrl_start(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +atmegadci_root_ctrl_task(struct usb2_bus *bus) +{ + atmegadci_root_ctrl_poll(ATMEGA_BUS2SC(bus)); +} + +static void +atmegadci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + uint16_t value; + uint16_t index; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + + if (std->state != USB_SW_TR_SETUP) { + if (std->state == USB_SW_TR_PRE_CALLBACK) { + /* transfer transferred */ + atmegadci_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->xroot->xfer_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(atmegadci_devd); + std->ptr = USB_ADD_BYTES(&atmegadci_devd, 0); + goto tr_valid; + case UDESC_CONFIG: + if (value & 0xff) { + goto tr_stalled; + } + std->len = sizeof(atmegadci_confd); + std->ptr = USB_ADD_BYTES(&atmegadci_confd, 0); + goto tr_valid; + case UDESC_STRING: + switch (value & 0xff) { + case 0: /* Language table */ + std->len = sizeof(atmegadci_langtab); + std->ptr = USB_ADD_BYTES(&atmegadci_langtab, 0); + goto tr_valid; + + case 1: /* Vendor */ + std->len = sizeof(atmegadci_vendor); + std->ptr = USB_ADD_BYTES(&atmegadci_vendor, 0); + goto tr_valid; + + case 2: /* Product */ + std->len = sizeof(atmegadci_product); + std->ptr = USB_ADD_BYTES(&atmegadci_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: + atmegadci_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; + atmegadci_pull_down(sc); + atmegadci_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) { + atmegadci_clocks_on(sc); + atmegadci_pull_up(sc); + } else { + atmegadci_pull_down(sc); + atmegadci_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.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(&atmegadci_hubd, 0); + std->len = sizeof(atmegadci_hubd); + goto tr_valid; + +tr_stalled: + std->err = USB_ERR_STALLED; +tr_valid: +done: + return; +} + +static void +atmegadci_root_ctrl_poll(struct atmegadci_softc *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &atmegadci_root_ctrl_done); +} + +struct usb2_pipe_methods atmegadci_root_ctrl_methods = +{ + .open = atmegadci_root_ctrl_open, + .close = atmegadci_root_ctrl_close, + .enter = atmegadci_root_ctrl_enter, + .start = atmegadci_root_ctrl_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 0, +}; + +/*------------------------------------------------------------------------* + * at91dci root interrupt support + *------------------------------------------------------------------------*/ +static void +atmegadci_root_intr_open(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_root_intr_close(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + atmegadci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +atmegadci_root_intr_enter(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_root_intr_start(struct usb2_xfer *xfer) +{ + struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +struct usb2_pipe_methods atmegadci_root_intr_methods = +{ + .open = atmegadci_root_intr_open, + .close = atmegadci_root_intr_close, + .enter = atmegadci_root_intr_enter, + .start = atmegadci_root_intr_start, + .enter_is_cancelable = 1, + .start_is_cancelable = 1, +}; + +static void +atmegadci_xfer_setup(struct usb2_setup_params *parm) +{ + const struct usb2_hw_ep_profile *pf; + struct atmegadci_softc *sc; + struct usb2_xfer *xfer; + void *last_obj; + uint32_t ntd; + uint32_t n; + uint8_t ep_no; + + sc = ATMEGA_BUS2SC(parm->udev->bus); + xfer = parm->curr_xfer; + + /* + * 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 == &atmegadci_device_ctrl_methods) { + + ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ + + 1 /* SYNC 2 */ ; + + } else if (parm->methods == &atmegadci_device_bulk_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &atmegadci_device_intr_methods) { + + ntd = xfer->nframes + 1 /* SYNC */ ; + + } else if (parm->methods == &atmegadci_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; + atmegadci_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 atmegadci_td *td; + + if (parm->buf) { + + td = USB_ADD_BYTES(parm->buf, parm->size[0]); + + /* init TD */ + td->max_packet_size = xfer->max_packet_size; + td->ep_no = 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; +} + +static void +atmegadci_xfer_unsetup(struct usb2_xfer *xfer) +{ + return; +} + +static void +atmegadci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, + struct usb2_pipe *pipe) +{ + struct atmegadci_softc *sc = ATMEGA_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 = &atmegadci_root_ctrl_methods; + break; + case UE_DIR_IN | ATMEGA_INTR_ENDPT: + pipe->methods = &atmegadci_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 = &atmegadci_device_ctrl_methods; + break; + case UE_INTERRUPT: + pipe->methods = &atmegadci_device_intr_methods; + break; + case UE_ISOCHRONOUS: + pipe->methods = &atmegadci_device_isoc_fs_methods; + break; + case UE_BULK: + pipe->methods = &atmegadci_device_bulk_methods; + break; + default: + /* do nothing */ + break; + } + } +} + +struct usb2_bus_methods atmegadci_bus_methods = +{ + .pipe_init = &atmegadci_pipe_init, + .xfer_setup = &atmegadci_xfer_setup, + .xfer_unsetup = &atmegadci_xfer_unsetup, + .do_poll = &atmegadci_do_poll, + .get_hw_ep_profile = &atmegadci_get_hw_ep_profile, + .set_stall = &atmegadci_set_stall, + .clear_stall = &atmegadci_clear_stall, + .roothub_exec = &atmegadci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/atmegadci.h b/sys/dev/usb/controller/atmegadci.h new file mode 100644 index 0000000..90b3334 --- /dev/null +++ b/sys/dev/usb/controller/atmegadci.h @@ -0,0 +1,273 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2009 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 register definitions, copied from ATMEGA + * documentation provided by ATMEL. + */ + +#ifndef _ATMEGADCI_H_ +#define _ATMEGADCI_H_ + +#define ATMEGA_MAX_DEVICES (USB_MIN_DEVICES + 1) + +#ifndef ATMEGA_HAVE_BUS_SPACE +#define ATMEGA_HAVE_BUS_SPACE 1 +#endif + +#define ATMEGA_UEINT 0xF4 +#define ATMEGA_UEINT_MASK(n) (1 << (n)) /* endpoint interrupt mask */ + +#define ATMEGA_UEBCHX 0xF3 /* FIFO byte count high */ +#define ATMEGA_UEBCLX 0xF2 /* FIFO byte count low */ +#define ATMEGA_UEDATX 0xF1 /* FIFO data */ + +#define ATMEGA_UEIENX 0xF0 /* interrupt enable register */ +#define ATMEGA_UEIENX_TXINE (1 << 0) +#define ATMEGA_UEIENX_STALLEDE (1 << 1) +#define ATMEGA_UEIENX_RXOUTE (1 << 2) +#define ATMEGA_UEIENX_RXSTPE (1 << 3) /* received SETUP packet */ +#define ATMEGA_UEIENX_NAKOUTE (1 << 4) +#define ATMEGA_UEIENX_NAKINE (1 << 6) +#define ATMEGA_UEIENX_FLERRE (1 << 7) + +#define ATMEGA_UESTA1X 0xEF +#define ATMEGA_UESTA1X_CURRBK (3 << 0) /* current bank */ +#define ATMEGA_UESTA1X_CTRLDIR (1 << 2) /* control endpoint direction */ + +#define ATMEGA_UESTA0X 0xEE +#define ATMEGA_UESTA0X_NBUSYBK (3 << 0) +#define ATMEGA_UESTA0X_DTSEQ (3 << 2) +#define ATMEGA_UESTA0X_UNDERFI (1 << 5) /* underflow */ +#define ATMEGA_UESTA0X_OVERFI (1 << 6) /* overflow */ +#define ATMEGA_UESTA0X_CFGOK (1 << 7) + +#define ATMEGA_UECFG1X 0xED /* endpoint config register */ +#define ATMEGA_UECFG1X_ALLOC (1 << 1) +#define ATMEGA_UECFG1X_EPBK0 (0 << 2) +#define ATMEGA_UECFG1X_EPBK1 (1 << 2) +#define ATMEGA_UECFG1X_EPBK2 (2 << 2) +#define ATMEGA_UECFG1X_EPBK3 (3 << 2) +#define ATMEGA_UECFG1X_EPSIZE(n) ((n) << 4) + +#define ATMEGA_UECFG0X 0xEC +#define ATMEGA_UECFG0X_EPDIR (1 << 0) /* endpoint direction */ +#define ATMEGA_UECFG0X_EPTYPE0 (0 << 6) +#define ATMEGA_UECFG0X_EPTYPE1 (1 << 6) +#define ATMEGA_UECFG0X_EPTYPE2 (2 << 6) +#define ATMEGA_UECFG0X_EPTYPE3 (3 << 6) + +#define ATMEGA_UECONX 0xEB +#define ATMEGA_UECONX_EPEN (1 << 0) +#define ATMEGA_UECONX_RSTDT (1 << 3) +#define ATMEGA_UECONX_STALLRQC (1 << 4) /* stall request clear */ +#define ATMEGA_UECONX_STALLRQ (1 << 5) /* stall request set */ + +#define ATMEGA_UERST 0xEA /* endpoint reset register */ +#define ATMEGA_UERST_MASK(n) (1 << (n)) + +#define ATMEGA_UENUM 0xE9 /* endpoint number */ + +#define ATMEGA_UEINTX 0xE8 /* interrupt register */ +#define ATMEGA_UEINTX_TXINI (1 << 0) +#define ATMEGA_UEINTX_STALLEDI (1 << 1) +#define ATMEGA_UEINTX_RXOUTI (1 << 2) +#define ATMEGA_UEINTX_RXSTPI (1 << 3) /* received setup packet */ +#define ATMEGA_UEINTX_NAKOUTI (1 << 4) +#define ATMEGA_UEINTX_RWAL (1 << 5) +#define ATMEGA_UEINTX_NAKINI (1 << 6) +#define ATMEGA_UEINTX_FIFOCON (1 << 7) + +#define ATMEGA_UDMFN 0xE6 +#define ATMEGA_UDMFN_FNCERR (1 << 4) + +#define ATMEGA_UDFNUMH 0xE5 /* frame number high */ +#define ATMEGA_UDFNUMH_MASK 7 + +#define ATMEGA_UDFNUML 0xE4 /* frame number low */ +#define ATMEGA_UDFNUML_MASK 0xFF + +#define ATMEGA_FRAME_MASK 0x7FF + +#define ATMEGA_UDADDR 0xE3 /* USB address */ +#define ATMEGA_UDADDR_MASK 0x7F +#define ATMEGA_UDADDR_ADDEN (1 << 7) + +#define ATMEGA_UDIEN 0xE2 /* USB device interrupt enable */ +#define ATMEGA_UDINT_SUSPE (1 << 0) +#define ATMEGA_UDINT_MSOFE (1 << 1) +#define ATMEGA_UDINT_SOFE (1 << 2) +#define ATMEGA_UDINT_EORSTE (1 << 3) +#define ATMEGA_UDINT_WAKEUPE (1 << 4) +#define ATMEGA_UDINT_EORSME (1 << 5) +#define ATMEGA_UDINT_UPRSME (1 << 6) + +#define ATMEGA_UDINT 0xE1 /* USB device interrupt status */ +#define ATMEGA_UDINT_SUSPI (1 << 0) +#define ATMEGA_UDINT_MSOFI (1 << 1) +#define ATMEGA_UDINT_SOFI (1 << 2) +#define ATMEGA_UDINT_EORSTI (1 << 3) +#define ATMEGA_UDINT_WAKEUPI (1 << 4) +#define ATMEGA_UDINT_EORSMI (1 << 5) +#define ATMEGA_UDINT_UPRSMI (1 << 6) + +#define ATMEGA_UDCON 0xE0 /* USB device connection register */ +#define ATMEGA_UDCON_DETACH (1 << 0) +#define ATMEGA_UDCON_RMWKUP (1 << 1) +#define ATMEGA_UDCON_LSM (1 << 2) +#define ATMEGA_UDCON_RSTCPU (1 << 3) + +#define ATMEGA_USBINT 0xDA +#define ATMEGA_USBINT_VBUSTI (1 << 0) /* USB VBUS interrupt */ + +#define ATMEGA_USBSTA 0xD9 +#define ATMEGA_USBSTA_VBUS (1 << 0) +#define ATMEGA_USBSTA_ID (1 << 1) + +#define ATMEGA_USBCON 0xD8 +#define ATMEGA_USBCON_VBUSTE (1 << 0) +#define ATMEGA_USBCON_OTGPADE (1 << 4) +#define ATMEGA_USBCON_FRZCLK (1 << 5) +#define ATMEGA_USBCON_USBE (1 << 7) + +#define ATMEGA_UHWCON 0xD7 +#define ATMEGA_UHWCON_UVREGE (1 << 0) + +#define ATMEGA_READ_1(sc, reg) \ + bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) + +#define ATMEGA_WRITE_1(sc, reg, data) \ + bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) + +#define ATMEGA_WRITE_MULTI_1(sc, reg, ptr, len) \ + bus_space_write_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len) + +#define ATMEGA_READ_MULTI_1(sc, reg, ptr, len) \ + bus_space_read_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len) + +/* + * Maximum number of endpoints supported: + */ +#define ATMEGA_EP_MAX 7 + +struct atmegadci_td; + +typedef uint8_t (atmegadci_cmd_t)(struct atmegadci_td *td); +typedef void (atmegadci_clocks_t)(struct usb2_bus *); + +struct atmegadci_td { + struct atmegadci_td *obj_next; + atmegadci_cmd_t *func; + struct usb2_page_cache *pc; + uint32_t offset; + uint32_t remainder; + uint16_t max_packet_size; + 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 ep_no:3; +}; + +struct atmegadci_std_temp { + atmegadci_cmd_t *func; + struct usb2_page_cache *pc; + struct atmegadci_td *td; + struct atmegadci_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 atmegadci_config_desc { + struct usb2_config_descriptor confd; + struct usb2_interface_descriptor ifcd; + struct usb2_endpoint_descriptor endpd; +} __packed; + +union atmegadci_hub_temp { + uWord wValue; + struct usb2_port_status ps; +}; + +struct atmegadci_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 atmegadci_softc { + struct usb2_bus sc_bus; + union atmegadci_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; + + /* must be set by by the bus interface layer */ + atmegadci_clocks_t *sc_clocks_on; + atmegadci_clocks_t *sc_clocks_off; + + struct usb2_device *sc_devices[ATMEGA_MAX_DEVICES]; + struct resource *sc_irq_res; + void *sc_intr_hdl; +#if (ATMEGA_HAVE_BUS_SPACE != 0) + struct resource *sc_io_res; + bus_space_tag_t sc_io_tag; + bus_space_handle_t sc_io_hdl; +#endif + 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 atmegadci_flags sc_flags; +}; + +/* prototypes */ + +usb2_error_t atmegadci_init(struct atmegadci_softc *sc); +void atmegadci_uninit(struct atmegadci_softc *sc); +void atmegadci_suspend(struct atmegadci_softc *sc); +void atmegadci_resume(struct atmegadci_softc *sc); +void atmegadci_interrupt(struct atmegadci_softc *sc); + +#endif /* _ATMEGADCI_H_ */ diff --git a/sys/dev/usb/controller/atmegadci_atmelarm.c b/sys/dev/usb/controller/atmegadci_atmelarm.c new file mode 100644 index 0000000..e63f5cc --- /dev/null +++ b/sys/dev/usb/controller/atmegadci_atmelarm.c @@ -0,0 +1,27 @@ +#include +__FBSDID("$FreeBSD$"); + +/*- + * Copyright (c) 2009 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/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c new file mode 100644 index 0000000..5802268 --- /dev/null +++ b/sys/dev/usb/controller/ehci.c @@ -0,0 +1,3965 @@ +/*- + * 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 + +#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_softc_t *sc, 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 void ehci_do_poll(struct usb2_bus *bus); +static void ehci_root_ctrl_poll(ehci_softc_t *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 { + ehci_softc_t *sc; + 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; +}; + +/* + * Byte-order conversion functions. + */ +static uint32_t +htoehci32(ehci_softc_t *sc, const uint32_t v) +{ + return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? + htobe32(v) : htole32(v)); +} + +static uint32_t +ehci32toh(ehci_softc_t *sc, const uint32_t v) +{ + return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? + be32toh(v) : le32toh(v)); +} + +void +ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) +{ + ehci_softc_t *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); + } +} + +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.bus_mtx, hz / 1000); + 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.bus_mtx, hz / 1000); + hcr = EOREAD4(sc, EHCI_USBCMD); + if (!(hcr & EHCI_CMD_HCRESET)) { + if (sc->sc_flags & EHCI_SCFLG_SETMODE) + EOWRITE4(sc, 0x68, 0x3); + 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; + + DPRINTF("start\n"); + + usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0); + +#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)); + + USB_BUS_LOCK(&sc->sc_bus); + err = ehci_hc_reset(sc); + USB_BUS_UNLOCK(&sc->sc_bus); + if (err) { + device_printf(sc->sc_bus.bdev, "reset timeout\n"); + return (err); + } + /* + * 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"); + return (USB_ERR_IOERROR); + } + /* 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 = + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_QH); + + qh->qh_endp = + htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); + qh->qh_endphub = + htoehci32(sc, EHCI_QH_SET_MULT(1)); + qh->qh_curqtd = 0; + + qh->qh_qtd.qtd_next = + htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = + htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_status = + htoehci32(sc, 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 = htoehci32(sc, 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 = + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_SITD); + + sitd->sitd_back = + htoehci32(sc, 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 = + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, 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 = + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_QH); + + /* fill the QH */ + qh->qh_endp = + htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); + qh->qh_endphub = htoehci32(sc, EHCI_QH_SET_MULT(1)); + qh->qh_link = qh->qh_self; + qh->qh_curqtd = 0; + + /* fill the overlay qTD */ + qh->qh_qtd.qtd_next = htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_status = htoehci32(sc, 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->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(NULL, hz / 1000); + hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; + if (!hcr) { + break; + } + } + if (hcr) { + device_printf(sc->sc_bus.bdev, "run timeout\n"); + return (USB_ERR_IOERROR); + } + + 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(ehci_softc_t *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + usb2_callout_stop(&sc->sc_tmo_pcd); + + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + if (ehci_hc_reset(sc)) { + DPRINTF("reset failed!\n"); + } + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* XXX let stray task complete */ + usb2_pause_mtx(NULL, hz / 20); + + usb2_callout_drain(&sc->sc_tmo_pcd); +} + +void +ehci_suspend(ehci_softc_t *sc) +{ + uint32_t cmd; + uint32_t hcr; + uint8_t i; + + USB_BUS_LOCK(&sc->sc_bus); + + 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.bus_mtx, hz / 1000); + } + + 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.bus_mtx, hz / 1000); + } + + if (hcr != EHCI_STS_HCH) { + device_printf(sc->sc_bus.bdev, + "config timeout\n"); + } + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +ehci_resume(ehci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint32_t cmd; + uint32_t hcr; + uint8_t i; + + USB_BUS_LOCK(&sc->sc_bus); + + /* 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.bus_mtx, + USB_MS_TO_TICKS(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.bus_mtx, hz / 1000); + } + if (hcr == EHCI_STS_HCH) { + device_printf(sc->sc_bus.bdev, "config timeout\n"); + } + + USB_BUS_UNLOCK(&sc->sc_bus); + + usb2_pause_mtx(NULL, + USB_MS_TO_TICKS(USB_RESUME_WAIT)); + + /* catch any lost interrupts */ + ehci_do_poll(&sc->sc_bus); +} + +void +ehci_shutdown(ehci_softc_t *sc) +{ + DPRINTF("stopping the HC\n"); + + USB_BUS_LOCK(&sc->sc_bus); + + if (ehci_hc_reset(sc)) { + DPRINTF("reset failed!\n"); + } + USB_BUS_UNLOCK(&sc->sc_bus); +} + +#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))); + } +} + +static void +ehci_dump_link(ehci_softc_t *sc, uint32_t link, int type) +{ + link = ehci32toh(sc, 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(">"); + } +} + +static void +ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd) +{ + uint32_t s; + + printf(" next="); + ehci_dump_link(sc, qtd->qtd_next, 0); + printf(" altnext="); + ehci_dump_link(sc, qtd->qtd_altnext, 0); + printf("\n"); + s = ehci32toh(sc, 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, + ehci32toh(sc, qtd->qtd_buffer[s])); + } + for (s = 0; s < 5; s++) { + printf(" buffer_hi[%d]=0x%08x\n", s, + ehci32toh(sc, qtd->qtd_buffer_hi[s])); + } +} + +static uint8_t +ehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd) +{ + uint8_t temp; + + usb2_pc_cpu_invalidate(sqtd->page_cache); + printf("QTD(%p) at 0x%08x:\n", sqtd, ehci32toh(sc, sqtd->qtd_self)); + ehci_dump_qtd(sc, sqtd); + temp = (sqtd->qtd_next & htoehci32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0; + return (temp); +} + +static void +ehci_dump_sqtds(ehci_softc_t *sc, 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(sc, sqtd); + } + if (sqtd) { + printf("dump aborted, too many TDs\n"); + } +} + +static void +ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh) +{ + uint32_t endp; + uint32_t endphub; + + usb2_pc_cpu_invalidate(qh->page_cache); + printf("QH(%p) at 0x%08x:\n", qh, ehci32toh(sc, qh->qh_self) & ~0x1F); + printf(" link="); + ehci_dump_link(sc, qh->qh_link, 1); + printf("\n"); + endp = ehci32toh(sc, 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 = ehci32toh(sc, 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(sc, qh->qh_curqtd, 0); + printf("\n"); + printf("Overlay qTD:\n"); + ehci_dump_qtd(sc, (void *)&qh->qh_qtd); +} + +static void +ehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd) +{ + usb2_pc_cpu_invalidate(sitd->page_cache); + printf("SITD(%p) at 0x%08x\n", sitd, ehci32toh(sc, sitd->sitd_self) & ~0x1F); + printf(" next=0x%08x\n", ehci32toh(sc, sitd->sitd_next)); + printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", + ehci32toh(sc, sitd->sitd_portaddr), + (sitd->sitd_portaddr & htoehci32(sc, EHCI_SITD_SET_DIR_IN)) + ? "in" : "out", + EHCI_SITD_GET_ADDR(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_ENDPT(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_PORT(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_HUBA(ehci32toh(sc, sitd->sitd_portaddr))); + printf(" mask=0x%08x\n", ehci32toh(sc, sitd->sitd_mask)); + printf(" status=0x%08x <%s> len=0x%x\n", ehci32toh(sc, sitd->sitd_status), + (sitd->sitd_status & htoehci32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", + EHCI_SITD_GET_LEN(ehci32toh(sc, sitd->sitd_status))); + printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", + ehci32toh(sc, sitd->sitd_back), + ehci32toh(sc, sitd->sitd_bp[0]), + ehci32toh(sc, sitd->sitd_bp[1]), + ehci32toh(sc, sitd->sitd_bp_hi[0]), + ehci32toh(sc, sitd->sitd_bp_hi[1])); +} + +static void +ehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd) +{ + usb2_pc_cpu_invalidate(itd->page_cache); + printf("ITD(%p) at 0x%08x\n", itd, ehci32toh(sc, itd->itd_self) & ~0x1F); + printf(" next=0x%08x\n", ehci32toh(sc, itd->itd_next)); + printf(" status[0]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[0]), + (itd->itd_status[0] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[1]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[1]), + (itd->itd_status[1] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[2]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[2]), + (itd->itd_status[2] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[3]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[3]), + (itd->itd_status[3] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[4]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[4]), + (itd->itd_status[4] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[5]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[5]), + (itd->itd_status[5] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[6]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[6]), + (itd->itd_status[6] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[7]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[7]), + (itd->itd_status[7] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" bp[0]=0x%08x\n", ehci32toh(sc, itd->itd_bp[0])); + printf(" addr=0x%02x; endpt=0x%01x\n", + EHCI_ITD_GET_ADDR(ehci32toh(sc, itd->itd_bp[0])), + EHCI_ITD_GET_ENDPT(ehci32toh(sc, itd->itd_bp[0]))); + printf(" bp[1]=0x%08x\n", ehci32toh(sc, itd->itd_bp[1])); + printf(" dir=%s; mpl=0x%02x\n", + (ehci32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", + EHCI_ITD_GET_MPL(ehci32toh(sc, itd->itd_bp[1]))); + printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", + ehci32toh(sc, itd->itd_bp[2]), + ehci32toh(sc, itd->itd_bp[3]), + ehci32toh(sc, itd->itd_bp[4]), + ehci32toh(sc, itd->itd_bp[5]), + ehci32toh(sc, itd->itd_bp[6])); + printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" + " 0x%08x,0x%08x,0x%08x\n", + ehci32toh(sc, itd->itd_bp_hi[0]), + ehci32toh(sc, itd->itd_bp_hi[1]), + ehci32toh(sc, itd->itd_bp_hi[2]), + ehci32toh(sc, itd->itd_bp_hi[3]), + ehci32toh(sc, itd->itd_bp_hi[4]), + ehci32toh(sc, itd->itd_bp_hi[5]), + ehci32toh(sc, itd->itd_bp_hi[6])); +} + +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(sc, itd); + itd = itd->prev; + } + + while (sitd && max && max--) { + ehci_dump_sitd(sc, sitd); + sitd = sitd->prev; + } +} + +#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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout); + } +} + +#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); + + if (sqh->prev != NULL) { + /* should not happen */ + DPRINTFN(0, "QH already linked!\n"); + return (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); + + 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); + } + 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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + 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; + + if (xfer->aframes != xfer->nframes) { + xfer->frlengths[xfer->aframes] = 0; + } + while (1) { + + usb2_pc_cpu_invalidate(td->page_cache); + status = ehci32toh(sc, td->qtd_status); + + len = EHCI_QTD_GET_BYTES(status); + + /* + * Verify the status length and + * add the length to "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] += td->len - 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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + + ehci_dump_sqtds(sc, 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); +} + +/*------------------------------------------------------------------------* + * 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; + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + + 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 = ehci32toh(sc, td->sitd_status); + + /* also check if first is complete */ + + td = xfer->td_transfer_first; + usb2_pc_cpu_invalidate(td->page_cache); + status |= ehci32toh(sc, 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 & htoehci32(sc, 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 = ehci32toh(sc, 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) +{ + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +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; + } + } +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK(&sc->sc_bus); + + 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: + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/* + * called when a request does not complete + */ +static void +ehci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + ehci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +ehci_do_poll(struct usb2_bus *bus) +{ + ehci_softc_t *sc = EHCI_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + ehci_interrupt_poll(sc); + ehci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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 = htoehci32(temp->sc, 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 | + htoehci32(temp->sc, EHCI_QTD_SET_BYTES(average)); + + if (average == 0) { + + if (temp->auto_data_toggle == 0) { + + /* update data toggle, ZLP case */ + + temp->qtd_status ^= + htoehci32(temp->sc, 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 ^= + htoehci32(temp->sc, 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] = + htoehci32(temp->sc, 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] = + htoehci32(temp->sc, + 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] = + htoehci32(temp->sc, + 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; +} + +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->xroot->udev)); + + temp.average = xfer->max_usb2_frame_size; + temp.max_frame_size = xfer->max_frame_size; + temp.sc = EHCI_BUS2SC(xfer->xroot->bus); + + /* 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 |= + htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); + } + temp.auto_data_toggle = 0; + } else { + temp.auto_data_toggle = 1; + } + + if (usb2_get_speed(xfer->xroot->udev) != USB_SPEED_HIGH) { + /* max 3 retries */ + temp.qtd_status |= + htoehci32(temp.sc, 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 &= + htoehci32(temp.sc, 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 &= + htoehci32(temp.sc, 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) ? + htoehci32(temp.sc, EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : + htoehci32(temp.sc, 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 &= htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(1)); + temp.qtd_status |= + (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? + htoehci32(temp.sc, EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | + EHCI_QTD_SET_TOGGLE(1)) : + htoehci32(temp.sc, 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 = htoehci32(temp.sc, EHCI_LINK_TERMINATE); + td->qtd_altnext = htoehci32(temp.sc, EHCI_LINK_TERMINATE); + td->qtd_status |= htoehci32(temp.sc, 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(temp.sc, + 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->xroot->udev) == USB_SPEED_HIGH) { + qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | + EHCI_QH_DTC); + if (methods != &ehci_device_intr_methods) + qh_endp |= EHCI_QH_SET_NRL(8); + } else { + + if (usb2_get_speed(xfer->xroot->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 = htoehci32(temp.sc, 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->xroot->udev->hs_hub_addr) | + EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no)); + + qh->qh_endphub = htoehci32(temp.sc, qh_endphub); + qh->qh_curqtd = htoehci32(temp.sc, 0); + + /* fill the overlay qTD */ + qh->qh_qtd.qtd_status = htoehci32(temp.sc, 0); + + if (temp.auto_data_toggle) { + + /* let the hardware compute the data toggle */ + + qh->qh_endp &= htoehci32(temp.sc, ~EHCI_QH_DTC); + + if (xfer->pipe->toggle_next) { + /* DATA1 is next */ + qh->qh_qtd.qtd_status |= + htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); + } + } + td = xfer->td_transfer_first; + + qh->qh_qtd.qtd_next = td->qtd_self; + qh->qh_qtd.qtd_altnext = + htoehci32(temp.sc, EHCI_LINK_TERMINATE); + + usb2_pc_cpu_flush(qh->page_cache); + + if (xfer->xroot->udev->pwr_save.suspended == 0) { + EHCI_APPEND_QH(qh, *qh_last); + } +} + +static void +ehci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + uint16_t i; + uint16_t m; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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(sc, td); + } +#endif + usb2_pc_cpu_invalidate(td->page_cache); + status = ehci32toh(sc, 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; +} + +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(sc, td); + } +#endif + + usb2_pc_cpu_invalidate(td->page_cache); + status = ehci32toh(sc, 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; +} + +/* 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 = EHCI_BUS2SC(xfer->xroot->bus); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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(sc, + 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + uint16_t best; + uint16_t bit; + uint16_t x; + uint8_t slot; + + /* Allocate a microframe slot first: */ + + slot = usb2_intr_schedule_adjust + (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); + + if (usb2_get_speed(xfer->xroot->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); +} + +static void +ehci_device_intr_close(struct usb2_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + uint8_t slot; + + slot = usb2_intr_schedule_adjust + (xfer->xroot->udev, -(xfer->max_frame_size), xfer->usb2_uframe); + + sc->sc_intr_stat[xfer->qh_pos]--; + + ehci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + 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->xroot->udev->hs_hub_addr) | + EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no); + + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + sitd_portaddr |= EHCI_SITD_SET_DIR_IN; + } + sitd_portaddr = htoehci32(sc, 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 = htoehci32(sc, EHCI_LINK_TERMINATE); + + usb2_pc_cpu_flush(td->page_cache); + } + } +} + +static void +ehci_device_isoc_fs_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + 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->xroot->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] = htoehci32(sc, 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] = htoehci32(sc, temp); + + td->sitd_mask = htoehci32(sc, 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(sc, 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); +} + +static void +ehci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); +} + +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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + 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] = htoehci32(sc, temp); + + /* set transfer multiplier */ + td->itd_bp[2] = htoehci32(sc, xfer->max_packet_count & 3); + + usb2_pc_cpu_flush(td->page_cache); + } + } +} + +static void +ehci_device_isoc_hs_close(struct usb2_xfer *xfer) +{ + ehci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + 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] = htoehci32(sc, 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] &= htoehci32(sc, 0xFFF); + td->itd_bp[0] |= htoehci32(sc, 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] |= htoehci32(sc, 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] &= htoehci32(sc, 0xFFF); + td->itd_bp[page_no] |= htoehci32(sc, page_addr); + } + } + } + /* set IOC bit if we are complete */ + if (nframes == 0) { + td->itd_status[7] |= htoehci32(sc, 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(sc, 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); +} + +static void +ehci_device_isoc_hs_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ehci_transfer_intr_enqueue(xfer); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + ehci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* 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 = EHCI_BUS2SC(xfer->xroot->bus); + + DPRINTF("\n"); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +ehci_root_ctrl_task(struct usb2_bus *bus) +{ + ehci_root_ctrl_poll(EHCI_BUS2SC(bus)); +} + +static void +ehci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); + char *ptr; + uint32_t port; + uint32_t v; + uint16_t i; + uint16_t value; + uint16_t index; + uint8_t l; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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: + if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) { + + /* + * waking up a High Speed device is rather + * complicated if + */ + EOWRITE4(sc, port, v | EHCI_PS_FPR); + } + /* wait 20ms for resume sequence to complete */ + if (use_polling) { + /* polling */ + DELAY(20000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); + } + + EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP | + EHCI_PS_FPR | (3 << 10) /* High Speed */ )); + + /* settle time */ + if (use_polling) { + /* polling */ + DELAY(4000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); + } + 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)); + /* XXX can't find out? */ + sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; + 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); + if (sc->sc_flags & EHCI_SCFLG_FORCESPEED) { + if ((v & 0xc000000) == 0x8000000) + i = UPS_HIGH_SPEED; + else if ((v & 0xc000000) == 0x4000000) + i = UPS_LOW_SPEED; + else + i = 0; + } else { + 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) && !(v & EHCI_PS_FPR)) + 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 (v & EHCI_PS_FPR) + i |= UPS_C_SUSPEND; + 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.bus_mtx, + USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY)); + } + + /* Terminate reset sequence. */ + if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM)) + 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.bus_mtx, + USB_MS_TO_TICKS(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(ehci_softc_t *sc) +{ + usb2_sw_transfer(&sc->sc_root_ctrl, + &ehci_root_ctrl_done); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + ehci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = EHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +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; + + /* + * 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 = htoehci32(sc, 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 = htoehci32(sc, 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 = htoehci32(sc, 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 = htoehci32(sc, 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; + } +} + +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 */ +} + +static void +ehci_device_resume(struct usb2_device *udev) +{ + ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + + if ((methods == &ehci_device_bulk_methods) || + (methods == &ehci_device_ctrl_methods)) { + EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_async_p_last); + } + if (methods == &ehci_device_intr_methods) { + EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ehci_device_suspend(struct usb2_device *udev) +{ + ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + + if ((methods == &ehci_device_bulk_methods) || + (methods == &ehci_device_ctrl_methods)) { + 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]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ehci_set_hw_power(struct usb2_bus *bus) +{ + ehci_softc_t *sc = EHCI_BUS2SC(bus); + uint32_t temp; + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + temp = EOREAD4(sc, EHCI_USBCMD); + + temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE); + + if (flags & (USB_HW_POWER_CONTROL | + USB_HW_POWER_BULK)) { + DPRINTF("Async is active\n"); + temp |= EHCI_CMD_ASE; + } + if (flags & (USB_HW_POWER_INTERRUPT | + USB_HW_POWER_ISOC)) { + DPRINTF("Periodic is active\n"); + temp |= EHCI_CMD_PSE; + } + EOWRITE4(sc, EHCI_USBCMD, temp); + + USB_BUS_UNLOCK(bus); + + 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, + .device_resume = ehci_device_resume, + .device_suspend = ehci_device_suspend, + .set_hw_power = ehci_set_hw_power, + .roothub_exec = ehci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/ehci.h b/sys/dev/usb/controller/ehci.h new file mode 100644 index 0000000..9d7baa1 --- /dev/null +++ b/sys/dev/usb/controller/ehci.h @@ -0,0 +1,532 @@ +/* $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_ + +#define EHCI_MAX_DEVICES USB_MAX_DEVICES + +/* 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_USBMODE 0x68 /* RW USB Device mode register */ +#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */ +#define EHCI_UM_CM_IDLE 0x0 /* Idle */ +#define EHCI_UM_CM_HOST 0x3 /* Host Controller */ +#define EHCI_UM_ES 0x00000004 /* R/WO Endian Select */ +#define EHCI_UM_ES_LE 0x0 /* Little-endian byte alignment */ +#define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */ +#define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */ + +#define EHCI_PORT_RESET_COMPLETE 2 /* ms */ + +/* + * 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_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 usb2_device *sc_devices[EHCI_MAX_DEVICES]; + 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; + 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 */ + uint16_t sc_flags; /* chip specific flags */ +#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init */ +#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed */ +#define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence */ +#define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */ +#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */ +#define EHCI_SCFLG_TT 0x0020 /* transaction translator present */ + + 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/usb/controller/ehci_ixp4xx.c b/sys/dev/usb/controller/ehci_ixp4xx.c new file mode 100644 index 0000000..b369d47 --- /dev/null +++ b/sys/dev/usb/controller/ehci_ixp4xx.c @@ -0,0 +1,348 @@ +/*- + * Copyright (c) 2008 Sam Leffler. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * IXP435 attachment driver for the USB Enhanced Host Controller. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define EHCI_VENDORID_IXP4XX 0x42fa05 +#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller" + +struct ixp_ehci_softc { + ehci_softc_t base; /* storage for EHCI code */ + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct bus_space tag; /* tag for private bus space ops */ +}; + +static device_attach_t ehci_ixp_attach; +static device_detach_t ehci_ixp_detach; +static device_shutdown_t ehci_ixp_shutdown; +static device_suspend_t ehci_ixp_suspend; +static device_resume_t ehci_ixp_resume; + +static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t); +static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t); +static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t); + +static int +ehci_ixp_suspend(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_suspend(self); + if (err) + return (err); + ehci_suspend(sc); + return (0); +} + +static int +ehci_ixp_resume(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + ehci_resume(sc); + + bus_generic_resume(self); + + return (0); +} + +static int +ehci_ixp_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 int +ehci_ixp_probe(device_t self) +{ + + device_set_desc(self, EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +ehci_ixp_attach(device_t self) +{ + struct ixp_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + int err; + int rid; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* 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_bus.usbrev = USB_REV_2_0; + + /* NB: hints fix the memory location and irq */ + + rid = 0; + 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; + } + + /* + * Craft special resource for bus space ops that handle + * byte-alignment of non-word addresses. Also, since + * we're already intercepting bus space ops we handle + * the register window offset that could otherwise be + * done with bus_space_subregion. + */ + isc->iot = rman_get_bustag(sc->sc_io_res); + isc->tag.bs_cookie = isc->iot; + /* read single */ + isc->tag.bs_r_1 = ehci_bs_r_1, + isc->tag.bs_r_2 = ehci_bs_r_2, + isc->tag.bs_r_4 = ehci_bs_r_4, + /* write (single) */ + isc->tag.bs_w_1 = ehci_bs_w_1, + isc->tag.bs_w_2 = ehci_bs_w_2, + isc->tag.bs_w_4 = ehci_bs_w_4, + + sc->sc_io_tag = &isc->tag; + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = IXP435_USB1_SIZE - 0x100; + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + 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); + device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Intel"); + + + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + /* + * Arrange to force Host mode, select big-endian byte alignment, + * and arrange to not terminate reset operations (the adapter + * will ignore it if we do but might as well save a reg write). + * Also, the controller has an embedded Transaction Translator + * which means port speed must be read from the Port Status + * register following a port enable. + */ + sc->sc_flags |= EHCI_SCFLG_TT + | EHCI_SCFLG_SETMODE + | EHCI_SCFLG_BIGEDESC + | EHCI_SCFLG_BIGEMMIO + | EHCI_SCFLG_NORESTERM + ; + + 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_ixp_detach(self); + return (ENXIO); +} + +static int +ehci_ixp_detach(device_t self) +{ + struct ixp_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + device_t bdev; + int err; + + 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 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); + + 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, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +/* + * Bus space accessors for PIO operations. + */ + +static uint8_t +ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_1((bus_space_tag_t) t, h, + 0x100 + (o &~ 3) + (3 - (o & 3))); +} + +static void +ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v) +{ + panic("%s", __func__); +} + +static uint16_t +ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_2((bus_space_tag_t) t, h, + 0x100 + (o &~ 3) + (2 - (o & 3))); +} + +static void +ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v) +{ + panic("%s", __func__); +} + +static uint32_t +ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o); +} + +static void +ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v) +{ + bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ehci_ixp_probe), + DEVMETHOD(device_attach, ehci_ixp_attach), + DEVMETHOD(device_detach, ehci_ixp_detach), + DEVMETHOD(device_suspend, ehci_ixp_suspend), + DEVMETHOD(device_resume, ehci_ixp_resume), + DEVMETHOD(device_shutdown, ehci_ixp_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct ixp_ehci_softc), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/ehci_mbus.c b/sys/dev/usb/controller/ehci_mbus.c new file mode 100644 index 0000000..66cf8cc --- /dev/null +++ b/sys/dev/usb/controller/ehci_mbus.c @@ -0,0 +1,364 @@ +/*- + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Developed by Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * MBus attachment driver for the USB Enhanced Host Controller. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define EHCI_VENDORID_MRVL 0x1286 +#define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller" + +static device_attach_t ehci_mbus_attach; +static device_detach_t ehci_mbus_detach; +static device_shutdown_t ehci_mbus_shutdown; +static device_suspend_t ehci_mbus_suspend; +static device_resume_t ehci_mbus_resume; + +static int err_intr(void *arg); + +static struct resource *irq_err; +static void *ih_err; + +#define USB_BRIDGE_INTR_CAUSE 0x210 +#define USB_BRIDGE_INTR_MASK 0x214 + +#define MV_USB_ADDR_DECODE_ERR (1 << 0) +#define MV_USB_HOST_UNDERFLOW (1 << 1) +#define MV_USB_HOST_OVERFLOW (1 << 2) +#define MV_USB_DEVICE_UNDERFLOW (1 << 3) + +static int +ehci_mbus_suspend(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_suspend(self); + if (err) + return (err); + ehci_suspend(sc); + return (0); +} + +static int +ehci_mbus_resume(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + ehci_resume(sc); + + bus_generic_resume(self); + + return (0); +} + +static int +ehci_mbus_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 int +ehci_mbus_probe(device_t self) +{ + + device_set_desc(self, EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +ehci_mbus_attach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + bus_space_handle_t bsh; + int err; + int rid; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* 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_bus.usbrev = USB_REV_2_0; + + rid = 0; + 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); + bsh = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = MV_USB_SIZE - MV_USB_HOST_OFST; + + /* + * Marvell EHCI host controller registers start at certain offset within + * the whole USB registers range, so create a subregion for the host + * mode configuration purposes. + */ + if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST, + sc->sc_io_size, &sc->sc_io_hdl) != 0) + panic("%s: unable to subregion USB host registers", + device_get_name(self)); + + rid = 0; + irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (irq_err == NULL) { + device_printf(self, "Could not allocate error irq\n"); + ehci_mbus_detach(self); + return (ENXIO); + } + + /* + * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to + * use the correct rid for the main one (controller interrupt) -- + * refer to obio_devices[] for the right resource number to use here. + */ + rid = 1; + sc->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); + device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Marvell"); + + err = bus_setup_intr(self, irq_err, INTR_FAST | INTR_TYPE_BIO, + err_intr, NULL, sc, &ih_err); + if (err) { + device_printf(self, "Could not setup error irq, %d\n", err); + ih_err = NULL; + goto error; + } + + EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR | + MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW | + MV_USB_DEVICE_UNDERFLOW); + + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + /* + * Workaround for Marvell integrated EHCI controller: reset of + * the EHCI core clears the USBMODE register, which sets the core in + * an undefined state (neither host nor agent), so it needs to be set + * again for proper operation. + * + * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for + * details. + */ + sc->sc_flags |= EHCI_SCFLG_SETMODE; + if (bootverbose) + device_printf(self, "5.24 GL USB-2 workaround enabled\n"); + + /* XXX all MV chips need it? */ + sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM; + + err = ehci_init(sc); + if (!err) { + 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_mbus_detach(self); + return (ENXIO); +} + +static int +ehci_mbus_detach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + device_t bdev; + int err; + + 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 ehci_init + */ + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); + } + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + 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 (irq_err && ih_err) { + err = bus_teardown_intr(self, irq_err, ih_err); + + if (err) + device_printf(self, "Could not tear down irq, %d\n", + err); + ih_err = NULL; + } + if (irq_err) { + bus_release_resource(self, SYS_RES_IRQ, 0, irq_err); + irq_err = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static int +err_intr(void *arg) +{ + ehci_softc_t *sc = arg; + unsigned int cause; + + cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE); + if (cause) { + printf("IRQ ERR: cause: 0x%08x\n", cause); + if (cause & MV_USB_ADDR_DECODE_ERR) + printf("IRQ ERR: Address decoding error\n"); + if (cause & MV_USB_HOST_UNDERFLOW) + printf("IRQ ERR: USB Host Underflow\n"); + if (cause & MV_USB_HOST_OVERFLOW) + printf("IRQ ERR: USB Host Overflow\n"); + if (cause & MV_USB_DEVICE_UNDERFLOW) + printf("IRQ ERR: USB Device Underflow\n"); + if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW | + MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW)) + printf("IRQ ERR: Unknown error\n"); + + EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0); + } + return (FILTER_HANDLED); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ehci_mbus_probe), + DEVMETHOD(device_attach, ehci_mbus_attach), + DEVMETHOD(device_detach, ehci_mbus_detach), + DEVMETHOD(device_suspend, ehci_mbus_suspend), + DEVMETHOD(device_resume, ehci_mbus_resume), + DEVMETHOD(device_shutdown, ehci_mbus_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(ehci_softc_t), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c new file mode 100644 index 0000000..856ea68 --- /dev/null +++ b/sys/dev/usb/controller/ehci_pci.c @@ -0,0 +1,486 @@ +/*- + * 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 + +#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; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* get all DMA memory */ + if (usb2_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + 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: + /* Quirk for Parallels Desktop 4.0 */ + device_printf(self, "USB revision is unknown. Assuming v2.0.\n"); + sc->sc_bus.usbrev = USB_REV_2_0; + 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)); + } + +#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; + + 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_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, hz / 100); /* wait 10ms */ + } + } +} + +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, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c new file mode 100644 index 0000000..2194756 --- /dev/null +++ b/sys/dev/usb/controller/musb_otg.c @@ -0,0 +1,2875 @@ +/* $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 + +#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 *, usb2_error_t); +static void musbotg_do_poll(struct usb2_bus *); +static void musbotg_root_ctrl_poll(struct musbotg_softc *); +static void musbotg_standard_done(struct usb2_xfer *); +static void musbotg_interrupt_poll(struct musbotg_softc *); + +static usb2_sw_transfer_func_t musbotg_root_intr_done; +static usb2_sw_transfer_func_t musbotg_root_ctrl_done; + +/* + * 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; + } +} + +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 */ + } +} + +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; + } +} + +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); +} + +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); + } +} + +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); + } +} + +static void +musbotg_wakeup_peer(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + uint8_t temp; + uint8_t use_polling; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + use_polling = mtx_owned(xfer->xroot->xfer_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.bus_mtx, hz / 125); + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); +} + +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); +} + +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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + + /* 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; + } + } +} + +void +musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on) +{ + DPRINTFN(4, "vbus = %u\n", is_on); + + USB_BUS_LOCK(&sc->sc_bus); + 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); + } + } + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; + + USB_BUS_LOCK(&sc->sc_bus); + +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; + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; +} + +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->xroot->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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + 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; +} + +static void +musbotg_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTFN(1, "xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + musbotg_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +musbotg_ep_int_set(struct usb2_xfer *xfer, uint8_t on) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + 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); + } + } +} + +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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &musbotg_timeout, xfer->timeout); + } + } +} + +static void +musbotg_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + + DPRINTFN(8, "\n"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +/*------------------------------------------------------------------------* + * 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) +{ + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); +} + +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; + + USB_BUS_LOCK_ASSERT(udev->bus, 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); + } +} + +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); + } + } +} + +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); + + USB_BUS_LOCK_ASSERT(udev->bus, 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))); +} + +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; + + USB_BUS_LOCK(&sc->sc_bus); + + /* 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.bus_mtx, hz / 1000); + + /* 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.bus_mtx, hz / 100); + + /* 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); + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch any lost interrupts */ + + musbotg_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +musbotg_uninit(struct musbotg_softc *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + /* 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); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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); + + USB_BUS_LOCK(&sc->sc_bus); + musbotg_interrupt_poll(sc); + musbotg_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void +musbotg_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + 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->xroot->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); +} + +static void +musbotg_device_isoc_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + musbotg_start_standard_chain(xfer); +} + +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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + musbotg_device_done(xfer, USB_ERR_CANCELLED); +} + +/* + * 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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +musbotg_root_ctrl_task(struct usb2_bus *bus) +{ + musbotg_root_ctrl_poll(MUSBOTG_BUS2SC(bus)); +} + +static void +musbotg_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + uint16_t value; + uint16_t index; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); +} + +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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + musbotg_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = MUSBOTG_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +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; + + /* + * 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; +} + +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; + } + } +} + +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, + .roothub_exec = &musbotg_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/musb_otg.h b/sys/dev/usb/controller/musb_otg.h new file mode 100644 index 0000000..0d880e1 --- /dev/null +++ b/sys/dev/usb/controller/musb_otg.h @@ -0,0 +1,407 @@ +/* $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_ + +#define MUSB2_MAX_DEVICES (USB_MIN_DEVICES + 1) + +/* 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_hw_ep_profile sc_hw_ep_profile[16]; + + struct usb2_device *sc_devices[MUSB2_MAX_DEVICES]; + 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); +void musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on); + +#endif /* _MUSB2_OTG_H_ */ diff --git a/sys/dev/usb/controller/musb_otg_atmelarm.c b/sys/dev/usb/controller/musb_otg_atmelarm.c new file mode 100644 index 0000000..7652424 --- /dev/null +++ b/sys/dev/usb/controller/musb_otg_atmelarm.c @@ -0,0 +1,239 @@ +/* $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 + +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_poll(struct musbotg_super_softc *sc) +{ + uint8_t vbus_val = 1; /* fake VBUS on - TODO */ + + /* just forward it */ + musbotg_vbus_interrupt(&sc->sc_otg, vbus_val); +} + +static void +musbotg_clocks_on(void *arg) +{ +#if 0 + struct musbotg_super_softc *sc = arg; + +#endif +} + +static void +musbotg_clocks_off(void *arg) +{ +#if 0 + struct musbotg_super_softc *sc = arg; + +#endif +} + +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; + + /* 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; + + /* initialise some bus fields */ + sc->sc_otg.sc_bus.parent = dev; + sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; + sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES; + + /* 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); + +#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_poll(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_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, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c new file mode 100644 index 0000000..7751bd5 --- /dev/null +++ b/sys/dev/usb/controller/ohci.c @@ -0,0 +1,2862 @@ +/*- + * 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 + +#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 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); + } +} + +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(NULL, hz / 1000); + 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(NULL, + USB_MS_TO_TICKS(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(NULL, + USB_MS_TO_TICKS(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(NULL, + USB_MS_TO_TICKS(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(NULL, + USB_MS_TO_TICKS(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; + + 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.bus_mtx, 0); + +#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)) { + return (USB_ERR_INVAL); + } else { + /* 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) +{ + USB_BUS_LOCK(&sc->sc_bus); + + usb2_callout_stop(&sc->sc_tmo_rhsc); + + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* XXX let stray task complete */ + usb2_pause_mtx(NULL, hz / 20); + + usb2_callout_drain(&sc->sc_tmo_rhsc); +} + +/* NOTE: suspend/resume is called from + * interrupt context and cannot sleep! + */ +void +ohci_suspend(ohci_softc_t *sc) +{ + uint32_t ctl; + + USB_BUS_LOCK(&sc->sc_bus); + +#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.bus_mtx, + USB_MS_TO_TICKS(USB_RESUME_WAIT)); + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +ohci_resume(ohci_softc_t *sc) +{ + uint32_t ctl; + +#if USB_DEBUG + DPRINTF("\n"); + if (ohcidebug > 2) { + ohci_dumpregs(sc); + } +#endif + /* some broken BIOSes never initialize the Controller chip */ + ohci_controller_init(sc); + + USB_BUS_LOCK(&sc->sc_bus); + 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.bus_mtx, + USB_MS_TO_TICKS(USB_RESUME_DELAY)); + ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; + OWRITE4(sc, OHCI_CONTROL, ctl); + usb2_pause_mtx(&sc->sc_bus.bus_mtx, + USB_MS_TO_TICKS(USB_RESUME_RECOVERY)); + sc->sc_control = sc->sc_intre = 0; + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch any lost interrupts */ + ohci_do_poll(&sc->sc_bus); +} + +#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)); +} +static void +ohci_dump_tds(ohci_td_t *std) +{ + for (; std; std = std->obj_next) { + if (ohci_dump_td(std)) { + break; + } + } +} + +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; + } + } +} + +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)); +} + +#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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout); + } +} + +#define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last) +static ohci_ed_t * +_ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last) +{ + DPRINTFN(11, "%p to %p\n", sed, last); + + if (sed->prev != NULL) { + /* should not happen */ + DPRINTFN(0, "ED already linked!\n"); + return (last); + } + /* (sc->sc_bus.bus_mtx) must be locked */ + + sed->next = last->next; + sed->ed_next = last->ed_next; + sed->ed_tailp = 0; + + 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.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); + } + 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); +} + +#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; + + if (xfer->aframes != xfer->nframes) { + xfer->frlengths[xfer->aframes] = 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) { + /* + * Sum up total transfer length + * in "frlengths[]": + */ + xfer->frlengths[xfer->aframes] += td->len - temp; + } + } else { + if (xfer->aframes != xfer->nframes) { + /* transfer was complete */ + xfer->frlengths[xfer->aframes] += td->len; + } + } + /* 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); +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +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; + } + } +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK(&sc->sc_bus); + + 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: + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/* + * called when a request does not complete + */ +static void +ohci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + ohci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +ohci_do_poll(struct usb2_bus *bus) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + ohci_interrupt_poll(sc); + ohci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; +} + +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->xroot->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->xroot->udev->speed == USB_SPEED_LOW) { + ed_flags |= OHCI_ED_SPEED; + } + ed->ed_flags = htole32(ed_flags); + + td = xfer->td_transfer_first; + + ed->ed_headp = td->td_self; + + if (xfer->xroot->udev->pwr_save.suspended == 0) { + /* the append function will flush the endpoint descriptor */ + OHCI_APPEND_QH(ed, *ed_last); + + if (methods == &ohci_device_bulk_methods) { + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + } + if (methods == &ohci_device_ctrl_methods) { + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + } + } else { + usb2_pc_cpu_flush(ed->page_cache); + } +} + +static void +ohci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + uint32_t hstatus; + uint16_t i; + uint16_t m; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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 = OHCI_BUS2SC(xfer->xroot->bus); + ohci_ed_t *ed; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + 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); +} + +static void +ohci_device_intr_close(struct usb2_xfer *xfer) +{ + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_intr_stat[xfer->qh_pos]--; + + ohci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + /* 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); +} + +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); +} + +static void +ohci_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct usb2_page_search buf_res; + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + 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->xroot->udev->speed == USB_SPEED_LOW) { + ed_flags |= OHCI_ED_SPEED; + } + ed->ed_flags = htole32(ed_flags); + + td = xfer->td_transfer_first; + + ed->ed_headp = td->itd_self; + + /* isochronous transfers are not affected by suspend / resume */ + /* the append function will flush the endpoint descriptor */ + + OHCI_APPEND_QH(ed, sc->sc_isoc_p_last); +} + +static void +ohci_device_isoc_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + ohci_transfer_intr_enqueue(xfer); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + ohci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* 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 = OHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +ohci_root_ctrl_task(struct usb2_bus *bus) +{ + ohci_root_ctrl_poll(OHCI_BUS2SC(bus)); +} + +static void +ohci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); + char *ptr; + uint32_t port; + uint32_t v; + uint16_t value; + uint16_t index; + uint8_t l; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); + 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.bus_mtx, + USB_MS_TO_TICKS(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); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + ohci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = OHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +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; + + 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; + } +} + +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; + } + } +} + +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 */ +} + +static void +ohci_device_resume(struct usb2_device *udev) +{ + struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + ohci_ed_t *ed; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (methods == &ohci_device_bulk_methods) { + OHCI_APPEND_QH(ed, sc->sc_bulk_p_last); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + } + if (methods == &ohci_device_ctrl_methods) { + OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + } + if (methods == &ohci_device_intr_methods) { + OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ohci_device_suspend(struct usb2_device *udev) +{ + struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + ohci_ed_t *ed; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + 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]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ohci_set_hw_power(struct usb2_bus *bus) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + uint32_t temp; + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + temp = OREAD4(sc, OHCI_CONTROL); + temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE); + + if (flags & USB_HW_POWER_CONTROL) + temp |= OHCI_CLE; + + if (flags & USB_HW_POWER_BULK) + temp |= OHCI_BLE; + + if (flags & USB_HW_POWER_INTERRUPT) + temp |= OHCI_PLE; + + if (flags & USB_HW_POWER_ISOC) + temp |= OHCI_IE | OHCI_PLE; + + OWRITE4(sc, OHCI_CONTROL, temp); + + USB_BUS_UNLOCK(bus); + + 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, + .device_resume = ohci_device_resume, + .device_suspend = ohci_device_suspend, + .set_hw_power = ohci_set_hw_power, + .roothub_exec = ohci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/ohci.h b/sys/dev/usb/controller/ohci.h new file mode 100644 index 0000000..84a6afd --- /dev/null +++ b/sys/dev/usb/controller/ohci.h @@ -0,0 +1,366 @@ +/* $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_ + +#define OHCI_MAX_DEVICES USB_MAX_DEVICES + +/* 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_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 usb2_device *sc_devices[OHCI_MAX_DEVICES]; + 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/usb/controller/ohci_atmelarm.c b/sys/dev/usb/controller/ohci_atmelarm.c new file mode 100644 index 0000000..562cf3d --- /dev/null +++ b/sys/dev/usb/controller/ohci_atmelarm.c @@ -0,0 +1,223 @@ +/*- + * 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 + +#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; + + /* initialise some bus fields */ + sc->sc_ohci.sc_bus.parent = dev; + sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; + sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + + /* 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)); + +#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_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, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/ohci_pci.c b/sys/dev/usb/controller/ohci_pci.c new file mode 100644 index 0000000..49591af --- /dev/null +++ b/sys/dev/usb/controller/ohci_pci.c @@ -0,0 +1,387 @@ +/*- + * 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 + +#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; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + + /* 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); + + /* + * Some Sun PCIO-2 USB controllers have their intpin register + * bogusly set to 0, although it should be 4. Correct that. + */ + if (pci_get_devid(self) == 0x1103108e && pci_get_intpin(self) == 0) + pci_set_intpin(self, 4); + + 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)); + } + + /* 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; + + 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_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, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c new file mode 100644 index 0000000..40ae82a --- /dev/null +++ b/sys/dev/usb/controller/uhci.c @@ -0,0 +1,3381 @@ +/*- + * 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 + +#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 void uhci_root_ctrl_poll(struct uhci_softc *); +static void uhci_do_poll(struct usb2_bus *); +static void uhci_device_done(struct usb2_xfer *, usb2_error_t); +static void uhci_transfer_intr_enqueue(struct usb2_xfer *); +static void uhci_root_intr_check(void *); +static void uhci_timeout(void *); +static uint8_t uhci_check_transfer(struct usb2_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); + } +} + +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; +} + +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; +} + +void +uhci_reset(uhci_softc_t *sc) +{ + struct usb2_page_search buf_res; + uint16_t n; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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.bus_mtx, + USB_MS_TO_TICKS(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.bus_mtx, hz / 1000); + + 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.bus_mtx, hz / 1000); + + /* 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); +} + +static void +uhci_start(uhci_softc_t *sc) +{ + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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.bus_mtx, hz / 1000); + + /* 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; + + 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; + + USB_BUS_LOCK(&sc->sc_bus); + /* reset the controller */ + uhci_reset(sc); + + /* start the controller */ + uhci_start(sc); + USB_BUS_UNLOCK(&sc->sc_bus); + + /* 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) +{ + USB_BUS_LOCK(&sc->sc_bus); + +#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.bus_mtx, + USB_MS_TO_TICKS(USB_RESUME_WAIT)); + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +uhci_resume(uhci_softc_t *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + /* reset the controller */ + + uhci_reset(sc); + + /* force global resume */ + + UHCICMD(sc, UHCI_CMD_FGR); + + usb2_pause_mtx(&sc->sc_bus.bus_mtx, + USB_MS_TO_TICKS(USB_RESUME_DELAY)); + + /* and start traffic again */ + + uhci_start(sc); + +#if USB_DEBUG + if (uhcidebug > 2) { + uhci_dumpregs(sc); + } +#endif + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch lost interrupts */ + uhci_do_poll(&sc->sc_bus); +} + +#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)); +} + +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); +} + +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"); +} + +static void +uhci_dump_tds(uhci_td_t *td) +{ + for (; + td != NULL; + td = td->obj_next) { + if (uhci_dump_td(td)) { + break; + } + } +} + +#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); + } +} + +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); + } +} + +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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout); + } +} + +#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,last) (last) = _uhci_append_qh(sqh,last) +static uhci_qh_t * +_uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last) +{ + DPRINTFN(11, "%p to %p\n", sqh, last); + + if (sqh->h_prev != NULL) { + /* should not happen */ + DPRINTFN(0, "QH already linked!\n"); + return (last); + } + /* (sc->sc_bus.mtx) must be locked */ + + 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); + } + 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; +} + +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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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; + } + } +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK(&sc->sc_bus); + + 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 */ + DPRINTF("%s: host controller halted\n", + __FUNCTION__); +#if USB_DEBUG + if (uhcidebug > 0) { + 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: + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/* + * called when a request does not complete + */ +static void +uhci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + uhci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +uhci_do_poll(struct usb2_bus *bus) +{ + struct uhci_softc *sc = UHCI_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + uhci_interrupt_poll(sc); + uhci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; +} + +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->xroot->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->xroot->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 = UHCI_BUS2SC(xfer->xroot->bus); + uhci_qh_t *qh; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); + } + 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->xroot->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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + 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]; + + qh->e_next = td; + qh->qh_e_next = td->td_self; + + if (xfer->xroot->udev->pwr_save.suspended == 0) { + UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); + uhci_add_loop(sc); + xfer->flags_int.bandwidth_reclaimed = 1; + } else { + usb2_pc_cpu_flush(qh->page_cache); + } + + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); +} + +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); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + 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]; + + qh->e_next = td; + qh->qh_e_next = td->td_self; + + /* + * NOTE: some devices choke on bandwidth- reclamation for control + * transfers + */ + if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->speed == USB_SPEED_LOW) { + UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); + } + } else { + usb2_pc_cpu_flush(qh->page_cache); + } + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + 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); +} + +static void +uhci_device_intr_close(struct usb2_xfer *xfer) +{ + uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_intr_stat[xfer->qh_pos]--; + + uhci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + 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]; + + qh->e_next = td; + qh->qh_e_next = td->td_self; + + if (xfer->xroot->udev->pwr_save.suspended == 0) { + + /* enter QHs into the controller data structures */ + UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + + } else { + usb2_pc_cpu_flush(qh->page_cache); + } + + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); +} + +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); + } + } +} + +static void +uhci_device_isoc_close(struct usb2_xfer *xfer) +{ + uhci_device_done(xfer, USB_ERR_CANCELLED); +} + +static void +uhci_device_isoc_enter(struct usb2_xfer *xfer) +{ + struct uhci_mem_layout ml; + uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); + 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); +} + +static void +uhci_device_isoc_start(struct usb2_xfer *xfer) +{ + /* put transfer on interrupt queue */ + uhci_transfer_intr_enqueue(xfer); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + uhci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* 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); + + /* + * Before we do anything, turn on SOF messages on the USB + * BUS. Some USB devices do not cope without them! + */ + if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { + + DPRINTF("Activating SOFs!\n"); + + UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); + + /* wait a little bit */ + if (use_polling) { + DELAY(10000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); + } + } + + 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.bus_mtx, + USB_MS_TO_TICKS(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); + + + mtx_unlock(&sc->sc_bus.bus_mtx); + + /* + * This delay needs to be exactly 100us, else some USB devices + * fail to attach! + */ + DELAY(100); + + mtx_lock(&sc->sc_bus.bus_mtx); + + 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.bus_mtx, + USB_MS_TO_TICKS(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 = UHCI_BUS2SC(xfer->xroot->bus); + + DPRINTF("\n"); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +uhci_root_ctrl_task(struct usb2_bus *bus) +{ + uhci_root_ctrl_poll(UHCI_BUS2SC(bus)); +} + +static void +uhci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); + char *ptr; + uint16_t x; + uint16_t port; + uint16_t value; + uint16_t index; + uint16_t status; + uint16_t change; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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_C_PORT_SUSPEND: + sc->sc_isresumed &= ~(1 << index); + break; + case UHF_PORT_CONNECTION: + case UHF_PORT_OVER_CURRENT: + case UHF_PORT_POWER: + case UHF_PORT_LOW_SPEED: + 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_LSDA) + status |= UPS_LOW_SPEED; + if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD)) { + /* need to do a write back */ + UWRITE2(sc, port, URWMASK(x)); + + /* wait 20ms for resume sequence to complete */ + if (use_polling) { + /* polling */ + DELAY(20000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); + } + + /* clear suspend and resume detect */ + UWRITE2(sc, port, URWMASK(x) & ~(UHCI_PORTSC_RD | + UHCI_PORTSC_SUSP)); + + /* wait a little bit */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 500); + + sc->sc_isresumed |= (1 << index); + + } else if (x & UHCI_PORTSC_SUSP) { + status |= UPS_SUSPEND; + } + status |= UPS_PORT_POWER; + if (sc->sc_isresumed & (1 << index)) + change |= UPS_C_SUSPEND; + 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); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + uhci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = UHCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; + + usb2_transfer_timeout_ms(xfer, + &uhci_root_intr_check, xfer->interval); +} + +static void +uhci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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 = UHCI_BUS2SC(xfer->xroot->bus); + + DPRINTFN(21, "\n"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + + sc->sc_hub_idata[0] = 0; + + if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | + UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { + sc->sc_hub_idata[0] |= 1 << 1; + } + if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | + UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { + sc->sc_hub_idata[0] |= 1 << 2; + } + if (sc->sc_hub_idata[0] == 0) { + /* + * 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); + } +} + +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; + + 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; + } +} + +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; + } + } +} + +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 */ +} + +static void +uhci_device_resume(struct usb2_device *udev) +{ + struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + uhci_qh_t *qh; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (methods == &uhci_device_bulk_methods) { + UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); + uhci_add_loop(sc); + xfer->flags_int.bandwidth_reclaimed = 1; + } + if (methods == &uhci_device_ctrl_methods) { + if (xfer->xroot->udev->speed == USB_SPEED_LOW) { + UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); + } + } + if (methods == &uhci_device_intr_methods) { + UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +uhci_device_suspend(struct usb2_device *udev) +{ + struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + uhci_qh_t *qh; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->xroot->udev == udev) { + + methods = xfer->pipe->methods; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + 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->xroot->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]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +uhci_set_hw_power(struct usb2_bus *bus) +{ + struct uhci_softc *sc = UHCI_BUS2SC(bus); + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + /* + * WARNING: Some FULL speed USB devices require periodic SOF + * messages! If any USB devices are connected through the + * UHCI, power save will be disabled! + */ + if (flags & (USB_HW_POWER_CONTROL | + USB_HW_POWER_NON_ROOT_HUB | + USB_HW_POWER_BULK | + USB_HW_POWER_INTERRUPT | + USB_HW_POWER_ISOC)) { + DPRINTF("Some USB transfer is " + "active on %u.\n", + device_get_unit(sc->sc_bus.bdev)); + UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); + } else { + DPRINTF("Power save on %u.\n", + device_get_unit(sc->sc_bus.bdev)); + UHCICMD(sc, UHCI_CMD_MAXP); + } + + USB_BUS_UNLOCK(bus); + + 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, + .device_resume = uhci_device_resume, + .device_suspend = uhci_device_suspend, + .set_hw_power = uhci_set_hw_power, + .roothub_exec = uhci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/uhci.h b/sys/dev/usb/controller/uhci.h new file mode 100644 index 0000000..9365a4c --- /dev/null +++ b/sys/dev/usb/controller/uhci.h @@ -0,0 +1,321 @@ +/* $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_ + +#define UHCI_MAX_DEVICES USB_MAX_DEVICES + +/* 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 */ + union uhci_hub_desc sc_hub_desc; + struct usb2_sw_transfer sc_root_ctrl; + struct usb2_sw_transfer sc_root_intr; + + struct usb2_device *sc_devices[UHCI_MAX_DEVICES]; + 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; /* bits set if a root hub is reset */ + uint8_t sc_isresumed; /* bits set if a port was resumed */ + 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/usb/controller/uhci_pci.c b/sys/dev/usb/controller/uhci_pci.c new file mode 100644 index 0000000..f7f6f9c --- /dev/null +++ b/sys/dev/usb/controller/uhci_pci.c @@ -0,0 +1,443 @@ +/*- + * 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 + +#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; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = UHCI_MAX_DEVICES; + + /* 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: + /* Quirk for Parallels Desktop 4.0 */ + device_printf(self, "USB revision is unknown. Assuming v1.1.\n"); + sc->sc_bus.usbrev = USB_REV_1_1; + break; + } + +#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; + + 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) { + USB_BUS_LOCK(&sc->sc_bus); + + /* stop the controller */ + uhci_reset(sc); + + USB_BUS_UNLOCK(&sc->sc_bus); + } + 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_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, usb, 1, 1, 1); diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c new file mode 100644 index 0000000..b91be6c --- /dev/null +++ b/sys/dev/usb/controller/usb_controller.c @@ -0,0 +1,620 @@ +/* $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, struct usb2_bus *); +static void usb2_post_init(void *); +static void usb2_bus_mem_flush_all_cb(struct usb2_bus *, + struct usb2_page_cache *, struct usb2_page *, uint32_t, + uint32_t); +static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *, + struct usb2_page_cache *, struct usb2_page *, uint32_t, + uint32_t); +static void usb2_bus_mem_free_all_cb(struct usb2_bus *, + struct usb2_page_cache *, struct usb2_page *, uint32_t, + uint32_t); +static void usb2_bus_roothub(struct usb2_proc_msg *pm); + +/* 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); + +/*------------------------------------------------------------------------* + * 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); + } + + /* delay vfs_mountroot until the bus is explored */ + bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); + + 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); + } + /* Stop power watchdog */ + usb2_callout_drain(&bus->power_wdog); + + /* Let the USB explore process detach all devices. */ + if (bus->bus_roothold != NULL) { + root_mount_rel(bus->bus_roothold); + bus->bus_roothold = NULL; + } + + USB_BUS_LOCK(bus); + 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]); + + USB_BUS_UNLOCK(bus); + + /* Get rid of USB callback processes */ + + usb2_proc_free(&bus->giant_callback_proc); + usb2_proc_free(&bus->non_giant_callback_proc); + + /* Get rid of USB roothub process */ + + usb2_proc_free(&bus->roothub_proc); + + /* Get rid of USB explore process */ + + usb2_proc_free(&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; + } + USB_BUS_UNLOCK(bus); + + mtx_lock(&Giant); + + /* + * First update the USB power state! + */ + usb2_bus_powerd(bus); + + /* + * Explore the Root USB HUB. This call can sleep, + * exiting Giant, which is actually Giant. + */ + (udev->hub->explore) (udev); + + mtx_unlock(&Giant); + + USB_BUS_LOCK(bus); + } + if (bus->bus_roothold != NULL) { + root_mount_rel(bus->bus_roothold); + bus->bus_roothold = NULL; + } +} + +/*------------------------------------------------------------------------* + * 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); + USB_BUS_UNLOCK(bus); + + 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); + USB_BUS_LOCK(bus); + /* clear bdev variable last */ + bus->bdev = NULL; +} + +static void +usb2_power_wdog(void *arg) +{ + struct usb2_bus *bus = arg; + + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + usb2_callout_reset(&bus->power_wdog, + 4 * hz, usb2_power_wdog, arg); + + USB_BUS_UNLOCK(bus); + + usb2_bus_power_update(bus); + + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_attach + * + * This function attaches USB in context of the explore thread. + *------------------------------------------------------------------------*/ +static void +usb2_bus_attach(struct usb2_proc_msg *pm) +{ + struct usb2_bus *bus; + struct usb2_device *child; + device_t dev; + usb2_error_t err; + uint8_t speed; + + bus = ((struct usb2_bus_msg *)pm)->bus; + dev = bus->bdev; + + DPRINTF("\n"); + + 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; + } + + USB_BUS_UNLOCK(bus); + mtx_lock(&Giant); /* XXX not required by USB */ + + /* 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; + } + + mtx_unlock(&Giant); + USB_BUS_LOCK(bus); + + if (err) { + device_printf(bus->bdev, "Root HUB problem, error=%s\n", + usb2_errstr(err)); + } + + /* set softc - we are ready */ + device_set_softc(dev, bus); + + /* start watchdog - this function will unlock the BUS lock ! */ + usb2_power_wdog(bus); + + /* need to return locked */ + USB_BUS_LOCK(bus); +} + +/*------------------------------------------------------------------------* + * usb2_attach_sub + * + * This function creates a thread which runs the USB 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) +{ + const char *pname = device_get_nameunit(dev); + + /* 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; + + bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach; + bus->attach_msg[0].bus = bus; + bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach; + bus->attach_msg[1].bus = bus; + + bus->roothub_msg[0].hdr.pm_callback = &usb2_bus_roothub; + bus->roothub_msg[0].bus = bus; + bus->roothub_msg[1].hdr.pm_callback = &usb2_bus_roothub; + bus->roothub_msg[1].bus = bus; + + /* Create USB explore, roothub and callback processes */ + + if (usb2_proc_create(&bus->giant_callback_proc, + &bus->bus_mtx, pname, USB_PRI_MED)) { + printf("WARNING: Creation of USB Giant " + "callback process failed.\n"); + } else if (usb2_proc_create(&bus->non_giant_callback_proc, + &bus->bus_mtx, pname, USB_PRI_HIGH)) { + printf("WARNING: Creation of USB non-Giant " + "callback process failed.\n"); + } else if (usb2_proc_create(&bus->roothub_proc, + &bus->bus_mtx, pname, USB_PRI_HIGH)) { + printf("WARNING: Creation of USB roothub " + "process failed.\n"); + } else if (usb2_proc_create(&bus->explore_proc, + &bus->bus_mtx, pname, USB_PRI_MED)) { + printf("WARNING: Creation of USB explore " + "process failed.\n"); + } else { + /* Get final attach going */ + USB_BUS_LOCK(bus); + if (usb2_proc_msignal(&bus->explore_proc, + &bus->attach_msg[0], &bus->attach_msg[1])) { + /* ignore */ + } + USB_BUS_UNLOCK(bus); + } +} + +/*------------------------------------------------------------------------* + * 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); +} + +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); +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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; + + mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), + NULL, MTX_DEF | MTX_RECURSE); + + usb2_callout_init_mtx(&bus->power_wdog, + &bus->bus_mtx, CALLOUT_RETURNUNLOCKED); + + TAILQ_INIT(&bus->intr_q.head); + + usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, + dmat, &bus->bus_mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX); + + if ((bus->devices_max > USB_MAX_DEVICES) || + (bus->devices_max < USB_MIN_DEVICES) || + (bus->devices == NULL)) { + DPRINTFN(0, "Devices field has not been " + "initialised properly!\n"); + bus->alloc_failed = 1; /* failure */ + } + 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); +} + +/*------------------------------------------------------------------------* + * 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->bus_mtx); +} + +/*------------------------------------------------------------------------* + * usb2_bus_roothub + * + * This function is used to execute roothub control requests on the + * roothub and is called from the roothub process. + *------------------------------------------------------------------------*/ +static void +usb2_bus_roothub(struct usb2_proc_msg *pm) +{ + struct usb2_bus *bus; + + bus = ((struct usb2_bus_msg *)pm)->bus; + + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + (bus->methods->roothub_exec) (bus); +} + +/*------------------------------------------------------------------------* + * usb2_bus_roothub_exec + * + * This function is used to schedule the "roothub_done" bus callback + * method. The bus lock must be locked when calling this function. + *------------------------------------------------------------------------*/ +void +usb2_bus_roothub_exec(struct usb2_bus *bus) +{ + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + if (usb2_proc_msignal(&bus->roothub_proc, + &bus->roothub_msg[0], &bus->roothub_msg[1])) { + /* ignore */ + } +} diff --git a/sys/dev/usb/controller/uss820dci.c b/sys/dev/usb/controller/uss820dci.c new file mode 100644 index 0000000..2adc4e3 --- /dev/null +++ b/sys/dev/usb/controller/uss820dci.c @@ -0,0 +1,2489 @@ +/* $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 + +#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 *, usb2_error_t); +static void uss820dci_do_poll(struct usb2_bus *); +static void uss820dci_root_ctrl_poll(struct uss820dci_softc *); +static void uss820dci_standard_done(struct usb2_xfer *); +static void uss820dci_intr_set(struct usb2_xfer *, uint8_t); +static void uss820dci_update_shared_1(struct uss820dci_softc *, uint8_t, + uint8_t, uint8_t); + +static usb2_sw_transfer_func_t uss820dci_root_intr_done; +static usb2_sw_transfer_func_t uss820dci_root_ctrl_done; + +/* + * 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); +} + +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; + } +} + +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); + } +} + +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); + } +} + +static void +uss820dci_wakeup_peer(struct uss820dci_softc *sc) +{ + if (!(sc->sc_flags.status_suspend)) { + return; + } + DPRINTFN(0, "not supported\n"); +} + +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); +} + +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; + } + } +} + +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); +} + +void +uss820dci_interrupt(struct uss820dci_softc *sc) +{ + uint8_t ssr; + uint8_t event; + + USB_BUS_LOCK(&sc->sc_bus); + + 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); + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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; +} + +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->xroot->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 = USS820_DCI_BUS2SC(xfer->xroot->bus); + 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; +} + +static void +uss820dci_timeout(void *arg) +{ + struct usb2_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + uss820dci_device_done(xfer, USB_ERR_TIMEOUT); +} + +static void +uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); + 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); +} + +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->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usb2_transfer_timeout_ms(xfer, + &uss820dci_timeout, xfer->timeout); + } + } +} + +static void +uss820dci_root_intr_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); + + DPRINTFN(9, "\n"); + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); +} + +/*------------------------------------------------------------------------* + * 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) +{ + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); +} + +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; + + USB_BUS_LOCK_ASSERT(udev->bus, 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); +} + +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); + } +} + +static void +uss820dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) +{ + struct uss820dci_softc *sc; + struct usb2_endpoint_descriptor *ed; + + USB_BUS_LOCK_ASSERT(udev->bus, 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))); +} + +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; + + USB_BUS_LOCK(&sc->sc_bus); + + /* 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) { + USB_BUS_UNLOCK(&sc->sc_bus); + 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.bus_mtx, hz / 100); + + /* check hardware revision */ + temp = USS820_READ_1(sc, USS820_REV); + + if (temp < 0x13) { + USB_BUS_UNLOCK(&sc->sc_bus); + return (USB_ERR_INVAL); + } + /* enable interrupts */ + USS820_WRITE_1(sc, USS820_SCR, + USS820_SCR_T_IRQ | + USS820_SCR_IE_RESET | + /* USS820_SCR_RWUPE | */ + 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); + } + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch any lost interrupts */ + + uss820dci_do_poll(&sc->sc_bus); + + return (0); /* success */ +} + +void +uss820dci_uninit(struct uss820dci_softc *sc) +{ + uint8_t temp; + + USB_BUS_LOCK(&sc->sc_bus); + + /* 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); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +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); + + USB_BUS_LOCK(&sc->sc_bus); + uss820dci_interrupt_poll(sc); + uss820dci_root_ctrl_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/*------------------------------------------------------------------------* + * 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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void +uss820dci_device_isoc_fs_enter(struct usb2_xfer *xfer) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); + 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); +} + +static void +uss820dci_device_isoc_fs_start(struct usb2_xfer *xfer) +{ + /* start TD chain */ + uss820dci_start_standard_chain(xfer); +} + +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 = USS820_DCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_ctrl.xfer == xfer) { + sc->sc_root_ctrl.xfer = NULL; + } + uss820dci_device_done(xfer, USB_ERR_CANCELLED); +} + +/* + * 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) >> 8, + .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 = USS820_DCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_ctrl.xfer = xfer; + + usb2_bus_roothub_exec(xfer->xroot->bus); +} + +static void +uss820dci_root_ctrl_task(struct usb2_bus *bus) +{ + uss820dci_root_ctrl_poll(USS820_DCI_BUS2SC(bus)); +} + +static void +uss820dci_root_ctrl_done(struct usb2_xfer *xfer, + struct usb2_sw_transfer *std) +{ + struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); + uint16_t value; + uint16_t index; + uint8_t use_polling; + + USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); +} + +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 = USS820_DCI_BUS2SC(xfer->xroot->bus); + + if (sc->sc_root_intr.xfer == xfer) { + sc->sc_root_intr.xfer = NULL; + } + uss820dci_device_done(xfer, USB_ERR_CANCELLED); +} + +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 = USS820_DCI_BUS2SC(xfer->xroot->bus); + + sc->sc_root_intr.xfer = xfer; +} + +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; + + /* + * 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; +} + +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; + } + } +} + +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, + .roothub_exec = &uss820dci_root_ctrl_task, +}; diff --git a/sys/dev/usb/controller/uss820dci.h b/sys/dev/usb/controller/uss820dci.h new file mode 100644 index 0000000..f99e2d5 --- /dev/null +++ b/sys/dev/usb/controller/uss820dci.h @@ -0,0 +1,377 @@ +/* $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_MAX_DEVICES (USB_MIN_DEVICES + 1) + +#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_device *sc_devices[USS820_MAX_DEVICES]; + 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/usb/controller/uss820dci_atmelarm.c b/sys/dev/usb/controller/uss820dci_atmelarm.c new file mode 100644 index 0000000..ddbffd7 --- /dev/null +++ b/sys/dev/usb/controller/uss820dci_atmelarm.c @@ -0,0 +1,238 @@ +#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 + +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, usb, 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; + + /* initialise some bus fields */ + sc->sc_bus.parent = dev; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = USS820_MAX_DEVICES; + + /* 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); + +#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_bus_mem_free_all(&sc->sc_bus, NULL); + + return (0); +} diff --git a/sys/dev/usb/image/uscanner.c b/sys/dev/usb/image/uscanner.c new file mode 100644 index 0000000..ca631b5 --- /dev/null +++ b/sys/dev/usb/image/uscanner.c @@ -0,0 +1,643 @@ +/* $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 "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR uscanner_debug + +#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, debug, CTLFLAG_RW, &uscanner_debug, + 0, "uscanner debug level"); +#endif + +/* + * uscanner transfers macros definition. + */ +#define USCANNER_BSIZE (1 << 15) +#define USCANNER_IFQ_MAXLEN 2 + +/* + * 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 + +enum { + USCANNER_BULK_DT_WR, + USCANNER_BULK_DT_RD, + USCANNER_BULK_CS_WR, + USCANNER_BULK_CS_RD, + USCANNER_N_TRANSFER = 4, +}; + +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] = { + [USCANNER_BULK_DT_WR] = { + .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, + }, + + [USCANNER_BULK_DT_RD] = { + .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, + }, + + [USCANNER_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 = &uscanner_write_clear_stall_callback, + .mh.timeout = 1000, + .mh.interval = 50, /* 50ms */ + }, + + [USCANNER_BULK_CS_RD] = { + .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, usb, 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_4470C, 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[USCANNER_BULK_CS_RD]); + 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[USCANNER_BULK_CS_RD]); + } + break; + } +} + +/* + * 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[USCANNER_BULK_DT_RD]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } +} + +/* + * 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[USCANNER_BULK_CS_WR]); + 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[USCANNER_BULK_CS_WR]); + } + break; + } +} + +/* + * 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[USCANNER_BULK_DT_WR]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; + usb2_transfer_start(xfer_other); + } +} + +/* + * 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[USCANNER_BULK_DT_RD]->max_data_length, + USCANNER_IFQ_MAXLEN)) { + return (ENOMEM); + } + } + if (fflags & FWRITE) { + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[USCANNER_BULK_DT_WR]->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); + } +} + +/* + * 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[USCANNER_BULK_DT_RD]); +} + +/* + * 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[USCANNER_BULK_DT_WR]); +} + +/* + * 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[USCANNER_BULK_CS_RD]); + usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_RD]); +} + +/* + * 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[USCANNER_BULK_CS_WR]); + usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_WR]); +} diff --git a/sys/dev/usb/input/uhid.c b/sys/dev/usb/input/uhid.c new file mode 100644 index 0000000..563bd99 --- /dev/null +++ b/sys/dev/usb/input/uhid.c @@ -0,0 +1,789 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uhid_debug + +#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_BSIZE 1024 /* bytes, buffer size */ +#define UHID_FRAME_NUM 50 /* bytes, frame number */ + +enum { + UHID_INTR_DT_RD, + UHID_CTRL_DT_WR, + UHID_CTRL_DT_RD, + UHID_N_TRANSFER, +}; + +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_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_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: +re_submit: + if (usb2_fifo_put_bytes_max( + sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { + xfer->frlengths[0] = sc->sc_isize; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto re_submit; + } + 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); +} + +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); +} + +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] = { + + [UHID_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = UHID_BSIZE, + .mh.callback = &uhid_intr_callback, + }, + + [UHID_CTRL_DT_WR] = { + .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 */ + }, + + [UHID_CTRL_DT_RD] = { + .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[UHID_CTRL_DT_RD]); + } else { + usb2_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]); + } +} + +static void +uhid_stop_read(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]); +} + +static void +uhid_start_write(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]); +} + +static void +uhid_stop_write(struct usb2_fifo *fifo) +{ + struct uhid_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]); +} + +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); + } +} + +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; + if (ugd->ugd_data == NULL) + break; /* descriptor length only */ + 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); + + 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, usb, 1, 1, 1); diff --git a/sys/dev/usb/input/ukbd.c b/sys/dev/usb/input/ukbd.c new file mode 100644 index 0000000..c8350264 --- /dev/null +++ b/sys/dev/usb/input/ukbd.c @@ -0,0 +1,1489 @@ +#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 + +/* 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_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; + +enum { + UKBD_INTR_DT, + UKBD_INTR_CS, + UKBD_CTRL_LED, + UKBD_N_TRANSFER = 3, +}; + +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 *); +static void ukbd_set_leds(struct ukbd_softc *, uint8_t); +static int ukbd_set_typematic(keyboard_t *, int); +#ifdef UKBD_EMULATE_ATSCANCODE +static int ukbd_key2scan(struct ukbd_softc *, int, int, int); +#endif +static uint32_t ukbd_read_char(keyboard_t *, int); +static void ukbd_clear_state(keyboard_t *); +static int ukbd_ioctl(keyboard_t *, u_long, caddr_t); +static int ukbd_enable(keyboard_t *); +static int ukbd_disable(keyboard_t *); +static void ukbd_interrupt(struct ukbd_softc *); + +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"); + } +} + +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[UKBD_INTR_DT]); + } + 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); +} + +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[UKBD_INTR_DT]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } +} + +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[UKBD_INTR_CS]); + 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[UKBD_INTR_CS]); + } + 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] = { + + [UKBD_INTR_DT] = { + .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, + }, + + [UKBD_INTR_CS] = { + .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 */ + }, + + [UKBD_CTRL_LED] = { + .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; + + 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, 0); + + 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[UKBD_INTR_DT]); + + /* start the timer */ + + ukbd_timeout(sc); + mtx_unlock(&Giant); + 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)); +} + +/* 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[UKBD_CTRL_LED]); +} + +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, usb, 1, 1, 1); diff --git a/sys/dev/usb/input/ums.c b/sys/dev/usb/input/ums.c new file mode 100644 index 0000000..b064aa9 --- /dev/null +++ b/sys/dev/usb/input/ums.c @@ -0,0 +1,901 @@ +/*- + * 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 "usbdevs.h" +#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 + +#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_BUTTON_MAX 31 /* exclusive, must be less than 32 */ +#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) + +enum { + UMS_INTR_DT, + UMS_INTR_CS, + UMS_N_TRANSFER = 2, +}; + +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); +} + +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[UMS_INTR_DT]; + + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UMS_FLAG_INTR_STALL; + usb2_transfer_start(xfer_other); + } +} + +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[UMS_INTR_CS]); + } 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[UMS_INTR_CS]); + } + return; + } +} + +static const struct usb2_config ums_config[UMS_N_TRANSFER] = { + + [UMS_INTR_DT] = { + .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, + }, + + [UMS_INTR_CS] = { + .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, 0); + + /* + * 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[UMS_INTR_DT]->max_frame_size) { + DPRINTF("WARNING: report size, %d bytes, is larger " + "than interrupt size, %d bytes!\n", + isize, sc->sc_xfer[UMS_INTR_DT]->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[UMS_INTR_DT]); +} + +static void +ums_stop_read(struct usb2_fifo *fifo) +{ + struct ums_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[UMS_INTR_CS]); + usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); + usb2_callout_stop(&sc->sc_callout); +} + + +#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"); + } +} + +static void +ums_reset_buf(struct ums_softc *sc) +{ + /* reset read queue */ + usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); +} + +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); + } +} + +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, usb, 1, 1, 1); diff --git a/sys/dev/usb/input/usb_rdesc.h b/sys/dev/usb/input/usb_rdesc.h new file mode 100644 index 0000000..9f4363d --- /dev/null +++ b/sys/dev/usb/input/usb_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/usb/misc/udbp.c b/sys/dev/usb/misc/udbp.c new file mode 100644 index 0000000..b519058 --- /dev/null +++ b/sys/dev/usb/misc/udbp.c @@ -0,0 +1,853 @@ +/*- + * 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 "usbdevs.h" +#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, hook_p, void *, int); + +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, usb, 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; + + 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); + } +} + +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); +} + +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); + } +} + +/*********************************************************************** + * 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/usb/misc/udbp.h b/sys/dev/usb/misc/udbp.h new file mode 100644 index 0000000..e6fd853 --- /dev/null +++ b/sys/dev/usb/misc/udbp.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file was derived from src/sys/netgraph/ng_sample.h, revision 1.1 + * written by Julian Elischer, Whistle Communications. + * + * $FreeBSD$ + */ + +#ifndef _NETGRAPH_UDBP_H_ +#define _NETGRAPH_UDBP_H_ + +/* Node type name. This should be unique among all netgraph node types */ +#define NG_UDBP_NODE_TYPE "udbp" + +/* Node type cookie. Should also be unique. This value MUST change whenever + an incompatible change is made to this header file, to insure consistency. + The de facto method for generating cookies is to take the output of the + date command: date -u +'%s' */ +#define NGM_UDBP_COOKIE 944609300 + + +#define NG_UDBP_HOOK_NAME "data" + +/* Netgraph commands understood by this node type */ +enum { + NGM_UDBP_SET_FLAG = 1, + NGM_UDBP_GET_STATUS, +}; + +/* This structure is returned by the NGM_UDBP_GET_STATUS command */ +struct ngudbpstat { + 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/usb/misc/ufm.c b/sys/dev/usb/misc/ufm.c new file mode 100644 index 0000000..81ce1b3 --- /dev/null +++ b/sys/dev/usb/misc/ufm.c @@ -0,0 +1,329 @@ +/*- + * 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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#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 *, uint8_t, uint16_t, uint16_t, + uint8_t *); +static int ufm_set_freq(struct ufm_softc *, void *); +static int ufm_get_freq(struct ufm_softc *, void *); +static int ufm_start(struct ufm_softc *, void *); +static int ufm_stop(struct ufm_softc *, void *); +static int ufm_get_stat(struct ufm_softc *, void *); + +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), +}; + +DRIVER_MODULE(ufm, ushub, ufm_driver, ufm_devclass, NULL, 0); +MODULE_DEPEND(ufm, usb, 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; + + 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. + */ + usb2_pause_mtx(NULL, hz / 4); + + 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/usb/net/if_aue.c b/sys/dev/usb/net/if_aue.c new file mode 100644 index 0000000..026fa7c --- /dev/null +++ b/sys/dev/usb/net/if_aue.c @@ -0,0 +1,1054 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Copyright (c) 2006 + * Alfred Perlstein . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must 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 + * + * SMP locking by Alfred Perlstein . + * RED Inc. + */ + +/* + * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet + * support: the control endpoint for reading/writing registers, burst + * read endpoint for packet reception, burst write for packet transmission + * and one for "interrupts." The chip uses the same RX filter scheme + * as the other ADMtek ethernet parts: one perfect filter entry for the + * the station address and a 64-bit multicast hash table. The chip supports + * both MII and HomePNA attachments. + * + * Since the maximum data transfer speed of USB is supposed to be 12Mbps, + * you're never really going to get 100Mbps speeds from this device. I + * think the idea is to allow the device to connect to 10 or 100Mbps + * networks, not necessarily to provide 100Mbps performance. Also, since + * the controller uses an external PHY chip, it's possible that board + * designers might simply choose a 10Mbps PHY. + * + * Registers are accessed using usb2_ether_do_request(). Packet + * transfers are done using usb2_transfer() and friends. + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR aue_debug + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 miibus_readreg_t aue_miibus_readreg; +static miibus_writereg_t aue_miibus_writereg; +static miibus_statchg_t aue_miibus_statchg; + +static usb2_callback_t aue_intr_callback; +static usb2_callback_t aue_bulk_read_callback; +static usb2_callback_t aue_bulk_write_callback; + +static usb2_ether_fn_t aue_attach_post; +static usb2_ether_fn_t aue_init; +static usb2_ether_fn_t aue_stop; +static usb2_ether_fn_t aue_start; +static usb2_ether_fn_t aue_tick; +static usb2_ether_fn_t aue_setmulti; +static usb2_ether_fn_t aue_setpromisc; + +static uint8_t aue_csr_read_1(struct aue_softc *, uint16_t); +static uint16_t aue_csr_read_2(struct aue_softc *, uint16_t); +static void aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t); +static void aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t); +static void aue_eeprom_getword(struct aue_softc *, int, uint16_t *); +static void aue_read_eeprom(struct aue_softc *, uint8_t *, uint16_t, + uint16_t); +static void aue_reset(struct aue_softc *); +static void aue_reset_pegasus_II(struct aue_softc *); + +static int aue_ifmedia_upd(struct ifnet *); +static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static const struct usb2_config aue_config[AUE_N_TRANSFER] = { + + [AUE_BULK_DT_WR] = { + .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 */ + }, + + [AUE_BULK_DT_RD] = { + .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, + }, + + [AUE_INTR_DT_RD] = { + .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, + }, +}; + +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_miibus_readreg), + DEVMETHOD(miibus_writereg, aue_miibus_writereg), + DEVMETHOD(miibus_statchg, aue_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); +MODULE_DEPEND(aue, uether, 1, 1, 1); +MODULE_DEPEND(aue, usb, 1, 1, 1); +MODULE_DEPEND(aue, ether, 1, 1, 1); +MODULE_DEPEND(aue, miibus, 1, 1, 1); + +static const struct usb2_ether_methods aue_ue_methods = { + .ue_attach_post = aue_attach_post, + .ue_start = aue_start, + .ue_init = aue_init, + .ue_stop = aue_stop, + .ue_tick = aue_tick, + .ue_setmulti = aue_setmulti, + .ue_setpromisc = aue_setpromisc, + .ue_mii_upd = aue_ifmedia_upd, + .ue_mii_sts = aue_ifmedia_sts, +}; + +#define AUE_SETBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x)) + +#define AUE_CLRBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +aue_csr_read_1(struct aue_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + usb2_error_t err; + 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); + + err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); + if (err) + return (0); + return (val); +} + +static uint16_t +aue_csr_read_2(struct aue_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + usb2_error_t err; + 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); + + err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); + if (err) + return (0); + return (le16toh(val)); +} + +static void +aue_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); + + if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* error ignored */ + } +} + +static void +aue_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); + + if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* error ignored */ + } +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void +aue_eeprom_getword(struct aue_softc *sc, int addr, uint16_t *dest) +{ + int i; + uint16_t word = 0; + + aue_csr_write_1(sc, AUE_EE_REG, addr); + aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) + break; + if (usb2_ether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n"); + + word = aue_csr_read_2(sc, AUE_EE_DATA); + *dest = word; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void +aue_read_eeprom(struct aue_softc *sc, uint8_t *dest, + uint16_t off, uint16_t len) +{ + uint16_t *ptr = (uint16_t *)dest; + int i; + + for (i = 0; i != len; i++, ptr++) + aue_eeprom_getword(sc, off + i, ptr); +} + +static int +aue_miibus_readreg(device_t dev, int phy, int reg) +{ + struct aue_softc *sc = device_get_softc(dev); + int i, locked; + uint16_t val = 0; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + /* + * 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) + goto done; +#if 0 + if (phy != 1) + goto done; +#endif + } + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + if (usb2_ether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); + + val = aue_csr_read_2(sc, AUE_PHY_DATA); + +done: + if (!locked) + AUE_UNLOCK(sc); + return (val); +} + +static int +aue_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct aue_softc *sc = device_get_softc(dev); + int i; + int locked; + + if (phy == 3) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + aue_csr_write_2(sc, AUE_PHY_DATA, data); + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + if (usb2_ether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); + + if (!locked) + AUE_UNLOCK(sc); + return (0); +} + +static void +aue_miibus_statchg(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + int locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + else + AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + else + AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); + + /* + * Set the LED modes on the LinkSys adapter. + * This turns on the 'dual link LED' bin in the auxmode + * register of the Broadcom PHY. + */ + if (sc->sc_flags & AUE_FLAG_LSYS) { + uint16_t auxmode; + + auxmode = aue_miibus_readreg(dev, 0, 0x1b); + aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); + } + if (!locked) + AUE_UNLOCK(sc); +} + +#define AUE_BITS 6 +static void +aue_setmulti(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0; + uint32_t i; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + return; + } + + AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + + /* now program new ones */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1); + hashtbl[(h >> 3)] |= 1 << (h & 0x7); + } + IF_ADDR_UNLOCK(ifp); + + /* write the hashtable */ + for (i = 0; i != 8; i++) + aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]); +} + +static void +aue_reset_pegasus_II(struct aue_softc *sc) +{ + /* Magic constants taken from Linux driver. */ + aue_csr_write_1(sc, AUE_REG_1D, 0); + aue_csr_write_1(sc, AUE_REG_7B, 2); +#if 0 + if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) + aue_csr_write_1(sc, AUE_REG_81, 6); + else +#endif + aue_csr_write_1(sc, AUE_REG_81, 2); +} + +static void +aue_reset(struct aue_softc *sc) +{ + int i; + + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) + break; + if (usb2_ether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "reset failed\n"); + + /* + * The PHY(s) attached to the Pegasus chip may be held + * in reset until we flip on the GPIO outputs. Make sure + * to set the GPIO pins high so that the PHY(s) will + * be enabled. + * + * Note: We force all of the GPIO pins low first, *then* + * enable the ones we want. + */ + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0); + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1); + + if (sc->sc_flags & AUE_FLAG_LSYS) { + /* Grrr. LinkSys has to be different from everyone else. */ + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1); + aue_csr_write_1(sc, AUE_GPIO0, + AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0); + } + if (sc->sc_flags & AUE_FLAG_PII) + aue_reset_pegasus_II(sc); + + /* Wait a little while for the chip to get its brains in order: */ + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +static void +aue_attach_post(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + + /* reset the adapter */ + aue_reset(sc); + + /* get station address from the EEPROM */ + aue_read_eeprom(sc, ue->ue_eaddr, 0, 3); +} + +/* + * 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); + /* + * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict + * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of + * the devices that look like Bluetooth adapters. + */ + if (uaa->info.idVendor == USB_VENDOR_BELKIN && + uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 && + uaa->info.bcdDevice == 0x0413) + 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); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + if (uaa->info.bcdDevice >= 0x0201) { + /* XXX currently undocumented */ + sc->sc_flags |= AUE_FLAG_VER_2; + } + + device_set_usb2_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = AUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, aue_config, AUE_N_TRANSFER, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &aue_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + aue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +aue_detach(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +aue_intr_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct aue_intrpkt pkt; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if ((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++; + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +aue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + struct aue_rxpkt stat; + + 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 <= (sizeof(stat) + ETHER_CRC_LEN)) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, + xfer->actlen - sizeof(stat), &stat, sizeof(stat)); + + /* + * turn off all the non-error bits in the rx status + * word: + */ + stat.aue_rxstat &= AUE_RXSTAT_MASK; + if (stat.aue_rxstat) { + ifp->if_ierrors++; + goto tr_setup; + } + /* No errors; receive the packet. */ + xfer->actlen -= (sizeof(stat) + ETHER_CRC_LEN); + } + usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + return; + } +} + +static void +aue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct aue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + 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++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & AUE_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + 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); + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +aue_tick(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & AUE_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= AUE_FLAG_LINK; + aue_start(ue); + } +} + +static void +aue_start(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]); + usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]); +} + +static void +aue_init(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + int i; + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + aue_reset(sc); + + /* Set MAC address */ + for (i = 0; i != ETHER_ADDR_LEN; i++) + aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]); + + /* update promiscuous setting */ + aue_setpromisc(ue); + + /* Load the multicast filter. */ + aue_setmulti(ue); + + /* Enable RX and TX */ + aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB); + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); + AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); + + usb2_transfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + aue_start(ue); +} + +static void +aue_setpromisc(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + /* if we want promiscuous mode, set the allframes bit: */ + if (ifp->if_flags & IFF_PROMISC) + AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); + else + AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); +} + +/* + * Set media options. + */ +static int +aue_ifmedia_upd(struct ifnet *ifp) +{ + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~AUE_FLAG_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 (0); +} + +/* + * Report current media status. + */ +static void +aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + AUE_LOCK(sc); + mii_pollstat(mii); + AUE_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +aue_stop(struct usb2_ether *ue) +{ + struct aue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~AUE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]); + + aue_csr_write_1(sc, AUE_CTL0, 0); + aue_csr_write_1(sc, AUE_CTL1, 0); + aue_reset(sc); +} + +/* + * 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); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_auereg.h b/sys/dev/usb/net/if_auereg.h new file mode 100644 index 0000000..249c913 --- /dev/null +++ b/sys/dev/usb/net/if_auereg.h @@ -0,0 +1,220 @@ +/*- + * 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. + */ +enum { + AUE_BULK_DT_WR, + AUE_BULK_DT_RD, + AUE_INTR_DT_RD, + AUE_N_TRANSFER, +}; + +#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) usb2_ether_getmii(&(sc)->sc_ue) + +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; + uint8_t pad; +} __packed; + +struct aue_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[AUE_N_TRANSFER]; + + int 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_LINK 0x0008 /* wait for link to come up */ +#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */ +#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */ +}; + +#define AUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/if_axe.c b/sys/dev/usb/net/if_axe.c new file mode 100644 index 0000000..0555e69 --- /dev/null +++ b/sys/dev/usb/net/if_axe.c @@ -0,0 +1,1076 @@ +/*- + * 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 + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR axe_debug + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * AXE_178_MAX_FRAME_BURST + * max frame burst size for Ax88178 and Ax88772 + * 0 2048 bytes + * 1 4096 bytes + * 2 8192 bytes + * 3 16384 bytes + * use the largest your system can handle without USB stalling. + * + * NB: 88772 parts appear to generate lots of input errors with + * a 2K rx buffer and 8K is only slightly faster than 4K on an + * EHCI port on a T42 so change at your own risk. + */ +#define AXE_178_MAX_FRAME_BURST 1 + +#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_callback; +static usb2_callback_t axe_bulk_read_callback; +static usb2_callback_t axe_bulk_write_callback; + +static miibus_readreg_t axe_miibus_readreg; +static miibus_writereg_t axe_miibus_writereg; +static miibus_statchg_t axe_miibus_statchg; + +static usb2_ether_fn_t axe_attach_post; +static usb2_ether_fn_t axe_init; +static usb2_ether_fn_t axe_stop; +static usb2_ether_fn_t axe_start; +static usb2_ether_fn_t axe_tick; +static usb2_ether_fn_t axe_setmulti; +static usb2_ether_fn_t axe_setpromisc; + +static int axe_ifmedia_upd(struct ifnet *); +static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int axe_cmd(struct axe_softc *, int, int, int, void *); +static void axe_ax88178_init(struct axe_softc *); +static void axe_ax88772_init(struct axe_softc *); +static int axe_get_phyno(struct axe_softc *, int); + +static const struct usb2_config axe_config[AXE_N_TRANSFER] = { + + [AXE_BULK_DT_WR] = { + .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 */ + }, + + [AXE_BULK_DT_RD] = { + .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 */ + }, + + [AXE_INTR_DT_RD] = { + .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, + }, +}; + +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_miibus_readreg), + DEVMETHOD(miibus_writereg, axe_miibus_writereg), + DEVMETHOD(miibus_statchg, axe_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); +MODULE_DEPEND(axe, uether, 1, 1, 1); +MODULE_DEPEND(axe, usb, 1, 1, 1); +MODULE_DEPEND(axe, ether, 1, 1, 1); +MODULE_DEPEND(axe, miibus, 1, 1, 1); + +static const struct usb2_ether_methods axe_ue_methods = { + .ue_attach_post = axe_attach_post, + .ue_start = axe_start, + .ue_init = axe_init, + .ue_stop = axe_stop, + .ue_tick = axe_tick, + .ue_setmulti = axe_setmulti, + .ue_setpromisc = axe_setpromisc, + .ue_mii_upd = axe_ifmedia_upd, + .ue_mii_sts = axe_ifmedia_sts, +}; + +static int +axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) +{ + struct usb2_device_request req; + usb2_error_t err; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + 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, AXE_CMD_LEN(cmd)); + + err = usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000); + + return (err); +} + +static int +axe_miibus_readreg(device_t dev, int phy, int reg) +{ + struct axe_softc *sc = device_get_softc(dev); + uint16_t val; + int locked; + + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); + axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + val = le16toh(val); + if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) { + /* + * BMSR of AX88772 indicates that it supports extended + * capability but the extended status register is + * revered for embedded ethernet PHY. So clear the + * extended capability bit of BMSR. + */ + val &= ~BMSR_EXTCAP; + } + + if (!locked) + AXE_UNLOCK(sc); + return (val); +} + +static int +axe_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct axe_softc *sc = device_get_softc(dev); + int locked; + + val = htole16(val); + + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); + axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + if (!locked) + AXE_UNLOCK(sc); + return (0); +} + +static void +axe_miibus_statchg(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + struct ifnet *ifp; + uint16_t val; + int err, locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + ifp = usb2_ether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + sc->sc_flags &= ~AXE_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= AXE_FLAG_LINK; + break; + case IFM_1000_T: + if ((sc->sc_flags & AXE_FLAG_178) == 0) + break; + sc->sc_flags |= AXE_FLAG_LINK; + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) + goto done; + + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) + val |= AXE_MEDIA_FULL_DUPLEX; + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { + val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; + if ((sc->sc_flags & AXE_FLAG_178) != 0) + val |= AXE_178_MEDIA_ENCK; + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; + break; + case IFM_100_TX: + val |= AXE_178_MEDIA_100TX; + break; + case IFM_10_T: + /* doesn't need to be handled */ + break; + } + } + err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); + if (err) + device_printf(dev, "media change failed, error %d\n", err); +done: + if (!locked) + AXE_UNLOCK(sc); +} + +/* + * Set media options. + */ +static int +axe_ifmedia_upd(struct ifnet *ifp) +{ + struct axe_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + int error; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct axe_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + AXE_LOCK(sc); + mii_pollstat(mii); + AXE_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static void +axe_setmulti(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0; + uint16_t rxmode; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + rxmode = le16toh(rxmode); + + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + rxmode |= AXE_RXCMD_ALLMULTI; + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + return; + } + rxmode &= ~AXE_RXCMD_ALLMULTI; + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + IF_ADDR_UNLOCK(ifp); + + axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); +} + +static int +axe_get_phyno(struct axe_softc *sc, int sel) +{ + int phyno; + + switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) { + case PHY_TYPE_100_HOME: + case PHY_TYPE_GIG: + phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]); + break; + case PHY_TYPE_SPECIAL: + /* FALLTHROUGH */ + case PHY_TYPE_RSVD: + /* FALLTHROUGH */ + case PHY_TYPE_NON_SUP: + /* FALLTHROUGH */ + default: + phyno = -1; + break; + } + + return (phyno); +} + +static void +axe_ax88178_init(struct axe_softc *sc) +{ + int gpio0 = 0, phymode = 0; + uint16_t eeprom; + + axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); + /* XXX magic */ + axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); + eeprom = le16toh(eeprom); + axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); + + /* if EEPROM is invalid we have to use to GPIO0 */ + if (eeprom == 0xffff) { + phymode = 0; + gpio0 = 1; + } else { + phymode = eeprom & 7; + gpio0 = (eeprom & 0x80) ? 0 : 1; + } + + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 16); + + if ((eeprom >> 8) != 0x01) { + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 32); + + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 3); + + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 32); + } else { + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 32); + + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 32); + } + + /* soft reset */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 4); + + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 4); + /* Enable MII/GMII/RGMII interface to work with external PHY. */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 4); + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_ax88772_init(struct axe_softc *sc) +{ + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 16); + + if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { + /* ask for the embedded PHY */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 64); + + /* power down and reset state, pin reset state */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_CLEAR, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 16); + + /* power down/reset state, pin operating state */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 4); + + /* power up, reset */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); + + /* power up, operating */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); + } else { + /* ask for external PHY */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); + usb2_ether_pause(&sc->sc_ue, hz / 64); + + /* power down internal PHY */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + } + + usb2_ether_pause(&sc->sc_ue, hz / 4); + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_reset(struct axe_softc *sc) +{ + struct usb2_config_descriptor *cd; + usb2_error_t err; + + cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usb2_req_set_config(sc->sc_ue.ue_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. */ + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +static void +axe_attach_post(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + + /* + * Load PHY indexes first. Needed by axe_xxx_init(). + */ + axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); +#if 1 + device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n", + sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); +#endif + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); + if (sc->sc_phyno == -1) + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); + if (sc->sc_phyno == -1) { + device_printf(sc->sc_ue.ue_dev, + "no valid PHY address found, assuming PHY address 0\n"); + sc->sc_phyno = 0; + } + + if (sc->sc_flags & AXE_FLAG_178) + axe_ax88178_init(sc); + else if (sc->sc_flags & AXE_FLAG_772) + axe_ax88772_init(sc); + + /* + * Get station address. + */ + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + else + axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + + /* + * Fetch IPG values. + */ + axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); +} + +/* + * 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); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = AXE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &axe_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + axe_detach(dev); + return (ENXIO); /* failure */ +} + +static int +axe_detach(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +axe_intr_callback(struct usb2_xfer *xfer) +{ + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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 usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + struct axe_sframe_hdr hdr; + int error, pos, len, 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; + } + error = usb2_ether_rxbuf(ue, xfer->frbuffers, pos, len); + if (error) + break; + + 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++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + 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 = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + int pos; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_opackets++; + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + pos = 0; + + while (1) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) { + if (pos > 0) + break; /* send out data */ + return; + } + 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); + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + + } +} + +static void +axe_tick(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { + axe_miibus_statchg(ue->ue_dev); + if ((sc->sc_flags & AXE_FLAG_LINK) != 0) + axe_start(ue); + } +} + +static void +axe_start(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[AXE_INTR_DT_RD]); + usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]); +} + +static void +axe_init(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + uint16_t rxmode; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + /* Cancel pending I/O */ + axe_stop(ue); + +#ifdef notdef + /* Set MAC address */ + axe_mac(sc, IF_LLADDR(ifp), 1); +#endif + + /* Set transmitter IPG values */ + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { + axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], + (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); + } else { + axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); + axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); + axe_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 (ifp->if_flags & IFF_PROMISC) + rxmode |= AXE_RXCMD_PROMISC; + + if (ifp->if_flags & IFF_BROADCAST) + rxmode |= AXE_RXCMD_BROADCAST; + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + /* Load the multicast filter. */ + axe_setmulti(ue); + + usb2_transfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + axe_start(ue); +} + +static void +axe_setpromisc(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + uint16_t rxmode; + + axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + + rxmode = le16toh(rxmode); + + if (ifp->if_flags & IFF_PROMISC) { + rxmode |= AXE_RXCMD_PROMISC; + } else { + rxmode &= ~AXE_RXCMD_PROMISC; + } + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + axe_setmulti(ue); +} + +static void +axe_stop(struct usb2_ether *ue) +{ + struct axe_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~AXE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[AXE_INTR_DT_RD]); + + axe_reset(sc); +} + +/* + * 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); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_axereg.h b/sys/dev/usb/net/if_axereg.h new file mode 100644 index 0000000..dc063e3 --- /dev/null +++ b/sys/dev/usb/net/if_axereg.h @@ -0,0 +1,196 @@ +/*- + * 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_PHY_SEL_PRI 1 +#define AXE_PHY_SEL_SEC 0 +#define AXE_PHY_TYPE_MASK 0xE0 +#define AXE_PHY_TYPE_SHIFT 5 +#define AXE_PHY_TYPE(x) \ + (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT) + +#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */ +#define PHY_TYPE_GIG 1 /* Gigabit PHY */ +#define PHY_TYPE_SPECIAL 4 /* Special case */ +#define PHY_TYPE_RSVD 5 /* Reserved */ +#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */ + +#define AXE_PHY_NO_MASK 0x1F +#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK) + +#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ + +#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 + +struct axe_sframe_hdr { + uint16_t len; + uint16_t ilen; +} __packed; + +#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue) + +/* The interrupt endpoint is currently unused by the ASIX part. */ +enum { + AXE_BULK_DT_WR, + AXE_BULK_DT_RD, + AXE_INTR_DT_RD, + AXE_N_TRANSFER, +}; + +struct axe_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[AXE_N_TRANSFER]; + int sc_phyno; + + int sc_flags; +#define AXE_FLAG_LINK 0x0001 +#define AXE_FLAG_772 0x1000 /* AX88772 */ +#define AXE_FLAG_178 0x2000 /* AX88178 */ + + uint8_t sc_ipgs[3]; + uint8_t sc_phyaddrs[2]; +}; + +#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c new file mode 100644 index 0000000..4097bde --- /dev/null +++ b/sys/dev/usb/net/if_cdce.c @@ -0,0 +1,771 @@ +/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul + * Copyright (c) 2003-2005 Craig Boston + * Copyright (c) 2004 Daniel Hartmeier + * Copyright (c) 2009 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. + * 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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR cdce_debug + +#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 usb_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 usb2_ether_fn_t cdce_attach_post; +static usb2_ether_fn_t cdce_init; +static usb2_ether_fn_t cdce_stop; +static usb2_ether_fn_t cdce_start; +static usb2_ether_fn_t cdce_setmulti; +static usb2_ether_fn_t cdce_setpromisc; + +static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t); + +#if USB_DEBUG +static int cdce_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet"); +SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0, + "Debug level"); +#endif + +static const struct usb2_config cdce_config[CDCE_N_TRANSFER] = { + + [CDCE_BULK_A] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 0, + /* Host Mode */ + .mh.frames = CDCE_FRAMES_MAX, + .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .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_FRAMES_MAX, + .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .md.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .md.callback = cdce_bulk_read_callback, + .md.timeout = 0, /* no timeout */ + }, + + [CDCE_BULK_B] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 0, + /* Host Mode */ + .mh.frames = CDCE_FRAMES_MAX, + .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .mh.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .mh.callback = cdce_bulk_read_callback, + .mh.timeout = 0, /* no timeout */ + /* Device Mode */ + .md.frames = CDCE_FRAMES_MAX, + .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, + .md.callback = cdce_bulk_write_callback, + .md.timeout = 10000, /* 10 seconds */ + }, + + [CDCE_INTR] = { + .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(usb_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, uether, 1, 1, 1); +MODULE_DEPEND(cdce, usb, 1, 1, 1); +MODULE_DEPEND(cdce, ether, 1, 1, 1); + +static const struct usb2_ether_methods cdce_ue_methods = { + .ue_attach_post = cdce_attach_post, + .ue_start = cdce_start, + .ue_init = cdce_init, + .ue_stop = cdce_stop, + .ue_setmulti = cdce_setmulti, + .ue_setpromisc = cdce_setpromisc, +}; + +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 void +cdce_attach_post(struct usb2_ether *ue) +{ + /* no-op */ + return; +} + +static int +cdce_attach(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_interface *iface; + const struct usb2_cdc_union_descriptor *ud; + const struct usb2_interface_descriptor *id; + const struct usb2_cdc_ethernet_descriptor *ued; + int error; + uint8_t i; + char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + 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; + } + } + + ued = usb2_find_descriptor + (uaa->device, NULL, uaa->info.bIfaceIndex, + UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1); + + if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { + error = USB_ERR_INVAL; + } else { + error = usb2_req_get_string_any(uaa->device, NULL, + eaddr_str, sizeof(eaddr_str), ued->iMacAddress); + } + + if (error) { + + /* fake MAC address */ + + device_printf(dev, "faking MAC address\n"); + sc->sc_ue.ue_eaddr[0] = 0x2a; + memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t)); + sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); + + } else { + + bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr)); + + for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { + + char c = eaddr_str[i]; + + if ('0' <= c && c <= '9') + c -= '0'; + else if (c != 0) + c -= 'A' - 10; + else + break; + + c &= 0xf; + + if ((i & 1) == 0) + c <<= 4; + sc->sc_ue.ue_eaddr[i / 2] |= c; + } + + if (uaa->usb2_mode == USB_MODE_DEVICE) { + /* + * Do not use the same MAC address like the peer ! + */ + sc->sc_ue.ue_eaddr[5] ^= 0xFF; + } + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &cdce_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + 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 usb2_ether *ue = &sc->sc_ue; + + /* stop all USB transfers first */ + usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cdce_start(struct usb2_ether *ue) +{ + struct cdce_softc *sc = usb2_ether_getsc(ue); + + /* + * Start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[CDCE_BULK_B]); + usb2_transfer_start(sc->sc_xfer[CDCE_BULK_A]); +} + +static void +cdce_free_queue(struct mbuf **ppm, uint8_t n) +{ + uint8_t x; + for (x = 0; x != n; x++) { + if (ppm[x] != NULL) { + m_freem(ppm[x]); + ppm[x] = NULL; + } + } +} + +static void +cdce_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + struct mbuf *mt; + uint32_t crc; + uint8_t x; + + 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 TX buffers */ + cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + for (x = 0; x != CDCE_FRAMES_MAX; x++) { + + 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++; + continue; + } + } + if (m->m_len != m->m_pkthdr.len) { + mt = m_defrag(m, M_DONTWAIT); + if (mt == NULL) { + m_freem(m); + ifp->if_oerrors++; + continue; + } + m = mt; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + sc->sc_tx_buf[x] = m; + xfer->frlengths[x] = m->m_len; + usb2_set_frame_data(xfer, m->m_data, x); + + /* + * If there's a BPF listener, bounce a copy of + * this frame to him: + */ + BPF_MTAP(ifp, m); + } + if (x != 0) { + xfer->nframes = x; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + /* free all previous TX buffers */ + cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); + + /* count output errors */ + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } +} + +static int32_t +cdce_m_crc32_cb(void *arg, void *src, uint32_t count) +{ + 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) +{ + uint32_t crc = 0xFFFFFFFF; + int error; + + error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc); + return (crc ^ 0xFFFFFFFF); +} + +static void +cdce_init(struct usb2_ether *ue) +{ + struct cdce_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + CDCE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* start interrupt transfer */ + usb2_transfer_start(sc->sc_xfer[CDCE_INTR]); + + /* stall data write direction, which depends on USB mode */ + if (usb2_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) + usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_A]); + else + usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_B]); + + /* start data transfers */ + cdce_start(ue); +} + +static void +cdce_stop(struct usb2_ether *ue) +{ + struct cdce_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + CDCE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_A]); + usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_B]); + usb2_transfer_stop(sc->sc_xfer[CDCE_INTR]); +} + +static void +cdce_setmulti(struct usb2_ether *ue) +{ + /* no-op */ + return; +} + +static void +cdce_setpromisc(struct usb2_ether *ue) +{ + /* no-op */ + return; +} + +static int +cdce_shutdown(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + + usb2_ether_ifshutdown(&sc->sc_ue); + + 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 void +cdce_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct cdce_softc *sc = xfer->priv_sc; + struct mbuf *m; + uint8_t x; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("received %u bytes in %u frames\n", + xfer->actlen, xfer->aframes); + + for (x = 0; x != xfer->aframes; x++) { + + m = sc->sc_rx_buf[x]; + sc->sc_rx_buf[x] = NULL; + + /* Strip off CRC added by Zaurus, if any */ + if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && + (xfer->frlengths[x] >= 14)) + xfer->frlengths[x] -= 4; + + if (xfer->frlengths[x] < sizeof(struct ether_header)) { + m_freem(m); + continue; + } + /* queue up mbuf */ + usb2_ether_rxmbuf(&sc->sc_ue, m, xfer->frlengths[x]); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: + /* + * TODO: Implement support for multi frame transfers, + * when the USB hardware supports it. + */ + for (x = 0; x != 1; x++) { + if (sc->sc_rx_buf[x] == NULL) { + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + goto tr_stall; + sc->sc_rx_buf[x] = m; + /* adjust for ethernet */ + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, ETHER_ALIGN); + } else { + m = sc->sc_rx_buf[x]; + } + + usb2_set_frame_data(xfer, m->m_data, x); + xfer->frlengths[x] = m->m_len; + } + /* set number of frames and start hardware */ + xfer->nframes = x; + usb2_start_hardware(xfer); + /* flush any received frames */ + usb2_ether_rxflush(&sc->sc_ue); + break; + + default: /* Error */ + DPRINTF("error = %s\n", + usb2_errstr(xfer->error)); + + if (xfer->error != USB_ERR_CANCELLED) { +tr_stall: + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + xfer->nframes = 0; + usb2_start_hardware(xfer); + break; + } + + /* need to free the RX-mbufs when we are cancelled */ + cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX); + break; + } +} + +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 */ + + /* FALLTHROUGH */ + 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; + } +} + +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); + + /* FALLTHROUGH */ + 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; + } +} + +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/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h new file mode 100644 index 0000000..dac5121 --- /dev/null +++ b/sys/dev/usb/net/if_cdcereg.h @@ -0,0 +1,68 @@ +/*- + * 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_FRAMES_MAX 8 /* units */ +#define CDCE_IND_SIZE_MAX 32 /* bytes */ + +enum { + CDCE_BULK_A, + CDCE_BULK_B, + CDCE_INTR, + CDCE_N_TRANSFER, +}; + +struct cdce_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[CDCE_N_TRANSFER]; + struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX]; + struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX]; + + int sc_flags; +#define CDCE_FLAG_ZAURUS 0x0001 +#define CDCE_FLAG_NO_UNION 0x0002 +#define CDCE_FLAG_RX_DATA 0x0010 + + uint8_t sc_eaddr_str_index; + uint8_t sc_data_iface_no; + uint8_t sc_ifaces_index[2]; +}; + +#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define CDCE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) +#endif /* _USB_IF_CDCEREG_H_ */ diff --git a/sys/dev/usb/net/if_cue.c b/sys/dev/usb/net/if_cue.c new file mode 100644 index 0000000..72ec92d --- /dev/null +++ b/sys/dev/usb/net/if_cue.c @@ -0,0 +1,645 @@ +/*- + * 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. + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR cue_debug + +#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_callback; +static usb2_callback_t cue_bulk_write_callback; + +static usb2_ether_fn_t cue_attach_post; +static usb2_ether_fn_t cue_init; +static usb2_ether_fn_t cue_stop; +static usb2_ether_fn_t cue_start; +static usb2_ether_fn_t cue_tick; +static usb2_ether_fn_t cue_setmulti; +static usb2_ether_fn_t cue_setpromisc; + +static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t); +static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t); +static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t); +static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int); +static int cue_getmac(struct cue_softc *, void *); +static uint32_t cue_mchash(const uint8_t *); +static void cue_reset(struct cue_softc *); + +#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_N_TRANSFER] = { + + [CUE_BULK_DT_WR] = { + .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 */ + }, + + [CUE_BULK_DT_RD] = { + .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, + }, +}; + +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, uether, 1, 1, 1); +MODULE_DEPEND(cue, usb, 1, 1, 1); +MODULE_DEPEND(cue, ether, 1, 1, 1); + +static const struct usb2_ether_methods cue_ue_methods = { + .ue_attach_post = cue_attach_post, + .ue_start = cue_start, + .ue_init = cue_init, + .ue_stop = cue_stop, + .ue_tick = cue_tick, + .ue_setmulti = cue_setmulti, + .ue_setpromisc = cue_setpromisc, +}; + +#define CUE_SETBIT(sc, reg, x) \ + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) + +#define CUE_CLRBIT(sc, reg, x) \ + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +cue_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); + + if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* ignore any errors */ + } + return (val); +} + +static uint16_t +cue_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); + + (void)usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); + return (le16toh(val)); +} + +static int +cue_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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} + +static int +cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +cue_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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +#define CUE_BITS 9 + +static uint32_t +cue_mchash(const uint8_t *addr) +{ + uint32_t crc; + + /* Compute CRC for the address value. */ + crc = ether_crc32_le(addr, ETHER_ADDR_LEN); + + return (crc & ((1 << CUE_BITS) - 1)); +} + +static void +cue_setpromisc(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + /* if we want promiscuous mode, set the allframes bit */ + if (ifp->if_flags & IFF_PROMISC) + CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + else + CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + + /* write multicast hash-bits */ + cue_setmulti(ue); +} + +static void +cue_setmulti(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0, i; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + for (i = 0; i < 8; i++) + hashtbl[i] = 0xff; + cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, + &hashtbl, 8); + return; + } + + /* now program new ones */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashtbl[h >> 3] |= 1 << (h & 0x7); + } + IF_ADDR_UNLOCK(ifp); + + /* + * Also include the broadcast address in the filter + * so we can receive broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + h = cue_mchash(ifp->if_broadcastaddr); + hashtbl[h >> 3] |= 1 << (h & 0x7); + } + + cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8); +} + +static void +cue_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); + + if (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)) { + /* ignore any errors */ + } + + /* + * wait a little while for the chip to get its brains in order: + */ + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +static void +cue_attach_post(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + + cue_getmac(sc, ue->ue_eaddr); +} + +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)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +cue_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct cue_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb2_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = CUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &cue_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + cue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +cue_detach(device_t dev) +{ + struct cue_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + uint8_t buf[2]; + int 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); + xfer->actlen -= 2; + len = buf[0] | (buf[1] << 8); + len = min(xfer->actlen, len); + + usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + return; + + } +} + +static void +cue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct cue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_opackets++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + 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); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +cue_tick(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL); + ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL); + ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL); + + if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) + ifp->if_ierrors++; +} + +static void +cue_start(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]); +} + +static void +cue_init(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + int i; + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + cue_stop(ue); +#if 0 + cue_reset(sc); +#endif + /* Set MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]); + + /* Enable RX logic. */ + cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); + + /* Load the multicast filter */ + cue_setpromisc(ue); + + /* + * Set the number of RX and TX buffers that we want + * to reserve inside the ASIC. + */ + cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); + cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); + + /* Set advanced operation modes. */ + cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, + CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ + + /* Program the LED operation. */ + cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); + + usb2_transfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + cue_start(ue); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +cue_stop(struct usb2_ether *ue) +{ + struct cue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]); + + cue_csr_write_1(sc, CUE_ETHCTL, 0); + cue_reset(sc); +} + +/* + * 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); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_cuereg.h b/sys/dev/usb/net/if_cuereg.h new file mode 100644 index 0000000..ca3a816 --- /dev/null +++ b/sys/dev/usb/net/if_cuereg.h @@ -0,0 +1,132 @@ +/*- + * 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_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. */ +enum { + CUE_BULK_DT_WR, + CUE_BULK_DT_RD, + CUE_N_TRANSFER, +}; + +struct cue_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[CUE_N_TRANSFER]; + + int sc_flags; +#define CUE_FLAG_LINK 0x0001 /* got a link */ +}; + +#define CUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define CUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/if_kue.c b/sys/dev/usb/net/if_kue.c new file mode 100644 index 0000000..b97922f --- /dev/null +++ b/sys/dev/usb/net/if_kue.c @@ -0,0 +1,704 @@ +/*- + * 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. + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR kue_debug + +#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_callback; +static usb2_callback_t kue_bulk_write_callback; + +static usb2_ether_fn_t kue_attach_post; +static usb2_ether_fn_t kue_init; +static usb2_ether_fn_t kue_stop; +static usb2_ether_fn_t kue_start; +static usb2_ether_fn_t kue_setmulti; +static usb2_ether_fn_t kue_setpromisc; + +static int kue_do_request(struct kue_softc *, + struct usb2_device_request *, void *); +static int kue_setword(struct kue_softc *, uint8_t, uint16_t); +static int kue_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t, + void *, int); +static int kue_load_fw(struct kue_softc *); +static void kue_reset(struct kue_softc *); + +#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_N_TRANSFER] = { + + [KUE_BULK_DT_WR] = { + .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 */ + }, + + [KUE_BULK_DT_RD] = { + .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 */ + }, +}; + +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, uether, 1, 1, 1); +MODULE_DEPEND(kue, usb, 1, 1, 1); +MODULE_DEPEND(kue, ether, 1, 1, 1); + +static const struct usb2_ether_methods kue_ue_methods = { + .ue_attach_post = kue_attach_post, + .ue_start = kue_start, + .ue_init = kue_init, + .ue_stop = kue_stop, + .ue_setmulti = kue_setmulti, + .ue_setpromisc = kue_setpromisc, +}; + +/* + * 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 int +kue_do_request(struct kue_softc *sc, struct usb2_device_request *req, + void *data) +{ + usb2_error_t err; + + err = usb2_ether_do_request(&sc->sc_ue, req, data, 60000); + + return (err); +} + +static int +kue_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); + + return (kue_do_request(sc, &req, NULL)); +} + +static int +kue_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, + uint16_t val, void *data, int 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); + + return (kue_do_request(sc, &req, data)); +} + +static int +kue_load_fw(struct kue_softc *sc) +{ + struct usb2_device_descriptor *dd; + uint16_t hwrev; + usb2_error_t err; + + dd = usb2_get_device_descriptor(sc->sc_ue.ue_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(0); + + /* Load code segment */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_code_seg, sizeof(kue_code_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load code segment: %s\n", + usb2_errstr(err)); + return(ENXIO); + } + + /* Load fixup segment */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_fix_seg, sizeof(kue_fix_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load fixup segment: %s\n", + usb2_errstr(err)); + return(ENXIO); + } + + /* Send trigger command. */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_trig_seg, sizeof(kue_trig_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load trigger segment: %s\n", + usb2_errstr(err)); + return(ENXIO); + } + + return (0); +} + +static void +kue_setpromisc(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) + sc->sc_rxfilt |= KUE_RXFILT_PROMISC; + else + sc->sc_rxfilt &= ~KUE_RXFILT_PROMISC; + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); +} + +static void +kue_setmulti(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + struct ifmultiaddr *ifma; + int i = 0; + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; + sc->sc_rxfilt &= ~KUE_RXFILT_MULTICAST; + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); + return; + } + + sc->sc_rxfilt &= ~KUE_RXFILT_ALLMULTI; + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + /* + * If there are too many addresses for the + * internal filter, switch over to allmulti mode. + */ + if (i == KUE_MCFILTCNT(sc)) + break; + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + KUE_MCFILT(sc, i), ETHER_ADDR_LEN); + i++; + } + IF_ADDR_UNLOCK(ifp); + + if (i == KUE_MCFILTCNT(sc)) + sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; + else { + sc->sc_rxfilt |= KUE_RXFILT_MULTICAST; + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, + i, sc->sc_mcfilters, i * ETHER_ADDR_LEN); + } + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); +} + +/* + * Issue a SET_CONFIGURATION command to reset the MAC. This should be + * done after the firmware is loaded into the adapter in order to + * bring it into proper operation. + */ +static void +kue_reset(struct kue_softc *sc) +{ + struct usb2_config_descriptor *cd; + usb2_error_t err; + + cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usb2_req_set_config(sc->sc_ue.ue_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 */ + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +static void +kue_attach_post(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + int error; + + /* load the firmware into the NIC */ + error = kue_load_fw(sc); + if (error) { + device_printf(sc->sc_ue.ue_dev, "could not load firmware\n"); + /* ignore the error */ + } + + /* reset the adapter */ + kue_reset(sc); + + /* read ethernet descriptor */ + kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, + 0, &sc->sc_desc, sizeof(sc->sc_desc)); + + /* copy in ethernet address */ + memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr)); +} + +/* + * 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); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb2_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = KUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, kue_config, KUE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + sc->sc_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, + M_USBDEV, M_WAITOK); + if (sc->sc_mcfilters == NULL) { + device_printf(dev, "failed allocating USB memory!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &kue_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + kue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +kue_detach(device_t dev) +{ + struct kue_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + free(sc->sc_mcfilters, M_USBDEV); + + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +kue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + uint8_t buf[2]; + int 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); + xfer->actlen -= 2; + len = buf[0] | (buf[1] << 8); + len = min(xfer->actlen, len); + + usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + return; + + } +} + +static void +kue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct kue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + int total_len; + int temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_opackets++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + 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); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + + } +} + +static void +kue_start(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]); +} + +static void +kue_init(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + /* set MAC address */ + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, + 0, IF_LLADDR(ifp), 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_setword(sc, KUE_CMD_SET_SOFS, 1); +#endif + kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64); + + /* load the multicast filter */ + kue_setpromisc(ue); + + usb2_transfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + kue_start(ue); +} + +static void +kue_stop(struct usb2_ether *ue) +{ + struct kue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]); +} + +/* + * 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); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_kuefw.h b/sys/dev/usb/net/if_kuefw.h new file mode 100644 index 0000000..2b055a9 --- /dev/null +++ b/sys/dev/usb/net/if_kuefw.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/usb/net/if_kuereg.h b/sys/dev/usb/net/if_kuereg.h new file mode 100644 index 0000000..8650687 --- /dev/null +++ b/sys/dev/usb/net/if_kuereg.h @@ -0,0 +1,141 @@ +/*- + * 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(x, y) \ + (char *)&(sc->sc_mcfilters[y * ETHER_ADDR_LEN]) + +#define KUE_STAT_TX_OK 0x00000001 +#define KUE_STAT_RX_OK 0x00000002 +#define KUE_STAT_TX_ERR 0x00000004 +#define KUE_STAT_RX_ERR 0x00000008 +#define KUE_STAT_RX_NOBUF 0x00000010 +#define KUE_STAT_TX_UCAST_BYTES 0x00000020 +#define KUE_STAT_TX_UCAST_FRAMES 0x00000040 +#define KUE_STAT_TX_MCAST_BYTES 0x00000080 +#define KUE_STAT_TX_MCAST_FRAMES 0x00000100 +#define KUE_STAT_TX_BCAST_BYTES 0x00000200 +#define KUE_STAT_TX_BCAST_FRAMES 0x00000400 +#define KUE_STAT_RX_UCAST_BYTES 0x00000800 +#define KUE_STAT_RX_UCAST_FRAMES 0x00001000 +#define KUE_STAT_RX_MCAST_BYTES 0x00002000 +#define KUE_STAT_RX_MCAST_FRAMES 0x00004000 +#define KUE_STAT_RX_BCAST_BYTES 0x00008000 +#define KUE_STAT_RX_BCAST_FRAMES 0x00010000 +#define KUE_STAT_RX_CRCERR 0x00020000 +#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 +#define KUE_STAT_RX_ALIGNERR 0x00080000 +#define KUE_STAT_TX_SINGLECOLL 0x00100000 +#define KUE_STAT_TX_MULTICOLL 0x00200000 +#define KUE_STAT_TX_DEFERRED 0x00400000 +#define KUE_STAT_TX_MAXCOLLS 0x00800000 +#define KUE_STAT_RX_OVERRUN 0x01000000 +#define KUE_STAT_TX_UNDERRUN 0x02000000 +#define KUE_STAT_TX_SQE_ERR 0x04000000 +#define KUE_STAT_TX_CARRLOSS 0x08000000 +#define KUE_STAT_RX_LATECOLL 0x10000000 + +#define KUE_RXFILT_PROMISC 0x0001 +#define KUE_RXFILT_ALLMULTI 0x0002 +#define KUE_RXFILT_UNICAST 0x0004 +#define KUE_RXFILT_BROADCAST 0x0008 +#define KUE_RXFILT_MULTICAST 0x0010 + +#define KUE_TIMEOUT 1000 +#define KUE_MIN_FRAMELEN 60 + +#define KUE_CTL_READ 0x01 +#define KUE_CTL_WRITE 0x02 + +#define KUE_CONFIG_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 +enum { + KUE_BULK_DT_WR, + KUE_BULK_DT_RD, + KUE_N_TRANSFER, +}; + +struct kue_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct kue_ether_desc sc_desc; + struct usb2_xfer *sc_xfer[KUE_N_TRANSFER]; + uint8_t *sc_mcfilters; + + int sc_flags; +#define KUE_FLAG_LINK 0x0001 + + uint16_t sc_rxfilt; +}; + +#define KUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define KUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/if_rue.c b/sys/dev/usb/net/if_rue.c new file mode 100644 index 0000000..1d0f6ee --- /dev/null +++ b/sys/dev/usb/net/if_rue.c @@ -0,0 +1,913 @@ +/*- + * 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/. + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR rue_debug + +#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 miibus_readreg_t rue_miibus_readreg; +static miibus_writereg_t rue_miibus_writereg; +static miibus_statchg_t rue_miibus_statchg; + +static usb2_callback_t rue_intr_callback; +static usb2_callback_t rue_bulk_read_callback; +static usb2_callback_t rue_bulk_write_callback; + +static usb2_ether_fn_t rue_attach_post; +static usb2_ether_fn_t rue_init; +static usb2_ether_fn_t rue_stop; +static usb2_ether_fn_t rue_start; +static usb2_ether_fn_t rue_tick; +static usb2_ether_fn_t rue_setmulti; +static usb2_ether_fn_t rue_setpromisc; + +static int rue_read_mem(struct rue_softc *, uint16_t, void *, int); +static int rue_write_mem(struct rue_softc *, uint16_t, void *, int); +static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t); +static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t); +static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t); +static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t); +static int rue_csr_write_4(struct rue_softc *, int, uint32_t); + +static void rue_reset(struct rue_softc *); +static int rue_ifmedia_upd(struct ifnet *); +static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static const struct usb2_config rue_config[RUE_N_TRANSFER] = { + + [RUE_BULK_DT_WR] = { + .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 */ + }, + + [RUE_BULK_DT_RD] = { + .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 */ + }, + + [RUE_INTR_DT_RD] = { + .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, + }, +}; + +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_miibus_readreg), + DEVMETHOD(miibus_writereg, rue_miibus_writereg), + DEVMETHOD(miibus_statchg, rue_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, uether, 1, 1, 1); +MODULE_DEPEND(rue, usb, 1, 1, 1); +MODULE_DEPEND(rue, ether, 1, 1, 1); +MODULE_DEPEND(rue, miibus, 1, 1, 1); + +static const struct usb2_ether_methods rue_ue_methods = { + .ue_attach_post = rue_attach_post, + .ue_start = rue_start, + .ue_init = rue_init, + .ue_stop = rue_stop, + .ue_tick = rue_tick, + .ue_setmulti = rue_setmulti, + .ue_setpromisc = rue_setpromisc, + .ue_mii_upd = rue_ifmedia_upd, + .ue_mii_sts = rue_ifmedia_sts, +}; + +#define RUE_SETBIT(sc, reg, x) \ + rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x)) + +#define RUE_CLRBIT(sc, reg, x) \ + rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x)) + +static int +rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static uint8_t +rue_csr_read_1(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val; + + rue_read_mem(sc, reg, &val, 1); + return (val); +} + +static uint16_t +rue_csr_read_2(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val[2]; + + rue_read_mem(sc, reg, &val, 2); + return (UGETW(val)); +} + +static int +rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) +{ + return (rue_write_mem(sc, reg, &val, 1)); +} + +static int +rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) +{ + uint8_t temp[2]; + + USETW(temp, val); + return (rue_write_mem(sc, reg, &temp, 2)); +} + +static int +rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) +{ + uint8_t temp[4]; + + USETDW(temp, val); + return (rue_write_mem(sc, reg, &temp, 4)); +} + +static int +rue_miibus_readreg(device_t dev, int phy, int reg) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t rval; + uint16_t ruereg; + int locked; + + if (phy != 0) /* RTL8150 supports PHY == 0, only */ + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + 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_csr_read_1(sc, reg); + goto done; + } + device_printf(sc->sc_ue.ue_dev, "bad phy register\n"); + rval = 0; + goto done; + } + + rval = rue_csr_read_2(sc, ruereg); +done: + if (!locked) + RUE_UNLOCK(sc); + return (rval); +} + +static int +rue_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t ruereg; + int locked; + + if (phy != 0) /* RTL8150 supports PHY == 0, only */ + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + 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_csr_write_1(sc, reg, data); + goto done; + } + device_printf(sc->sc_ue.ue_dev, " bad phy register\n"); + goto done; + } + rue_csr_write_2(sc, ruereg, data); +done: + if (!locked) + RUE_UNLOCK(sc); + return (0); +} + +static void +rue_miibus_statchg(device_t dev) +{ + /* + * When the code below is enabled the card starts doing weird + * things after link going from UP to DOWN and back UP. + * + * Looks like some of register writes below messes up PHY + * interface. + * + * No visible regressions were found after commenting this code + * out, so that disable it for good. + */ +#if 0 + struct rue_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + uint16_t bmcr; + int locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + bmcr = rue_csr_read_2(sc, RUE_BMCR); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + bmcr |= RUE_BMCR_SPD_SET; + else + bmcr &= ~RUE_BMCR_SPD_SET; + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + bmcr |= RUE_BMCR_DUPLEX; + else + bmcr &= ~RUE_BMCR_DUPLEX; + + rue_csr_write_2(sc, RUE_BMCR, bmcr); + + RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + if (!locked) + RUE_UNLOCK(sc); +#endif +} + +static void +rue_setpromisc(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP); + else + RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +rue_setmulti(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + uint16_t rxcfg; + int h = 0; + uint32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + int mcnt = 0; + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + rxcfg = rue_csr_read_2(sc, RUE_RCR); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); + rxcfg &= ~RUE_RCR_AM; + rue_csr_write_2(sc, RUE_RCR, rxcfg); + rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); + rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + rue_csr_write_4(sc, RUE_MAR0, 0); + rue_csr_write_4(sc, RUE_MAR4, 0); + + /* now program new ones */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + IF_ADDR_UNLOCK(ifp); + + if (mcnt) + rxcfg |= RUE_RCR_AM; + else + rxcfg &= ~RUE_RCR_AM; + + rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); + + rue_csr_write_2(sc, RUE_RCR, rxcfg); + rue_csr_write_4(sc, RUE_MAR0, hashes[0]); + rue_csr_write_4(sc, RUE_MAR4, hashes[1]); +} + +static void +rue_reset(struct rue_softc *sc) +{ + int i; + + rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); + + for (i = 0; i != RUE_TIMEOUT; i++) { + if (usb2_ether_pause(&sc->sc_ue, hz / 1000)) + break; + if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) + break; + } + if (i == RUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "reset never completed!\n"); + + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +static void +rue_attach_post(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + + /* reset the adapter */ + rue_reset(sc); + + /* get station address from the EEPROM */ + rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN); +} + +/* + * 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); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb2_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = RUE_IFACE_IDX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rue_config, RUE_N_TRANSFER, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &rue_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + rue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +rue_detach(device_t dev) +{ + struct rue_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +rue_intr_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + 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; + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +rue_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + uint16_t status; + + 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)); + xfer->actlen -= 4; + + /* check recieve packet was valid or not */ + status = le16toh(status); + if ((status & RUE_RXSTAT_VALID) == 0) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + return; + } +} + +static void +rue_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct rue_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + int temp_len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_opackets++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & RUE_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + 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); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +rue_tick(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & RUE_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= RUE_FLAG_LINK; + rue_start(ue); + } +} + +static void +rue_start(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]); + usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]); +} + +static void +rue_init(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + rue_reset(sc); + + /* Set MAC address */ + rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + rue_stop(ue); + + /* + * Set the initial TX and RX configuration. + */ + rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); + rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB); + + /* Load the multicast filter */ + rue_setpromisc(ue); + /* Load the multicast filter. */ + rue_setmulti(ue); + + /* Enable RX and TX */ + rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); + + usb2_transfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + rue_start(ue); +} + +/* + * Set media options. + */ +static int +rue_ifmedia_upd(struct ifnet *ifp) +{ + struct rue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~RUE_FLAG_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 (0); +} + +/* + * Report current media status. + */ +static void +rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct rue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + RUE_LOCK(sc); + mii_pollstat(mii); + RUE_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static void +rue_stop(struct usb2_ether *ue) +{ + struct rue_softc *sc = usb2_ether_getsc(ue); + struct ifnet *ifp = usb2_ether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~RUE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]); + + rue_csr_write_1(sc, RUE_CR, 0x00); + + rue_reset(sc); +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static int +rue_shutdown(device_t dev) +{ + struct rue_softc *sc = device_get_softc(dev); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_ruereg.h b/sys/dev/usb/net/if_ruereg.h new file mode 100644 index 0000000..a94d45a --- /dev/null +++ b/sys/dev/usb/net/if_ruereg.h @@ -0,0 +1,183 @@ +/*- + * 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_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) usb2_ether_getmii(&(sc)->sc_ue) + +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; +}; + +enum { + RUE_BULK_DT_WR, + RUE_BULK_DT_RD, + RUE_INTR_DT_RD, + RUE_N_TRANSFER, +}; + +struct rue_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[RUE_N_TRANSFER]; + + int sc_flags; +#define RUE_FLAG_LINK 0x0001 +}; + +#define RUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define RUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/if_udav.c b/sys/dev/usb/net/if_udav.c new file mode 100644 index 0000000..82cec80 --- /dev/null +++ b/sys/dev/usb/net/if_udav.c @@ -0,0 +1,856 @@ +/* $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 + */ + +/* + * TODO: + * Interrupt Endpoint support + * External PHYs + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR udav_debug + +#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_callback; +static usb2_callback_t udav_bulk_read_callback; +static usb2_callback_t udav_intr_callback; + +static usb2_ether_fn_t udav_attach_post; +static usb2_ether_fn_t udav_init; +static usb2_ether_fn_t udav_stop; +static usb2_ether_fn_t udav_start; +static usb2_ether_fn_t udav_tick; +static usb2_ether_fn_t udav_setmulti; +static usb2_ether_fn_t udav_setpromisc; + +static int udav_csr_read(struct udav_softc *, uint16_t, void *, int); +static int udav_csr_write(struct udav_softc *, uint16_t, void *, int); +static uint8_t udav_csr_read1(struct udav_softc *, uint16_t); +static int udav_csr_write1(struct udav_softc *, uint16_t, uint8_t); +static void udav_reset(struct udav_softc *); +static int udav_ifmedia_upd(struct ifnet *); +static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *); + +static miibus_readreg_t udav_miibus_readreg; +static miibus_writereg_t udav_miibus_writereg; +static miibus_statchg_t udav_miibus_statchg; + +static const struct usb2_config udav_config[UDAV_N_TRANSFER] = { + + [UDAV_BULK_DT_WR] = { + .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 */ + }, + + [UDAV_BULK_DT_RD] = { + .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 */ + }, + + [UDAV_INTR_DT_RD] = { + .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, + }, +}; + +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_miibus_readreg), + DEVMETHOD(miibus_writereg, udav_miibus_writereg), + DEVMETHOD(miibus_statchg, udav_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, uether, 1, 1, 1); +MODULE_DEPEND(udav, usb, 1, 1, 1); +MODULE_DEPEND(udav, ether, 1, 1, 1); +MODULE_DEPEND(udav, miibus, 1, 1, 1); + +static const struct usb2_ether_methods udav_ue_methods = { + .ue_attach_post = udav_attach_post, + .ue_start = udav_start, + .ue_init = udav_init, + .ue_stop = udav_stop, + .ue_tick = udav_tick, + .ue_setmulti = udav_setmulti, + .ue_setpromisc = udav_setpromisc, + .ue_mii_upd = udav_ifmedia_upd, + .ue_mii_sts = udav_ifmedia_status, +}; + +#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_SETBIT(sc, reg, x) \ + udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x)) + +#define UDAV_CLRBIT(sc, reg, x) \ + udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x)) + +static const struct 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 void +udav_attach_post(struct usb2_ether *ue) +{ + struct udav_softc *sc = usb2_ether_getsc(ue); + + /* reset the adapter */ + udav_reset(sc); + + /* Get Ethernet Address */ + udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN); +} + +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); + struct usb2_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb2_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = UDAV_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed!\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &udav_ue_methods; + + error = usb2_ether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + + return (0); /* success */ + +detach: + udav_detach(dev); + return (ENXIO); /* failure */ +} + +static int +udav_detach(device_t dev) +{ + struct udav_softc *sc = device_get_softc(dev); + struct usb2_ether *ue = &sc->sc_ue; + + usb2_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER); + usb2_ether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +#if 0 +static int +udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf, + int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf, + int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} +#endif + +static int +udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int 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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static uint8_t +udav_csr_read1(struct udav_softc *sc, uint16_t offset) +{ + uint8_t val; + + udav_csr_read(sc, offset, &val, 1); + return (val); +} + +static int +udav_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); + + return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} + +static void +udav_init(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + udav_stop(ue); + + /* set MAC address */ + udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + /* initialize network control register */ + + /* disable loopback */ + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); + + /* Initialize RX control register */ + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); + + /* load multicast filter and update promiscious mode bit */ + udav_setpromisc(ue); + + /* enable RX */ + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); + + /* clear POWER_DOWN state of internal PHY */ + UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); + UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); + + usb2_transfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + udav_start(ue); +} + +static void +udav_reset(struct udav_softc *sc) +{ + int i; + + /* Select PHY */ +#if 1 + /* + * XXX: force select internal phy. + * external phy routines are not tested. + */ + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); +#else + if (sc->sc_flags & UDAV_EXT_PHY) + UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); + else + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); +#endif + + UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); + + for (i = 0; i < UDAV_TX_TIMEOUT; i++) { + if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) + break; + if (usb2_ether_pause(&sc->sc_ue, hz / 100)) + break; + } + + usb2_ether_pause(&sc->sc_ue, hz / 100); +} + +#define UDAV_BITS 6 +static void +udav_setmulti(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct ifmultiaddr *ifma; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int h = 0; + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); + return; + } + + /* first, zot all the existing hash bits */ + memset(hashtbl, 0x00, sizeof(hashtbl)); + hashtbl[7] |= 0x80; /* broadcast address */ + udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); + + /* now program new ones */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + IF_ADDR_UNLOCK(ifp); + + /* disable all multicast */ + UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL); + + /* write hash value to the register */ + udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); +} + +static void +udav_setpromisc(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + uint8_t rxmode; + + rxmode = udav_csr_read1(sc, UDAV_RCR); + rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC); + + if (ifp->if_flags & IFF_PROMISC) + rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC; + else if (ifp->if_flags & IFF_ALLMULTI) + rxmode |= UDAV_RCR_ALL; + + /* write new mode bits */ + udav_csr_write1(sc, UDAV_RCR, rxmode); +} + +static void +udav_start(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + + /* + * start the USB transfers, if not already started: + */ + usb2_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]); + usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]); + usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]); +} + +static void +udav_bulk_write_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + struct mbuf *m; + int extra_len; + int temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_opackets++; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + 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); + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +udav_bulk_read_callback(struct usb2_xfer *xfer) +{ + struct udav_softc *sc = xfer->priv_sc; + struct usb2_ether *ue = &sc->sc_ue; + struct ifnet *ifp = usb2_ether_getifp(ue); + struct udav_rxpkt stat; + int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (xfer->actlen < sizeof(stat) + ETHER_CRC_LEN) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_copy_out(xfer->frbuffers, 0, &stat, sizeof(stat)); + xfer->actlen -= sizeof(stat); + len = min(xfer->actlen, le16toh(stat.pktlen)); + len -= ETHER_CRC_LEN; + + if (stat.rxstat & UDAV_RSR_LCS) { + ifp->if_collisions++; + goto tr_setup; + } + if (stat.rxstat & UDAV_RSR_ERR) { + ifp->if_ierrors++; + goto tr_setup; + } + usb2_ether_rxbuf(ue, xfer->frbuffers, sizeof(stat), len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + usb2_ether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read 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; + } + return; + } +} + +static void +udav_intr_callback(struct usb2_xfer *xfer) +{ + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +udav_stop(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~UDAV_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]); + usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]); + usb2_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]); + + udav_reset(sc); +} + +static int +udav_ifmedia_upd(struct ifnet *ifp) +{ + struct udav_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~UDAV_FLAG_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 (0); +} + +static void +udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct udav_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + UDAV_LOCK(sc); + mii_pollstat(mii); + UDAV_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static void +udav_tick(struct usb2_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct mii_data *mii = GET_MII(sc); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & UDAV_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= UDAV_FLAG_LINK; + udav_start(ue); + } +} + +static int +udav_miibus_readreg(device_t dev, int phy, int reg) +{ + struct udav_softc *sc = device_get_softc(dev); + uint16_t data16; + uint8_t val[2]; + int locked; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + UDAV_LOCK(sc); + + /* select internal PHY and set PHY register address */ + udav_csr_write1(sc, UDAV_EPAR, + UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); + + /* select PHY operation and start read command */ + udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); + + /* XXX: should we wait? */ + + /* end read command */ + UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); + + /* retrieve the result from data registers */ + udav_csr_read(sc, UDAV_EPDRL, val, 2); + + data16 = (val[0] | (val[1] << 8)); + + DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n", + phy, reg, data16); + + if (!locked) + UDAV_UNLOCK(sc); + return (data16); +} + +static int +udav_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct udav_softc *sc = device_get_softc(dev); + uint8_t val[2]; + int locked; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + UDAV_LOCK(sc); + + /* select internal PHY and set PHY register address */ + udav_csr_write1(sc, UDAV_EPAR, + UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); + + /* put the value to the data registers */ + val[0] = (data & 0xff); + val[1] = (data >> 8) & 0xff; + udav_csr_write(sc, UDAV_EPDRL, val, 2); + + /* select PHY operation and start write command */ + udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); + + /* XXX: should we wait? */ + + /* end write command */ + UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); + + if (!locked) + UDAV_UNLOCK(sc); + return (0); +} + +static void +udav_miibus_statchg(device_t dev) +{ + /* nothing to do */ +} + +/* + * 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); + + usb2_ether_ifshutdown(&sc->sc_ue); + + return (0); +} diff --git a/sys/dev/usb/net/if_udavreg.h b/sys/dev/usb/net/if_udavreg.h new file mode 100644 index 0000000..d652f5b --- /dev/null +++ b/sys/dev/usb/net/if_udavreg.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_TX_TIMEOUT 1000 +#define UDAV_TIMEOUT 10000 + +#define UDAV_TX_TIMEOUT 1000 +#define UDAV_TIMEOUT 10000 + +/* 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) usb2_ether_getmii(&(sc)->sc_ue) + +struct udav_rxpkt { + uint8_t rxstat; + uint16_t pktlen; +} __packed; + +enum { + UDAV_BULK_DT_WR, + UDAV_BULK_DT_RD, + UDAV_INTR_DT_RD, + UDAV_N_TRANSFER, +}; + +struct udav_softc { + struct usb2_ether sc_ue; + struct mtx sc_mtx; + struct usb2_xfer *sc_xfer[UDAV_N_TRANSFER]; + + int sc_flags; +#define UDAV_FLAG_LINK 0x0001 +#define UDAV_FLAG_EXT_PHY 0x0040 +}; + +#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define UDAV_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb/net/usb_ethernet.c b/sys/dev/usb/net/usb_ethernet.c new file mode 100644 index 0000000..ac4b701 --- /dev/null +++ b/sys/dev/usb/net/usb_ethernet.c @@ -0,0 +1,587 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.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. + * + * 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 + +SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, "USB Ethernet parameters"); + +#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx) +#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx) +#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t) + +MODULE_DEPEND(uether, usb, 1, 1, 1); +MODULE_DEPEND(uether, miibus, 1, 1, 1); + +static struct unrhdr *ueunit; + +static usb2_proc_callback_t ue_attach_post_task; +static usb2_proc_callback_t ue_promisc_task; +static usb2_proc_callback_t ue_setmulti_task; +static usb2_proc_callback_t ue_ifmedia_task; +static usb2_proc_callback_t ue_tick_task; +static usb2_proc_callback_t ue_start_task; +static usb2_proc_callback_t ue_stop_task; + +static void ue_init(void *); +static void ue_start(struct ifnet *); +static int ue_ifmedia_upd(struct ifnet *); +static void ue_watchdog(void *); + +/* + * Return values: + * 0: success + * Else: device has been detached + */ +uint8_t +usb2_ether_pause(struct usb2_ether *ue, unsigned int _ticks) +{ + if (usb2_proc_is_gone(&ue->ue_tq)) { + /* nothing to do */ + return (1); + } + usb2_pause_mtx(ue->ue_mtx, _ticks); + return (0); +} + +static void +ue_queue_command(struct usb2_ether *ue, + usb2_proc_callback_t *fn, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) +{ + struct usb2_ether_cfg_task *task; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (usb2_proc_is_gone(&ue->ue_tq)) { + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct usb2_ether_cfg_task *) + usb2_proc_msignal(&ue->ue_tq, t0, t1); + + /* Setup callback and self pointers */ + task->hdr.pm_callback = fn; + task->ue = ue; + + /* + * Start and stop must be synchronous! + */ + if ((fn == ue_start_task) || (fn == ue_stop_task)) + usb2_proc_mwait(&ue->ue_tq, t0, t1); +} + +struct ifnet * +usb2_ether_getifp(struct usb2_ether *ue) +{ + return (ue->ue_ifp); +} + +struct mii_data * +usb2_ether_getmii(struct usb2_ether *ue) +{ + return (device_get_softc(ue->ue_miibus)); +} + +void * +usb2_ether_getsc(struct usb2_ether *ue) +{ + return (ue->ue_sc); +} + +static int +ue_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct usb2_ether *ue = arg1; + const char *name; + + name = device_get_nameunit(ue->ue_dev); + return SYSCTL_OUT(req, name, strlen(name)); +} + +int +usb2_ether_ifattach(struct usb2_ether *ue) +{ + int error; + + /* check some critical parameters */ + if ((ue->ue_dev == NULL) || + (ue->ue_udev == NULL) || + (ue->ue_mtx == NULL) || + (ue->ue_methods == NULL)) + return (EINVAL); + + error = usb2_proc_create(&ue->ue_tq, ue->ue_mtx, + device_get_nameunit(ue->ue_dev), USB_PRI_MED); + if (error) { + device_printf(ue->ue_dev, "could not setup taskqueue\n"); + goto error; + } + + /* fork rest of the attach code */ + UE_LOCK(ue); + ue_queue_command(ue, ue_attach_post_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); + +error: + return (error); +} + +static void +ue_attach_post_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + struct ifnet *ifp; + int error; + char num[14]; /* sufficient for 32 bits */ + + /* first call driver's post attach routine */ + ue->ue_methods->ue_attach_post(ue); + + UE_UNLOCK(ue); + + ue->ue_unit = alloc_unr(ueunit); + usb2_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0); + sysctl_ctx_init(&ue->ue_sysctl_ctx); + + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(ue->ue_dev, "could not allocate ifnet\n"); + goto error; + } + + ifp->if_softc = ue; + if_initname(ifp, "ue", ue->ue_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + if (ue->ue_methods->ue_ioctl != NULL) + ifp->if_ioctl = ue->ue_methods->ue_ioctl; + else + ifp->if_ioctl = usb2_ether_ioctl; + ifp->if_start = ue_start; + ifp->if_init = ue_init; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + ue->ue_ifp = ifp; + + if (ue->ue_methods->ue_mii_upd != NULL && + ue->ue_methods->ue_mii_sts != NULL) { + mtx_lock(&Giant); /* device_xxx() depends on this */ + error = mii_phy_probe(ue->ue_dev, &ue->ue_miibus, + ue_ifmedia_upd, ue->ue_methods->ue_mii_sts); + mtx_unlock(&Giant); + if (error) { + device_printf(ue->ue_dev, "MII without any PHY\n"); + goto error; + } + } + + if_printf(ifp, " on %s\n", device_get_nameunit(ue->ue_dev)); + ether_ifattach(ifp, ue->ue_eaddr); + + snprintf(num, sizeof(num), "%u", ue->ue_unit); + ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx, + &SYSCTL_NODE_CHILDREN(_net, ue), + OID_AUTO, num, CTLFLAG_RD, NULL, ""); + SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx, + SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, + "%parent", CTLFLAG_RD, ue, 0, + ue_sysctl_parent, "A", "parent device"); + + UE_LOCK(ue); + return; + +error: + free_unr(ueunit, ue->ue_unit); + if (ue->ue_ifp != NULL) { + if_free(ue->ue_ifp); + ue->ue_ifp = NULL; + } + UE_LOCK(ue); + return; +} + +void +usb2_ether_ifdetach(struct usb2_ether *ue) +{ + struct ifnet *ifp; + + /* wait for any post attach or other command to complete */ + usb2_proc_drain(&ue->ue_tq); + + /* read "ifnet" pointer after taskqueue drain */ + ifp = ue->ue_ifp; + + if (ifp != NULL) { + + /* we are not running any more */ + UE_LOCK(ue); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + UE_UNLOCK(ue); + + /* drain any callouts */ + usb2_callout_drain(&ue->ue_watchdog); + + /* detach miibus */ + if (ue->ue_miibus != NULL) { + mtx_lock(&Giant); /* device_xxx() depends on this */ + device_delete_child(ue->ue_dev, ue->ue_miibus); + mtx_unlock(&Giant); + } + + /* detach ethernet */ + ether_ifdetach(ifp); + + /* free interface instance */ + if_free(ifp); + + /* free sysctl */ + sysctl_ctx_free(&ue->ue_sysctl_ctx); + + /* free unit */ + free_unr(ueunit, ue->ue_unit); + } + + /* free taskqueue, if any */ + usb2_proc_free(&ue->ue_tq); +} + +void +usb2_ether_ifshutdown(struct usb2_ether *ue) +{ + struct ifnet *ifp = ue->ue_ifp; + + UE_LOCK(ue); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ue_queue_command(ue, ue_stop_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); +} + +uint8_t +usb2_ether_is_gone(struct usb2_ether *ue) +{ + return (usb2_proc_is_gone(&ue->ue_tq)); +} + +static void +ue_init(void *arg) +{ + struct usb2_ether *ue = arg; + + UE_LOCK(ue); + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); +} + +static void +ue_start_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + ue->ue_methods->ue_init(ue); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + if (ue->ue_methods->ue_tick != NULL) + usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_stop_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + usb2_callout_stop(&ue->ue_watchdog); + + ue->ue_methods->ue_stop(ue); +} + +static void +ue_start(struct ifnet *ifp) +{ + struct usb2_ether *ue = ifp->if_softc; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + UE_LOCK(ue); + ue->ue_methods->ue_start(ue); + UE_UNLOCK(ue); +} + +static void +ue_promisc_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + + ue->ue_methods->ue_setpromisc(ue); +} + +static void +ue_setmulti_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + + ue->ue_methods->ue_setmulti(ue); +} + +static int +ue_ifmedia_upd(struct ifnet *ifp) +{ + struct usb2_ether *ue = ifp->if_softc; + + /* Defer to process context */ + UE_LOCK(ue); + ue_queue_command(ue, ue_ifmedia_task, + &ue->ue_media_task[0].hdr, + &ue->ue_media_task[1].hdr); + UE_UNLOCK(ue); + + return (0); +} + +static void +ue_ifmedia_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + ue->ue_methods->ue_mii_upd(ifp); +} + +static void +ue_watchdog(void *arg) +{ + struct usb2_ether *ue = arg; + struct ifnet *ifp = ue->ue_ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + ue_queue_command(ue, ue_tick_task, + &ue->ue_tick_task[0].hdr, + &ue->ue_tick_task[1].hdr); + + usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_tick_task(struct usb2_proc_msg *_task) +{ + struct usb2_ether_cfg_task *task = + (struct usb2_ether_cfg_task *)_task; + struct usb2_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + ue->ue_methods->ue_tick(ue); +} + +int +usb2_ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct usb2_ether *ue = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + UE_LOCK(ue); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ue_queue_command(ue, ue_promisc_task, + &ue->ue_promisc_task[0].hdr, + &ue->ue_promisc_task[1].hdr); + else + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } else { + ue_queue_command(ue, ue_stop_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } + UE_UNLOCK(ue); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + UE_LOCK(ue); + ue_queue_command(ue, ue_setmulti_task, + &ue->ue_multi_task[0].hdr, + &ue->ue_multi_task[1].hdr); + UE_UNLOCK(ue); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (ue->ue_miibus != NULL) { + mii = device_get_softc(ue->ue_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + } else + error = ether_ioctl(ifp, command, data); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static int +usb2_ether_modevent(module_t mod, int type, void *data) +{ + + switch (type) { + case MOD_LOAD: + ueunit = new_unrhdr(0, INT_MAX, NULL); + break; + case MOD_UNLOAD: + break; + default: + return (EOPNOTSUPP); + } + return (0); +} +static moduledata_t usb2_ether_mod = { + "uether", + usb2_ether_modevent, + 0 +}; + +int +usb2_ether_rxmbuf(struct usb2_ether *ue, struct mbuf *m, + unsigned int len) +{ + struct ifnet *ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + /* finalize mbuf */ + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + _IF_ENQUEUE(&ue->ue_rxq, m); + return (0); +} + +int +usb2_ether_rxbuf(struct usb2_ether *ue, struct usb2_page_cache *pc, + unsigned int offset, unsigned int len) +{ + struct ifnet *ifp = ue->ue_ifp; + struct mbuf *m; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (len < ETHER_HDR_LEN || len > MCLBYTES) + return (1); + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + ifp->if_ierrors++; + return (ENOMEM); + } + + m_adj(m, ETHER_ALIGN); + usb2_copy_out(pc, offset, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + _IF_ENQUEUE(&ue->ue_rxq, m); + return (0); +} + +void +usb2_ether_rxflush(struct usb2_ether *ue) +{ + struct ifnet *ifp = ue->ue_ifp; + struct mbuf *m; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + for (;;) { + _IF_DEQUEUE(&ue->ue_rxq, m); + if (m == NULL) + break; + + /* + * The USB xfer has been resubmitted so its safe to unlock now. + */ + UE_UNLOCK(ue); + ifp->if_input(ifp, m); + UE_LOCK(ue); + } +} + +DECLARE_MODULE(uether, usb2_ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_VERSION(uether, 1); diff --git a/sys/dev/usb/net/usb_ethernet.h b/sys/dev/usb/net/usb_ethernet.h new file mode 100644 index 0000000..0ee36f2 --- /dev/null +++ b/sys/dev/usb/net/usb_ethernet.h @@ -0,0 +1,122 @@ +/* $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 + +#include "miibus_if.h" + +#include +#include + +struct usb2_ether; +struct usb2_device_request; + +typedef void (usb2_ether_fn_t)(struct usb2_ether *); + +struct usb2_ether_methods { + usb2_ether_fn_t *ue_attach_post; + usb2_ether_fn_t *ue_start; + usb2_ether_fn_t *ue_init; + usb2_ether_fn_t *ue_stop; + usb2_ether_fn_t *ue_setmulti; + usb2_ether_fn_t *ue_setpromisc; + usb2_ether_fn_t *ue_tick; + int (*ue_mii_upd)(struct ifnet *); + void (*ue_mii_sts)(struct ifnet *, + struct ifmediareq *); + int (*ue_ioctl)(struct ifnet *, u_long, caddr_t); + +}; + +struct usb2_ether_cfg_task { + struct usb2_proc_msg hdr; + struct usb2_ether *ue; +}; + +struct usb2_ether { + /* NOTE: the "ue_ifp" pointer must be first --hps */ + struct ifnet *ue_ifp; + struct mtx *ue_mtx; + const struct usb2_ether_methods *ue_methods; + struct sysctl_oid *ue_sysctl_oid; + void *ue_sc; + struct usb2_device *ue_udev; /* used by usb2_ether_do_request() */ + device_t ue_dev; + device_t ue_miibus; + + struct usb2_process ue_tq; + struct sysctl_ctx_list ue_sysctl_ctx; + struct ifqueue ue_rxq; + struct usb2_callout ue_watchdog; + struct usb2_ether_cfg_task ue_sync_task[2]; + struct usb2_ether_cfg_task ue_media_task[2]; + struct usb2_ether_cfg_task ue_multi_task[2]; + struct usb2_ether_cfg_task ue_promisc_task[2]; + struct usb2_ether_cfg_task ue_tick_task[2]; + + int ue_unit; + + /* ethernet address from eeprom */ + uint8_t ue_eaddr[ETHER_ADDR_LEN]; +}; + +#define usb2_ether_do_request(ue,req,data,timo) \ + usb2_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo) + +uint8_t usb2_ether_pause(struct usb2_ether *, unsigned int); +struct ifnet *usb2_ether_getifp(struct usb2_ether *); +struct mii_data *usb2_ether_getmii(struct usb2_ether *); +void *usb2_ether_getsc(struct usb2_ether *); +int usb2_ether_ifattach(struct usb2_ether *); +void usb2_ether_ifdetach(struct usb2_ether *); +int usb2_ether_ioctl(struct ifnet *, u_long, caddr_t); +int usb2_ether_rxmbuf(struct usb2_ether *, struct mbuf *, + unsigned int); +int usb2_ether_rxbuf(struct usb2_ether *, + struct usb2_page_cache *, + unsigned int, unsigned int); +void usb2_ether_rxflush(struct usb2_ether *); +void usb2_ether_ifshutdown(struct usb2_ether *); +uint8_t usb2_ether_is_gone(struct usb2_ether *); +#endif /* _USB2_ETHERNET_H_ */ diff --git a/sys/dev/usb/quirk/usb_quirk.c b/sys/dev/usb/quirk/usb_quirk.c new file mode 100644 index 0000000..fe49ec7 --- /dev/null +++ b/sys/dev/usb/quirk/usb_quirk.c @@ -0,0 +1,397 @@ +/* $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 "usbdevs.h" + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include + +#include + +MODULE_DEPEND(usb_quirk, usb, 1, 1, 1); +MODULE_VERSION(usb_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_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD4X20, 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_MICROSOFT, USB_PRODUCT_MICROSOFT_COMFORT3000, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, 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)}, +}; + +static const char *usb_quirk_str[USB_QUIRK_MAX] = { + [UQ_NONE] = "UQ_NONE", + [UQ_AUDIO_SWAP_LR] = "UQ_AUDIO_SWAP_LR", + [UQ_AU_INP_ASYNC] = "UQ_AU_INP_ASYNC", + [UQ_AU_NO_FRAC] = "UQ_AU_NO_FRAC", + [UQ_AU_NO_XU] = "UQ_AU_NO_XU", + [UQ_BAD_ADC] = "UQ_BAD_ADC", + [UQ_BAD_AUDIO] = "UQ_BAD_AUDIO", + [UQ_BROKEN_BIDIR] = "UQ_BROKEN_BIDIR", + [UQ_BUS_POWERED] = "UQ_BUS_POWERED", + [UQ_HID_IGNORE] = "UQ_HID_IGNORE", + [UQ_KBD_IGNORE] = "UQ_KBD_IGNORE", + [UQ_MS_BAD_CLASS] = "UQ_MS_BAD_CLASS", + [UQ_MS_LEADING_BYTE] = "UQ_MS_LEADING_BYTE", + [UQ_MS_REVZ] = "UQ_MS_REVZ", + [UQ_NO_STRINGS] = "UQ_NO_STRINGS", + [UQ_OPEN_CLEARSTALL] = "UQ_OPEN_CLEARSTALL", + [UQ_POWER_CLAIM] = "UQ_POWER_CLAIM", + [UQ_SPUR_BUT_UP] = "UQ_SPUR_BUT_UP", + [UQ_SWAP_UNICODE] = "UQ_SWAP_UNICODE", + [UQ_CFG_INDEX_1] = "UQ_CFG_INDEX_1", + [UQ_CFG_INDEX_2] = "UQ_CFG_INDEX_2", + [UQ_CFG_INDEX_3] = "UQ_CFG_INDEX_3", + [UQ_CFG_INDEX_4] = "UQ_CFG_INDEX_4", + [UQ_CFG_INDEX_0] = "UQ_CFG_INDEX_0", +}; + +/*------------------------------------------------------------------------* + * 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_str[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; +} + +static void +usb2_quirk_uninit(void *arg) +{ + usb2_quirk_unload(arg); + + /* destroy mutex */ + mtx_destroy(&usb2_quirk_mtx); +} + +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/usb/quirk/usb_quirk.h b/sys/dev/usb/quirk/usb_quirk.h new file mode 100644 index 0000000..c9223e8 --- /dev/null +++ b/sys/dev/usb/quirk/usb_quirk.h @@ -0,0 +1,59 @@ +/* $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 */ +enum { /* keep in sync with usb_quirk_str table */ + UQ_NONE, + UQ_AUDIO_SWAP_LR, /* left and right sound channels are swapped */ + UQ_AU_INP_ASYNC, /* input is async despite claim of adaptive */ + UQ_AU_NO_FRAC, /* don't adjust for fractional samples */ + UQ_AU_NO_XU, /* audio device has broken extension unit */ + UQ_BAD_ADC, /* bad audio spec version number */ + UQ_BAD_AUDIO, /* device claims audio class, but isn't */ + UQ_BROKEN_BIDIR, /* printer has broken bidir mode */ + UQ_BUS_POWERED, /* device is bus powered, despite claim */ + UQ_HID_IGNORE, /* device should be ignored by hid class */ + UQ_KBD_IGNORE, /* device should be ignored by kbd class */ + UQ_MS_BAD_CLASS, /* doesn't identify properly */ + UQ_MS_LEADING_BYTE, /* mouse sends an unknown leading byte */ + UQ_MS_REVZ, /* mouse has Z-axis reversed */ + UQ_NO_STRINGS, /* string descriptors are broken */ + UQ_OPEN_CLEARSTALL, /* device needs clear endpoint stall */ + UQ_POWER_CLAIM, /* hub lies about power status */ + UQ_SPUR_BUT_UP, /* spurious mouse button up events */ + UQ_SWAP_UNICODE, /* has some Unicode strings swapped */ + UQ_CFG_INDEX_1, /* select configuration index 1 by default */ + UQ_CFG_INDEX_2, /* select configuration index 2 by default */ + UQ_CFG_INDEX_3, /* select configuration index 3 by default */ + UQ_CFG_INDEX_4, /* select configuration index 4 by default */ + UQ_CFG_INDEX_0, /* select configuration index 0 by default */ + USB_QUIRK_MAX +}; + +#endif /* _USB2_QUIRK_H_ */ diff --git a/sys/dev/usb/serial/u3g.c b/sys/dev/usb/serial/u3g.c new file mode 100644 index 0000000..ce963d5 --- /dev/null +++ b/sys/dev/usb/serial/u3g.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2008 AnyWi Technologies + * Author: Andrea Guzzo + * * based on uark.c 1.1 2006/08/14 08:30:22 jsg * + * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk * + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ + +/* + * NOTE: + * + * - The detour through the tty layer is ridiculously expensive wrt + * buffering due to the high speeds. + * + * We should consider adding a simple r/w device which allows + * attaching of PPP in a more efficient way. + * + * NOTE: + * + * - The device ID's are stored in "core/usb2_msctest.c" + */ + +#include "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR u3g_debug + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int u3g_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB u3g"); +SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW, + &u3g_debug, 0, "u3g debug level"); +#endif + +#define U3G_MAXPORTS 4 +#define U3G_CONFIG_INDEX 0 +#define U3G_BSIZE 2048 + +#define U3GSP_GPRS 0 +#define U3GSP_EDGE 1 +#define U3GSP_CDMA 2 +#define U3GSP_UMTS 3 +#define U3GSP_HSDPA 4 +#define U3GSP_HSUPA 5 +#define U3GSP_HSPA 6 +#define U3GSP_MAX 7 + +#define U3GFL_NONE 0x00 /* No flags */ +#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */ +#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */ +#define U3GFL_SIERRA_INIT 0x04 /* Init command required */ + +struct u3g_speeds_s { + uint32_t ispeed; + uint32_t ospeed; +}; + +enum { + U3G_BULK_WR, + U3G_BULK_RD, + U3G_N_TRANSFER, +}; + +struct u3g_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom[U3G_MAXPORTS]; + + struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* U3G status register */ + uint8_t sc_numports; +}; + +static device_probe_t u3g_probe; +static device_attach_t u3g_attach; +static device_detach_t u3g_detach; + +static usb2_callback_t u3g_write_callback; +static usb2_callback_t u3g_read_callback; + +static void u3g_start_read(struct usb2_com_softc *ucom); +static void u3g_stop_read(struct usb2_com_softc *ucom); +static void u3g_start_write(struct usb2_com_softc *ucom); +static void u3g_stop_write(struct usb2_com_softc *ucom); + +static int u3g_driver_loaded(struct module *mod, int what, void *arg); + +static const struct usb2_config u3g_config[U3G_N_TRANSFER] = { + + [U3G_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = U3G_BSIZE,/* bytes */ + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &u3g_write_callback, + }, + + [U3G_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = U3G_BSIZE,/* bytes */ + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &u3g_read_callback, + }, +}; + +static const struct usb2_com_callback u3g_callback = { + .usb2_com_start_read = &u3g_start_read, + .usb2_com_stop_read = &u3g_stop_read, + .usb2_com_start_write = &u3g_start_write, + .usb2_com_stop_write = &u3g_stop_write, +}; + +#if 0 +static const struct u3g_speeds_s u3g_speeds[U3GSP_MAX] = { + [U3GSP_GPRS] = {64000, 64000}, + [U3GSP_EDGE] = {384000, 64000}, + [U3GSP_CDMA] = {384000, 64000}, + [U3GSP_UMTS] = {384000, 64000}, + [U3GSP_HSDPA] = {1200000, 384000}, + [U3GSP_HSUPA] = {1200000, 384000}, + [U3GSP_HSPA] = {7200000, 384000}, +}; +#endif + +static device_method_t u3g_methods[] = { + DEVMETHOD(device_probe, u3g_probe), + DEVMETHOD(device_attach, u3g_attach), + DEVMETHOD(device_detach, u3g_detach), + {0, 0} +}; + +static devclass_t u3g_devclass; + +static driver_t u3g_driver = { + .name = "u3g", + .methods = u3g_methods, + .size = sizeof(struct u3g_softc), +}; + +DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0); +MODULE_DEPEND(u3g, ucom, 1, 1, 1); +MODULE_DEPEND(u3g, usb, 1, 1, 1); + +/* Huawei specific defines */ + +#define U3GINFO(flag,speed) ((flag)|((speed) * 256)) +#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256) + +/* + * NOTE: The entries marked with XXX should be checked for the correct + * speed indication to set the buffer sizes. + */ +static const struct usb2_device_id u3g_devs[] = { + /* OEM: Option */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + /* OEM: Qualcomm, Inc. */ + {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + /* OEM: Huawei */ + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, + /* OEM: Novatel */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + /* OEM: Merlin */ + {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + /* OEM: Sierra Wireless: */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */ + /* Sierra TruInstaller device ID */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, +}; + +static void +u3g_sierra_init(struct usb2_device *udev) +{ + struct usb2_device_request req; + + DPRINTFN(0, "\n"); + + req.bmRequestType = UT_VENDOR; + req.bRequest = UR_SET_INTERFACE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_CONNECTION); + USETW(req.wLength, 0); + + if (usb2_do_request_flags(udev, NULL, &req, + NULL, 0, NULL, USB_MS_HZ)) { + /* ignore any errors */ + } + return; +} + +static void +u3g_huawei_init(struct usb2_device *udev) +{ + struct usb2_device_request req; + + DPRINTFN(0, "\n"); + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_SUSPEND); + USETW(req.wLength, 0); + + if (usb2_do_request_flags(udev, NULL, &req, + NULL, 0, NULL, USB_MS_HZ)) { + /* ignore any errors */ + } + return; +} + +static int +u3g_lookup_huawei(struct usb2_attach_arg *uaa) +{ + /* Calling the lookup function will also set the driver info! */ + return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); +} + +/* + * The following function handles 3G modem devices (E220, Mobile, + * etc.) with auto-install flash disks for Windows/MacOSX on the first + * interface. After some command or some delay they change appearance + * to a modem. + */ +static usb2_error_t +u3g_test_huawei_autoinst(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + uint32_t flags; + + if (udev == NULL) { + return (USB_ERR_INVAL); + } + iface = usb2_get_iface(udev, 0); + 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); + } + if (u3g_lookup_huawei(uaa)) { + /* no device match */ + return (USB_ERR_INVAL); + } + flags = USB_GET_DRIVER_INFO(uaa); + + if (flags & U3GFL_HUAWEI_INIT) { + u3g_huawei_init(udev); + } else if (flags & U3GFL_SCSI_EJECT) { + return (usb2_test_autoinstall(udev, 0, 1)); + } else if (flags & U3GFL_SIERRA_INIT) { + u3g_sierra_init(udev); + } else { + /* no quirks */ + return (USB_ERR_INVAL); + } + return (0); /* success */ +} + +static int +u3g_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + /* register our autoinstall handler */ + usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst; + break; + case MOD_UNLOAD: + usb2_test_huawei_unload(NULL); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static int +u3g_probe(device_t self) +{ + struct usb2_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb2_mode != USB_MODE_HOST) { + return (ENXIO); + } + if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bInterfaceClass != UICLASS_VENDOR) { + return (ENXIO); + } + return (u3g_lookup_huawei(uaa)); +} + +static int +u3g_attach(device_t dev) +{ + struct usb2_config u3g_config_tmp[U3G_N_TRANSFER]; + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct u3g_softc *sc = device_get_softc(dev); + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + uint8_t m; + uint8_t n; + uint8_t i; + uint8_t x; + int error; + + DPRINTF("sc=%p\n", sc); + + /* copy in USB config */ + for (n = 0; n != U3G_N_TRANSFER; n++) + u3g_config_tmp[n] = u3g_config[n]; + + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + x = 0; /* interface index */ + i = 0; /* endpoint index */ + m = 0; /* number of ports */ + + while (m != U3G_MAXPORTS) { + + /* update BULK endpoint index */ + for (n = 0; n != U3G_N_TRANSFER; n++) + u3g_config_tmp[n].ep_index = i; + + iface = usb2_get_iface(uaa->device, x); + if (iface == NULL) { + if (m != 0) + break; /* end of interfaces */ + DPRINTF("did not find any modem endpoints\n"); + goto detach; + } + + id = usb2_get_interface_descriptor(iface); + if ((id == NULL) || + (id->bInterfaceClass != UICLASS_VENDOR)) { + /* next interface */ + x++; + i = 0; + continue; + } + + /* try to allocate a set of BULK endpoints */ + error = usb2_transfer_setup(uaa->device, &x, + sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER, + &sc->sc_ucom[m], &Giant); + if (error) { + /* next interface */ + x++; + i = 0; + continue; + } + + /* grab other interface, if any */ + if (x != uaa->info.bIfaceIndex) + usb2_set_parent_iface(uaa->device, x, + uaa->info.bIfaceIndex); + + /* set stall by default */ + usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]); + usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]); + + m++; /* found one port */ + i++; /* next endpoint index */ + } + + sc->sc_numports = m; + + error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, + sc->sc_numports, sc, &u3g_callback, &Giant); + if (error) { + DPRINTF("usb2_com_attach failed\n"); + goto detach; + } + if (sc->sc_numports != 1) { + /* be verbose */ + device_printf(dev, "Found %u ports.\n", + (unsigned int)sc->sc_numports); + } + return (0); + +detach: + u3g_detach(dev); + return (ENXIO); +} + +static int +u3g_detach(device_t dev) +{ + struct u3g_softc *sc = device_get_softc(dev); + uint8_t m; + + DPRINTF("sc=%p\n", sc); + + /* NOTE: It is not dangerous to detach more ports than attached! */ + usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS); + + for (m = 0; m != U3G_MAXPORTS; m++) + usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER); + + return (0); +} + +static void +u3g_start_read(struct usb2_com_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); + return; +} + +static void +u3g_stop_read(struct usb2_com_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); + return; +} + +static void +u3g_start_write(struct usb2_com_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); + return; +} + +static void +u3g_stop_write(struct usb2_com_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); + return; +} + +static void +u3g_write_callback(struct usb2_xfer *xfer) +{ + struct usb2_com_softc *ucom = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +tr_setup: + if (usb2_com_get_data(ucom, xfer->frbuffers, 0, + U3G_BSIZE, &actlen)) { + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* do a builtin clear-stall */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} + +static void +u3g_read_callback(struct usb2_xfer *xfer) +{ + struct usb2_com_softc *ucom = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen); + + 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) { + /* do a builtin clear-stall */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } + return; +} diff --git a/sys/dev/usb/serial/uark.c b/sys/dev/usb/serial/uark.c new file mode 100644 index 0000000..ce76854 --- /dev/null +++ b/sys/dev/usb/serial/uark.c @@ -0,0 +1,407 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include + +#include + +#define UARK_BUF_SIZE 1024 /* bytes */ + +#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 + +enum { + UARK_BULK_DT_WR, + UARK_BULK_DT_RD, + UARK_N_TRANSFER, +}; + +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_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_read_callback; + +static void uark_start_read(struct usb2_com_softc *); +static void uark_stop_read(struct usb2_com_softc *); +static void uark_start_write(struct usb2_com_softc *); +static void uark_stop_write(struct usb2_com_softc *); +static int uark_pre_param(struct usb2_com_softc *, struct termios *); +static void uark_cfg_param(struct usb2_com_softc *, struct termios *); +static void uark_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void uark_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void uark_cfg_write(struct uark_softc *, uint16_t, uint16_t); + +static const struct usb2_config + uark_xfer_config[UARK_N_TRANSFER] = { + + [UARK_BULK_DT_WR] = { + .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, + }, + + [UARK_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(uark, usb, 1, 1, 1); + +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; + + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]); + + 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +uark_start_read(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]); +} + +static void +uark_stop_read(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]); +} + +static void +uark_start_write(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]); +} + +static void +uark_stop_write(struct usb2_com_softc *ucom) +{ + struct uark_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]); +} + +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); +} + +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; +} + +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); +} + +static void +uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + + req.bmRequestType = UARK_WRITE; + req.bRequest = UARK_REQUEST; + USETW(req.wValue, value); + USETW(req.wIndex, index); + USETW(req.wLength, 0); + + err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +} diff --git a/sys/dev/usb/serial/ubsa.c b/sys/dev/usb/serial/ubsa.c new file mode 100644 index 0000000..718df6d --- /dev/null +++ b/sys/dev/usb/serial/ubsa.c @@ -0,0 +1,634 @@ +/*- + * 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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR ubsa_debug + +#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_BSIZE 1024 /* bytes */ + +#define UBSA_CONFIG_INDEX 0 +#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 */ + +enum { + UBSA_BULK_DT_WR, + UBSA_BULK_DT_RD, + UBSA_INTR_DT_RD, + UBSA_N_TRANSFER, +}; + +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; + + 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_read_callback; +static usb2_callback_t ubsa_intr_callback; + +static void ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t); +static void ubsa_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void ubsa_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void ubsa_cfg_set_break(struct usb2_com_softc *, uint8_t); +static int ubsa_pre_param(struct usb2_com_softc *, struct termios *); +static void ubsa_cfg_param(struct usb2_com_softc *, struct termios *); +static void ubsa_start_read(struct usb2_com_softc *); +static void ubsa_stop_read(struct usb2_com_softc *); +static void ubsa_start_write(struct usb2_com_softc *); +static void ubsa_stop_write(struct usb2_com_softc *); +static void ubsa_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); + +static const struct usb2_config ubsa_config[UBSA_N_TRANSFER] = { + + [UBSA_BULK_DT_WR] = { + .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, + }, + + [UBSA_BULK_DT_RD] = { + .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, + }, + + [UBSA_INTR_DT_RD] = { + .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, + }, +}; + +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)}, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(ubsa, usb, 1, 1, 1); + +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); + + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]); + + 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; + + 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +} + +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); +} + +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); +} + +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); +} + +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); +} + +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[UBSA_INTR_DT_RD]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]); +} + +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[UBSA_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]); +} + +static void +ubsa_start_write(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]); +} + +static void +ubsa_stop_write(struct usb2_com_softc *ucom) +{ + struct ubsa_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]); +} + +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; +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + + } +} diff --git a/sys/dev/usb/serial/ubser.c b/sys/dev/usb/serial/ubser.c new file mode 100644 index 0000000..1de4f82 --- /dev/null +++ b/sys/dev/usb/serial/ubser.c @@ -0,0 +1,518 @@ +/*- + * 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 + +#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 + +enum { + UBSER_BULK_DT_WR, + UBSER_BULK_DT_RD, + UBSER_N_TRANSFER, +}; + +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_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_tx_size; + + uint8_t sc_numser; + 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_callback; +static usb2_callback_t ubser_read_callback; + +static int ubser_pre_param(struct usb2_com_softc *, struct termios *); +static void ubser_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void ubser_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void ubser_start_read(struct usb2_com_softc *); +static void ubser_stop_read(struct usb2_com_softc *); +static void ubser_start_write(struct usb2_com_softc *); +static void ubser_stop_write(struct usb2_com_softc *); + +static const struct usb2_config ubser_config[UBSER_N_TRANSFER] = { + + [UBSER_BULK_DT_WR] = { + .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_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(ubser, usb, 1, 1, 1); + +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; + + 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_N_TRANSFER, sc, &Giant); + if (error) { + goto detach; + } + sc->sc_tx_size = sc->sc_xfer[UBSER_BULK_DT_WR]->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); + + usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]); + + usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); + + 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); + + DPRINTF("\n"); + + usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser); + + usb2_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER); + + 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; + } +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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_com_cfg_do_request(sc->sc_udev, ucom, + &req, NULL, 0, 1000); + if (err) { + DPRINTFN(0, "send break failed, error=%s\n", + usb2_errstr(err)); + } + } +} + +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; +} + +static void +ubser_start_read(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); +} + +static void +ubser_stop_read(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]); +} + +static void +ubser_start_write(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]); +} + +static void +ubser_stop_write(struct usb2_com_softc *ucom) +{ + struct ubser_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]); +} diff --git a/sys/dev/usb/serial/uchcom.c b/sys/dev/usb/serial/uchcom.c new file mode 100644 index 0000000..c8238c4 --- /dev/null +++ b/sys/dev/usb/serial/uchcom.c @@ -0,0 +1,883 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uchcom_debug + +#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 */ + +enum { + UCHCOM_BULK_DT_WR, + UCHCOM_BULK_DT_RD, + UCHCOM_INTR_DT_RD, + UCHCOM_N_TRANSFER, +}; + +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 */ +}; + +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_pre_param(struct usb2_com_softc *, struct termios *); +static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *); +static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void uchcom_start_read(struct usb2_com_softc *); +static void uchcom_start_write(struct usb2_com_softc *); +static void uchcom_stop_read(struct usb2_com_softc *); +static void uchcom_stop_write(struct usb2_com_softc *); +static void uchcom_update_version(struct uchcom_softc *); +static void uchcom_convert_status(struct uchcom_softc *, uint8_t); +static void uchcom_update_status(struct uchcom_softc *); +static void uchcom_set_dtrrts(struct uchcom_softc *); +static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); +static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); +static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t); +static void uchcom_clear_chip(struct uchcom_softc *); +static void uchcom_reset_chip(struct uchcom_softc *); + +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_write_callback; +static usb2_callback_t uchcom_read_callback; + +static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { + + [UCHCOM_BULK_DT_WR] = { + .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, + }, + + [UCHCOM_BULK_DT_RD] = { + .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, + }, + + [UCHCOM_INTR_DT_RD] = { + .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, + }, +}; + +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_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"); + + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); + + 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_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); + + usb2_com_cfg_do_request(sc->sc_udev, + &sc->sc_ucom, &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, + &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); +} + +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)); +} + +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]; +} + +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]; +} + +static void +uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) +{ + uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); +} + +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); +} + +static void +uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) +{ + uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); +} + + +/* ---------------------------------------------------------------------- + * middle layer + */ + +static void +uchcom_update_version(struct uchcom_softc *sc) +{ + uchcom_get_version(sc, &sc->sc_version); +} + +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); +} + + +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); +} + +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); +} + +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); +} + +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); +} + +static void +uchcom_clear_chip(struct uchcom_softc *sc) +{ + DPRINTF("\n"); + uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); +} + +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); +} + +/* ---------------------------------------------------------------------- + * 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; +} + +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); +} + +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); +} + +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); +} + +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[UCHCOM_INTR_DT_RD]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); +} + +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[UCHCOM_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); +} + +static void +uchcom_start_write(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); +} + +static void +uchcom_stop_write(struct usb2_com_softc *ucom) +{ + struct uchcom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); +} + +/* ---------------------------------------------------------------------- + * 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: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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, ucom, 1, 1, 1); +MODULE_DEPEND(uchcom, usb, 1, 1, 1); diff --git a/sys/dev/usb/serial/ucycom.c b/sys/dev/usb/serial/ucycom.c new file mode 100644 index 0000000..cabb7fb --- /dev/null +++ b/sys/dev/usb/serial/ucycom.c @@ -0,0 +1,564 @@ +#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 "usbdevs.h" +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ + +#define UCYCOM_IFACE_INDEX 0 + +enum { + UCYCOM_CTRL_RD, + UCYCOM_INTR_RD, + UCYCOM_N_TRANSFER, +}; + +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_N_TRANSFER]; + + 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_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_callback; + +static void ucycom_cfg_open(struct usb2_com_softc *); +static void ucycom_start_read(struct usb2_com_softc *); +static void ucycom_stop_read(struct usb2_com_softc *); +static void ucycom_start_write(struct usb2_com_softc *); +static void ucycom_stop_write(struct usb2_com_softc *); +static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); +static int ucycom_pre_param(struct usb2_com_softc *, struct termios *); +static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *); + +static const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = { + + [UCYCOM_CTRL_RD] = { + .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 */ + }, + + [UCYCOM_INTR_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(ucycom, usb, 1, 1, 1); + +/* + * 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; + + 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_N_TRANSFER, + 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_N_TRANSFER); + + 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); +} + +static void +ucycom_start_read(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); +} + +static void +ucycom_stop_read(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); +} + +static void +ucycom_start_write(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); +} + +static void +ucycom_stop_write(struct usb2_com_softc *ucom) +{ + struct ucycom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); +} + +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; + + err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, sc->sc_temp_cfg, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +} + +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); +} + +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: + xfer->frlengths[0] = sc->sc_ilen; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + + } +} diff --git a/sys/dev/usb/serial/ufoma.c b/sys/dev/usb/serial/ufoma.c new file mode 100644 index 0000000..1077676 --- /dev/null +++ b/sys/dev/usb/serial/ufoma.c @@ -0,0 +1,1212 @@ +/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ + +#include +__FBSDID("$FreeBSD$"); +#define UFOMA_HANDSFREE +/*- + * 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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#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 */ + +enum { + UFOMA_CTRL_ENDPT_INTR, + UFOMA_CTRL_ENDPT_READ, + UFOMA_CTRL_ENDPT_WRITE, + UFOMA_CTRL_ENDPT_MAX, +}; + +enum { + UFOMA_BULK_ENDPT_WRITE, + UFOMA_BULK_ENDPT_READ, + UFOMA_BULK_ENDPT_MAX, +}; + +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_nobulk; + 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_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_callback; +static usb2_callback_t ufoma_bulk_write_callback; +static usb2_callback_t ufoma_bulk_read_callback; + +static void *ufoma_get_intconf(struct usb2_config_descriptor *, + struct usb2_interface_descriptor *, uint8_t, uint8_t); +static void ufoma_cfg_link_state(struct ufoma_softc *); +static void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t); +static void ufoma_cfg_open(struct usb2_com_softc *); +static void ufoma_cfg_close(struct usb2_com_softc *); +static void ufoma_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void ufoma_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void ufoma_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void ufoma_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static int ufoma_pre_param(struct usb2_com_softc *, struct termios *); +static void ufoma_cfg_param(struct usb2_com_softc *, struct termios *); +static int ufoma_modem_setup(device_t, struct ufoma_softc *, + struct usb2_attach_arg *); +static void ufoma_start_read(struct usb2_com_softc *); +static void ufoma_stop_read(struct usb2_com_softc *); +static void ufoma_start_write(struct usb2_com_softc *); +static void ufoma_stop_write(struct usb2_com_softc *); + +/*sysctl stuff*/ +static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS); +static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS); +static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS); + + +static const struct usb2_config + ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { + + [UFOMA_CTRL_ENDPT_INTR] = { + .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, + }, + + [UFOMA_CTRL_ENDPT_READ] = { + .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 */ + }, + + [UFOMA_CTRL_ENDPT_WRITE] = { + .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] = { + + [UFOMA_BULK_ENDPT_WRITE] = { + .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, + }, + + [UFOMA_BULK_ENDPT_READ] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(ufoma, usb, 1, 1, 1); + +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; + struct sysctl_ctx_list *sctx; + struct sysctl_oid *soid; + + usb2_mcpc_acm_descriptor *mad; + uint8_t elements; + int32_t error; + + 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_nobulk = 1; + } else { + sc->sc_nobulk = 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, if any */ + usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); + usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); + + 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; + } + /*Sysctls*/ + sctx = device_get_sysctl_ctx(dev); + soid = device_get_sysctl_tree(dev); + + SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode", + CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support, + "A", "Supporting port role"); + + SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode", + CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current, + "A", "Current port role"); + + SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode", + CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open, + "A", "Mode to transit when port is opened"); + SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit", + CTLFLAG_RD, &(sc->sc_ucom.sc_unit), 0, + "Unit number as USB serial"); + + 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_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]); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, sc->sc_modetable, 0, 1000); + + error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz); + + if (error) { + DPRINTF("NO response\n"); + } +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + + error = usb2_cv_timedwait(&sc->sc_cv, &Giant, + (UFOMA_MAX_TIMEOUT * hz)); + if (error) { + DPRINTF("No response\n"); + } +} + +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_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_nobulk)) { + DPRINTF("Wrong serial state!\n"); + break; + } + if (sc->sc_num_msg != 0xFF) { + sc->sc_num_msg++; + } + usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); + break; + + case UCDC_N_SERIAL_STATE: + if (sc->sc_nobulk) { + 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: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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); + } +} + +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); +} + +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_nobulk || + (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { + 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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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; +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +static void +ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_nobulk) { + return; + } + if (onoff) + sc->sc_line |= UCDC_LINE_DTR; + else + sc->sc_line &= ~UCDC_LINE_DTR; + + ufoma_cfg_set_line_state(sc); +} + +static void +ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_nobulk) { + return; + } + if (onoff) + sc->sc_line |= UCDC_LINE_RTS; + else + sc->sc_line &= ~UCDC_LINE_RTS; + + ufoma_cfg_set_line_state(sc); +} + +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_nobulk || + (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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &ls, 0, 1000); +} + +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[UFOMA_CTRL_ENDPT_INTR]); + + /* start data transfer */ + if (sc->sc_nobulk) { + usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); + } else { + usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); + } +} + +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[UFOMA_CTRL_ENDPT_INTR]); + + /* stop data transfer */ + if (sc->sc_nobulk) { + usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); + } else { + usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); + } +} + +static void +ufoma_start_write(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_nobulk) { + usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); + } else { + usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); + } +} + +static void +ufoma_stop_write(struct usb2_com_softc *ucom) +{ + struct ufoma_softc *sc = ucom->sc_parent; + + if (sc->sc_nobulk) { + usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); + } else { + usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); + } +} + +struct umcpc_modetostr_tab{ + int mode; + char *str; +}umcpc_modetostr_tab[]={ + {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"}, + {UMCPC_ACM_MODE_MODEM, "modem"}, + {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"}, + {UMCPC_ACM_MODE_OBEX, "obex"}, + {UMCPC_ACM_MODE_VENDOR1, "vendor1"}, + {UMCPC_ACM_MODE_VENDOR2, "vendor2"}, + {UMCPC_ACM_MODE_UNLINKED, "unlinked"}, + {0, NULL} +}; + +static char *ufoma_mode_to_str(int mode) +{ + int i; + for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ + if(umcpc_modetostr_tab[i].mode == mode){ + return umcpc_modetostr_tab[i].str; + } + } + return NULL; +} + +static int ufoma_str_to_mode(char *str) +{ + int i; + for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ + if(strcmp(str, umcpc_modetostr_tab[i].str)==0){ + return umcpc_modetostr_tab[i].mode; + } + } + return -1; +} + +static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS) +{ + struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; + struct sbuf sb; + int i; + char *mode; + + sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND); + for(i = 1; i < sc->sc_modetable[0]; i++){ + mode = ufoma_mode_to_str(sc->sc_modetable[i]); + if(mode !=NULL){ + sbuf_cat(&sb, mode); + }else{ + sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]); + } + if(i < (sc->sc_modetable[0]-1)) + sbuf_cat(&sb, ","); + } + sbuf_trim(&sb); + sbuf_finish(&sb); + sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); + sbuf_delete(&sb); + + return 0; +} +static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS) +{ + struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; + char *mode; + char subbuf[]="(XXX)"; + mode = ufoma_mode_to_str(sc->sc_currentmode); + if(!mode){ + mode = subbuf; + snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode); + } + sysctl_handle_string(oidp, mode, strlen(mode), req); + + return 0; + +} +static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS) +{ + struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; + char *mode; + char subbuf[40]; + int newmode; + int error; + int i; + + mode = ufoma_mode_to_str(sc->sc_modetoactivate); + if(mode){ + strncpy(subbuf, mode, sizeof(subbuf)); + }else{ + snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate); + } + error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req); + if(error != 0 || req->newptr == NULL){ + return error; + } + + if((newmode = ufoma_str_to_mode(subbuf)) == -1){ + return EINVAL; + } + + for(i = 1 ; i < sc->sc_modetable[0] ; i++){ + if(sc->sc_modetable[i] == newmode){ + sc->sc_modetoactivate = newmode; + return 0; + } + } + + return EINVAL; +} diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c new file mode 100644 index 0000000..8ff7250 --- /dev/null +++ b/sys/dev/usb/serial/uftdi.c @@ -0,0 +1,784 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR uftdi_debug + +#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_IBUFSIZE 64 /* bytes, maximum number of bytes per + * frame */ +#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to + * do size encoding */ + +enum { + UFTDI_BULK_DT_WR, + UFTDI_BULK_DT_RD, + UFTDI_N_TRANSFER, +}; + +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_N_TRANSFER]; + 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_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_read_callback; + +static void uftdi_cfg_open(struct usb2_com_softc *); +static void uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t); +static int uftdi_set_parm_soft(struct termios *, + struct uftdi_param_config *, uint8_t); +static int uftdi_pre_param(struct usb2_com_softc *, struct termios *); +static void uftdi_cfg_param(struct usb2_com_softc *, struct termios *); +static void uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void uftdi_start_read(struct usb2_com_softc *); +static void uftdi_stop_read(struct usb2_com_softc *); +static void uftdi_start_write(struct usb2_com_softc *); +static void uftdi_stop_write(struct usb2_com_softc *); +static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *); + +static const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = { + + [UFTDI_BULK_DT_WR] = { + .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, + }, + + [UFTDI_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(uftdi, usb, 1, 1, 1); + +static struct usb2_device_id uftdi_devs[] = { + {USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)}, + {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_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, 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; + + 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_N_TRANSFER, 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]); + + /* 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_N_TRANSFER); + + return (0); +} + +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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + + /* 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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + + /* + * 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 + */ +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +uftdi_read_callback(struct usb2_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + uint8_t buf[2]; + uint8_t ftdi_msr; + 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); + + ftdi_msr = FTDI_GET_MSR(buf); + lsr = FTDI_GET_LSR(buf); + + msr = 0; + if (ftdi_msr & FTDI_SIO_CTS_MASK) + msr |= SER_CTS; + if (ftdi_msr & FTDI_SIO_DSR_MASK) + msr |= SER_DSR; + if (ftdi_msr & FTDI_SIO_RI_MASK) + msr |= SER_RI; + if (ftdi_msr & FTDI_SIO_RLSD_MASK) + msr |= SER_DCD; + + 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: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + + 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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + + 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); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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; +} + +static void +uftdi_start_read(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]); +} + +static void +uftdi_stop_read(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]); +} + +static void +uftdi_start_write(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]); +} + +static void +uftdi_stop_write(struct usb2_com_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]); +} + +/*------------------------------------------------------------------------* + * 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/usb/serial/uftdi_reg.h b/sys/dev/usb/serial/uftdi_reg.h new file mode 100644 index 0000000..0074bc5 --- /dev/null +++ b/sys/dev/usb/serial/uftdi_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/usb/serial/ugensa.c b/sys/dev/usb/serial/ugensa.c new file mode 100644 index 0000000..79676d6 --- /dev/null +++ b/sys/dev/usb/serial/ugensa.c @@ -0,0 +1,352 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UGENSA_BUF_SIZE 2048 /* bytes */ +#define UGENSA_CONFIG_INDEX 0 +#define UGENSA_IFACE_INDEX 0 +#define UGENSA_IFACE_MAX 8 /* exclusivly */ + +enum { + UGENSA_BULK_DT_WR, + UGENSA_BULK_DT_RD, + UGENSA_N_TRANSFER, +}; + +struct ugensa_sub_softc { + struct usb2_com_softc *sc_usb2_com_ptr; + struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER]; +}; + +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_read_callback; + +static void ugensa_start_read(struct usb2_com_softc *); +static void ugensa_stop_read(struct usb2_com_softc *); +static void ugensa_start_write(struct usb2_com_softc *); +static void ugensa_stop_write(struct usb2_com_softc *); + +static const struct usb2_config + ugensa_xfer_config[UGENSA_N_TRANSFER] = { + + [UGENSA_BULK_DT_WR] = { + .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, + }, + + [UGENSA_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(ugensa, usb, 1, 1, 1); + +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_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 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; + + 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 */ + usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); + usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); + + /* 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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[UGENSA_BULK_DT_RD]); +} + +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[UGENSA_BULK_DT_RD]); +} + +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[UGENSA_BULK_DT_WR]); +} + +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[UGENSA_BULK_DT_WR]); +} diff --git a/sys/dev/usb/serial/uipaq.c b/sys/dev/usb/serial/uipaq.c new file mode 100644 index 0000000..e2a88be --- /dev/null +++ b/sys/dev/usb/serial/uipaq.c @@ -0,0 +1,1314 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#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 + +enum { + UIPAQ_BULK_DT_WR, + UIPAQ_BULK_DT_RD, + UIPAQ_N_TRANSFER, +}; + +struct uipaq_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UIPAQ_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint16_t sc_line; + + uint8_t sc_lsr; /* local status register */ + uint8_t sc_msr; /* modem status register */ +}; + +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 void uipaq_start_read(struct usb2_com_softc *); +static void uipaq_stop_read(struct usb2_com_softc *); +static void uipaq_start_write(struct usb2_com_softc *); +static void uipaq_stop_write(struct usb2_com_softc *); +static void uipaq_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void uipaq_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void uipaq_cfg_set_break(struct usb2_com_softc *, uint8_t); + +static const struct usb2_config uipaq_config_data[UIPAQ_N_TRANSFER] = { + + [UIPAQ_BULK_DT_WR] = { + .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, + }, + + [UIPAQ_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(uipaq, usb, 1, 1, 1); + +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; + + 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, hz / 10); + } + + iface_index = UIPAQ_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, uipaq_config_data, + UIPAQ_N_TRANSFER, sc, &Giant); + + if (error) { + goto detach; + } + /* clear stall at first run */ + usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]); + + 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, UIPAQ_N_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[UIPAQ_BULK_DT_RD]); +} + +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[UIPAQ_BULK_DT_RD]); +} + +static void +uipaq_start_write(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]); +} + +static void +uipaq_stop_write(struct usb2_com_softc *ucom) +{ + struct uipaq_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} diff --git a/sys/dev/usb/serial/ulpt.c b/sys/dev/usb/serial/ulpt.c new file mode 100644 index 0000000..36e082b --- /dev/null +++ b/sys/dev/usb/serial/ulpt.c @@ -0,0 +1,726 @@ +#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 "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR ulpt_debug + +#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 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) + +enum { + ULPT_BULK_DT_WR, + ULPT_BULK_DT_RD, + ULPT_INTR_DT_RD, + ULPT_N_TRANSFER, +}; + +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_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_read_callback; +static usb2_callback_t ulpt_status_callback; + +static void ulpt_reset(struct ulpt_softc *); +static void ulpt_watchdog(void *); + +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); +} + +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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } +} + +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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + break; + } +} + +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; + } +} + +static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = { + [ULPT_BULK_DT_WR] = { + .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, + }, + + [ULPT_BULK_DT_RD] = { + .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, + }, + + [ULPT_INTR_DT_RD] = { + .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 */ + }, +}; + +static void +ulpt_start_read(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]); +} + +static void +ulpt_stop_read(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]); +} + +static void +ulpt_start_write(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]); +} + +static void +ulpt_stop_write(struct usb2_fifo *fifo) +{ + struct ulpt_softc *sc = fifo->priv_sc0; + + usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]); +} + +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); + usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]); + mtx_unlock(&sc->sc_mtx); + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[ULPT_BULK_DT_RD]->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); + usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]); + mtx_unlock(&sc->sc_mtx); + if (usb2_fifo_alloc_buffer(fifo, + sc->sc_xfer[ULPT_BULK_DT_WR]->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); + } +} + +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, 0); + + /* 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); + mtx_unlock(&sc->sc_mtx); + 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[ULPT_INTR_DT_RD]); + + usb2_callout_reset(&sc->sc_watchdog, + hz, &ulpt_watchdog, sc); +} + +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, usb, 1, 1, 1); +MODULE_DEPEND(ulpt, ucom, 1, 1, 1); diff --git a/sys/dev/usb/serial/umct.c b/sys/dev/usb/serial/umct.c new file mode 100644 index 0000000..a511e2e --- /dev/null +++ b/sys/dev/usb/serial/umct.c @@ -0,0 +1,579 @@ +#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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_debug + +#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 + +enum { + UMCT_BULK_DT_WR, + UMCT_BULK_DT_RD, + UMCT_INTR_DT_RD, + UMCT_N_TRANSFER, +}; + +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_N_TRANSFER]; + + 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_iface_no; + uint8_t sc_name[16]; +}; + +/* 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_callback; +static usb2_callback_t umct_write_callback; +static usb2_callback_t umct_read_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 *, uint8_t *, + uint8_t *); +static void umct_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void umct_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void umct_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static uint8_t umct_calc_baud(uint32_t); +static int umct_pre_param(struct usb2_com_softc *, struct termios *); +static void umct_cfg_param(struct usb2_com_softc *, struct termios *); +static void umct_start_read(struct usb2_com_softc *); +static void umct_stop_read(struct usb2_com_softc *); +static void umct_start_write(struct usb2_com_softc *); +static void umct_stop_write(struct usb2_com_softc *); + +static const struct usb2_config umct_config[UMCT_N_TRANSFER] = { + + [UMCT_BULK_DT_WR] = { + .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, + }, + + [UMCT_BULK_DT_RD] = { + .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 */ + }, + + [UMCT_INTR_DT_RD] = { + .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 */ + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(umct, usb, 1, 1, 1); + +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; + + 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_N_TRANSFER, 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[UMCT_BULK_DT_RD]->pipe->edesc->wMaxPacketSize); + if (maxp == 0x2) { + + /* guessed wrong - switch around endpoints */ + + struct usb2_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD]; + + sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD]; + sc->sc_xfer[UMCT_BULK_DT_RD] = temp; + + sc->sc_xfer[UMCT_BULK_DT_RD]->callback = &umct_read_callback; + sc->sc_xfer[UMCT_INTR_DT_RD]->callback = &umct_intr_callback; + } + sc->sc_obufsize = sc->sc_xfer[UMCT_BULK_DT_WR]->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_N_TRANSFER); + + 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 (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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, temp, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + 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: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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; +} + +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); +} + +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); +} + +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); +} + +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); +} + +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[UMCT_INTR_DT_RD]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]); +} + +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[UMCT_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]); +} + +static void +umct_start_write(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]); +} + +static void +umct_stop_write(struct usb2_com_softc *ucom) +{ + struct umct_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]); +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} diff --git a/sys/dev/usb/serial/umodem.c b/sys/dev/usb/serial/umodem.c new file mode 100644 index 0000000..44813dd --- /dev/null +++ b/sys/dev/usb/serial/umodem.c @@ -0,0 +1,788 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR umodem_debug + +#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 + +enum { + UMODEM_BULK_WR, + UMODEM_BULK_RD, + UMODEM_INTR_RD, + UMODEM_N_TRANSFER, +}; + +#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[UMODEM_N_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_data_iface_no; + uint8_t sc_iface_index[2]; + uint8_t sc_cm_over_data; + uint8_t sc_cm_cap; /* CM capabilities */ + uint8_t sc_acm_cap; /* ACM capabilities */ +}; + +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_write_callback; +static usb2_callback_t umodem_read_callback; + +static void umodem_start_read(struct usb2_com_softc *); +static void umodem_stop_read(struct usb2_com_softc *); +static void umodem_start_write(struct usb2_com_softc *); +static void umodem_stop_write(struct usb2_com_softc *); +static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *); +static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static int umodem_pre_param(struct usb2_com_softc *, struct termios *); +static void umodem_cfg_param(struct usb2_com_softc *, struct termios *); +static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, + struct thread *); +static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t); +static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t, + uint16_t, uint16_t); + +static const struct usb2_config umodem_config[UMODEM_N_TRANSFER] = { + + [UMODEM_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .if_index = 0, + .mh.bufsize = UMODEM_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &umodem_write_callback, + }, + + [UMODEM_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 0, + .mh.bufsize = UMODEM_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umodem_read_callback, + }, + + [UMODEM_INTR_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .if_index = 1, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umodem_intr_callback, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(umodem, usb, 1, 1, 1); +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; + + device_set_usb2_desc(dev); + + sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index[1] = 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_iface_index[0] = 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_iface_index, sc->sc_xfer, + umodem_config, UMODEM_N_TRANSFER, + sc, &Giant); + if (error) { + goto detach; + } + + /* clear stall at first run */ + usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); + + 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; + + /* start interrupt endpoint, if any */ + usb2_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); +} + +static void +umodem_stop_read(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + /* stop interrupt endpoint, if any */ + usb2_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); +} + +static void +umodem_start_write(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); +} + +static void +umodem_stop_write(struct usb2_com_softc *ucom) +{ + struct umodem_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); +} + +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; +} + +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; +} + +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)); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &ls, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + } +} + +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: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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, UMODEM_N_TRANSFER); + + return (0); +} diff --git a/sys/dev/usb/serial/umoscom.c b/sys/dev/usb/serial/umoscom.c new file mode 100644 index 0000000..344f4a1 --- /dev/null +++ b/sys/dev/usb/serial/umoscom.c @@ -0,0 +1,672 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR umoscom_debug + +#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_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 + +enum { + UMOSCOM_BULK_DT_WR, + UMOSCOM_BULK_DT_RD, + UMOSCOM_INTR_DT_RD, + UMOSCOM_N_TRANSFER, +}; + +struct umoscom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UMOSCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_mcr; + uint8_t sc_lcr; +}; + +/* 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_read_callback; +static usb2_callback_t umoscom_intr_callback; + +static void umoscom_cfg_open(struct usb2_com_softc *); +static void umoscom_cfg_close(struct usb2_com_softc *); +static void umoscom_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void umoscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void umoscom_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static int umoscom_pre_param(struct usb2_com_softc *, struct termios *); +static void umoscom_cfg_param(struct usb2_com_softc *, struct termios *); +static void umoscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t); +static uint8_t umoscom_cfg_read(struct umoscom_softc *, uint16_t); +static void umoscom_start_read(struct usb2_com_softc *); +static void umoscom_stop_read(struct usb2_com_softc *); +static void umoscom_start_write(struct usb2_com_softc *); +static void umoscom_stop_write(struct usb2_com_softc *); + +static const struct usb2_config umoscom_config_data[UMOSCOM_N_TRANSFER] = { + + [UMOSCOM_BULK_DT_WR] = { + .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, + }, + + [UMOSCOM_BULK_DT_RD] = { + .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, + }, + + [UMOSCOM_INTR_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(umoscom, usb, 1, 1, 1); + +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; + + 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, umoscom_config_data, + UMOSCOM_N_TRANSFER, sc, &Giant); + + if (error) { + goto detach; + } + /* clear stall at first run */ + usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); + + 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, UMOSCOM_N_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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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; +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &val, 0, 1000); + + DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val); + + return (val); +} + +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[UMOSCOM_INTR_DT_RD]); +#endif + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); +} + +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[UMOSCOM_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); +} + +static void +umoscom_start_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); +} + +static void +umoscom_stop_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); +} + +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: +tr_setup: + DPRINTF("\n"); + + 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"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + DPRINTF("\n"); + + 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"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: + 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"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} diff --git a/sys/dev/usb/serial/uplcom.c b/sys/dev/usb/serial/uplcom.c new file mode 100644 index 0000000..5f37417 --- /dev/null +++ b/sys/dev/usb/serial/uplcom.c @@ -0,0 +1,831 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR uplcom_debug + +#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_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 + +enum { + UPLCOM_BULK_DT_WR, + UPLCOM_BULK_DT_RD, + UPLCOM_INTR_DT_RD, + UPLCOM_N_TRANSFER, +}; + +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_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 *, struct usb2_device *); +static int uplcom_pl2303x_init(struct usb2_device *); +static void uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t); +static int uplcom_pre_param(struct usb2_com_softc *, struct termios *); +static void uplcom_cfg_param(struct usb2_com_softc *, struct termios *); +static void uplcom_start_read(struct usb2_com_softc *); +static void uplcom_stop_read(struct usb2_com_softc *); +static void uplcom_start_write(struct usb2_com_softc *); +static void uplcom_stop_write(struct usb2_com_softc *); +static void uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); + +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_write_callback; +static usb2_callback_t uplcom_read_callback; + +static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = { + + [UPLCOM_BULK_DT_WR] = { + .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, + }, + + [UPLCOM_BULK_DT_RD] = { + .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, + }, + + [UPLCOM_INTR_DT_RD] = { + .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, + }, +}; + +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_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, ucom, 1, 1, 1); +MODULE_DEPEND(uplcom, usb, 1, 1, 1); +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"); + + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]); + + 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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &ls, 0, 1000); + + 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); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + } else { + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UPLCOM_SET_REQUEST; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + } +} + +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[UPLCOM_INTR_DT_RD]); + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]); +} + +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[UPLCOM_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]); +} + +static void +uplcom_start_write(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]); +} + +static void +uplcom_stop_write(struct usb2_com_softc *ucom) +{ + struct uplcom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]); +} + +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; +} + +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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c new file mode 100644 index 0000000..38fd818 --- /dev/null +++ b/sys/dev/usb/serial/usb_serial.c @@ -0,0 +1,1127 @@ +/* $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. + */ + +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR usb2_com_debug + +#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 + +static usb2_proc_callback_t usb2_com_cfg_start_transfers; +static usb2_proc_callback_t usb2_com_cfg_open; +static usb2_proc_callback_t usb2_com_cfg_close; +static usb2_proc_callback_t usb2_com_cfg_line_state; +static usb2_proc_callback_t usb2_com_cfg_status_change; +static usb2_proc_callback_t usb2_com_cfg_param; + +static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *); +static void usb2_com_units_free(uint32_t, uint32_t); +static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t); +static void usb2_com_detach_tty(struct usb2_com_softc *); +static void usb2_com_queue_command(struct usb2_com_softc *, + usb2_proc_callback_t *, struct termios *pt, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1); +static void usb2_com_shutdown(struct usb2_com_softc *); +static void usb2_com_break(struct usb2_com_softc *, uint8_t); +static void usb2_com_dtr(struct usb2_com_softc *, uint8_t); +static void usb2_com_rts(struct usb2_com_softc *, uint8_t); + +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_outwakeup; +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_outwakeup, + .tsw_ioctl = usb2_com_ioctl, + .tsw_param = usb2_com_param, + .tsw_modem = usb2_com_modem, + .tsw_free = usb2_com_free, +}; + +MODULE_DEPEND(ucom, usb, 1, 1, 1); +MODULE_VERSION(ucom, 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); +} + +/* + * "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 "mtx" is applied before all + * callbacks are called back. Also "mtx" must be applied + * before calling into the ucom-layer! + */ +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 *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); + } + + /* XXX unit management does not really belong here */ + if (usb2_com_units_alloc(sub_units, &root_unit)) { + return (ENOMEM); + } + + error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); + if (error) { + usb2_com_units_free(root_unit, sub_units); + return (error); + } + + 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_mtx = mtx; + sc->sc_parent = parent; + sc->sc_callback = callback; + + error = usb2_com_attach_tty(sc, sub_units); + if (error) { + usb2_com_detach(ssc, sc - n, n); + usb2_com_units_free(root_unit + n, sub_units - n); + return (error); + } + sc->sc_flag |= UCOM_FLAG_ATTACHED; + } + return (0); +} + +/* + * 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_proc_drain(&ssc->sc_tq); + + for (n = 0; n != sub_units; n++, sc++) { + if (sc->sc_flag & UCOM_FLAG_ATTACHED) { + + usb2_com_detach_tty(sc); + + usb2_com_units_free(sc->sc_unit, 1); + + /* avoid duplicate detach: */ + sc->sc_flag &= ~UCOM_FLAG_ATTACHED; + } + } + usb2_proc_free(&ssc->sc_tq); +} + +static int +usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units) +{ + struct tty *tp; + int error = 0; + char buf[32]; /* temporary TTY device name buffer */ + + tp = tty_alloc(&usb2_com_class, sc, sc->sc_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 (sub_units > 1) { + /* multiple modems in one */ + if (snprintf(buf, sizeof(buf), "U%u.%u", + sc->sc_unit - sc->sc_local_unit, + sc->sc_local_unit)) { + /* ignore */ + } + } else { + /* single modem */ + 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_tty(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_mtx); + sc->sc_flag |= UCOM_FLAG_GONE; + sc->sc_flag &= ~(UCOM_FLAG_HL_READY | + UCOM_FLAG_LL_READY); + mtx_unlock(sc->sc_mtx); + if (tp) { + tty_lock(tp); + + usb2_com_close(tp); /* close, if any */ + + tty_rel_gone(tp); + + mtx_lock(sc->sc_mtx); + /* Wait for the callback after the TTY is torn down */ + while (sc->sc_ttyfreed == 0) + usb2_cv_wait(&sc->sc_cv, sc->sc_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_mtx); + } + usb2_cv_destroy(&sc->sc_cv); +} + +static void +usb2_com_queue_command(struct usb2_com_softc *sc, + usb2_proc_callback_t *fn, struct termios *pt, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) +{ + struct usb2_com_super_softc *ssc = sc->sc_super; + struct usb2_com_param_task *task; + + mtx_assert(sc->sc_mtx, MA_OWNED); + + if (usb2_proc_is_gone(&ssc->sc_tq)) { + DPRINTF("proc is gone\n"); + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct usb2_com_param_task *) + usb2_proc_msignal(&ssc->sc_tq, t0, t1); + + /* Setup callback and softc pointers */ + task->hdr.pm_callback = fn; + task->sc = sc; + + /* + * Make a copy of the termios. This field is only present if + * the "pt" field is not NULL. + */ + if (pt != NULL) + task->termios_copy = *pt; + + /* + * Closing the device should be synchronous. + */ + if (fn == usb2_com_cfg_close) + usb2_proc_mwait(&ssc->sc_tq, t0, t1); + +} + +static void +usb2_com_shutdown(struct usb2_com_softc *sc) +{ + struct tty *tp = sc->sc_tty; + + mtx_assert(sc->sc_mtx, MA_OWNED); + + DPRINTF("\n"); + + /* + * Hang up if necessary: + */ + if (tp->t_termios.c_cflag & HUPCL) { + usb2_com_modem(tp, 0, SER_DTR); + } +} + +/* + * Return values: + * 0: normal + * else: taskqueue is draining or gone + */ +uint8_t +usb2_com_cfg_is_gone(struct usb2_com_softc *sc) +{ + struct usb2_com_super_softc *ssc = sc->sc_super; + + return (usb2_proc_is_gone(&ssc->sc_tq)); +} + +static void +usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task) +{ + struct usb2_com_cfg_task *task = + (struct usb2_com_cfg_task *)_task; + struct usb2_com_softc *sc = task->sc; + + 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); + } +} + +static void +usb2_com_start_transfers(struct usb2_com_softc *sc) +{ + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + /* + * Make sure that data transfers are started in both + * directions: + */ + 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); + } +} + +static void +usb2_com_cfg_open(struct usb2_proc_msg *_task) +{ + struct usb2_com_cfg_task *task = + (struct usb2_com_cfg_task *)_task; + struct usb2_com_softc *sc = task->sc; + + 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_pause_mtx(sc->sc_mtx, hz / 10); + } + } +} + +static int +usb2_com_open(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + int error; + + mtx_assert(sc->sc_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; + + /* reset programmed line state */ + sc->sc_pls_curr = 0; + sc->sc_pls_set = 0; + sc->sc_pls_clr = 0; + + usb2_com_queue_command(sc, usb2_com_cfg_open, NULL, + &sc->sc_open_task[0].hdr, + &sc->sc_open_task[1].hdr); + + /* Queue transfer enable command last */ + usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, + &sc->sc_start_task[0].hdr, + &sc->sc_start_task[1].hdr); + + 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_proc_msg *_task) +{ + struct usb2_com_cfg_task *task = + (struct usb2_com_cfg_task *)_task; + struct usb2_com_softc *sc = task->sc; + + 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 */ + } +} + +static void +usb2_com_close(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + + mtx_assert(sc->sc_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, NULL, + &sc->sc_close_task[0].hdr, + &sc->sc_close_task[1].hdr); + + 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); + } +} + +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_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_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_line_state(struct usb2_proc_msg *_task) +{ + struct usb2_com_cfg_task *task = + (struct usb2_com_cfg_task *)_task; + struct usb2_com_softc *sc = task->sc; + uint8_t notch_bits; + uint8_t any_bits; + uint8_t prev_value; + uint8_t last_value; + uint8_t mask; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + + mask = 0; + /* compute callback mask */ + if (sc->sc_callback->usb2_com_cfg_set_dtr) + mask |= UCOM_LS_DTR; + if (sc->sc_callback->usb2_com_cfg_set_rts) + mask |= UCOM_LS_RTS; + if (sc->sc_callback->usb2_com_cfg_set_break) + mask |= UCOM_LS_BREAK; + + /* compute the bits we are to program */ + notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; + any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; + prev_value = sc->sc_pls_curr ^ notch_bits; + last_value = sc->sc_pls_curr; + + /* reset programmed line state */ + sc->sc_pls_curr = 0; + sc->sc_pls_set = 0; + sc->sc_pls_clr = 0; + + /* ensure that we don't loose any levels */ + if (notch_bits & UCOM_LS_DTR) + sc->sc_callback->usb2_com_cfg_set_dtr(sc, + (prev_value & UCOM_LS_DTR) ? 1 : 0); + if (notch_bits & UCOM_LS_RTS) + sc->sc_callback->usb2_com_cfg_set_rts(sc, + (prev_value & UCOM_LS_RTS) ? 1 : 0); + if (notch_bits & UCOM_LS_BREAK) + sc->sc_callback->usb2_com_cfg_set_break(sc, + (prev_value & UCOM_LS_BREAK) ? 1 : 0); + + /* set last value */ + if (any_bits & UCOM_LS_DTR) + sc->sc_callback->usb2_com_cfg_set_dtr(sc, + (last_value & UCOM_LS_DTR) ? 1 : 0); + if (any_bits & UCOM_LS_RTS) + sc->sc_callback->usb2_com_cfg_set_rts(sc, + (last_value & UCOM_LS_RTS) ? 1 : 0); + if (any_bits & UCOM_LS_BREAK) + sc->sc_callback->usb2_com_cfg_set_break(sc, + (last_value & UCOM_LS_BREAK) ? 1 : 0); +} + +static void +usb2_com_line_state(struct usb2_com_softc *sc, + uint8_t set_bits, uint8_t clear_bits) +{ + mtx_assert(sc->sc_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + + DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); + + /* update current programmed line state */ + sc->sc_pls_curr |= set_bits; + sc->sc_pls_curr &= ~clear_bits; + sc->sc_pls_set |= set_bits; + sc->sc_pls_clr |= clear_bits; + + /* defer driver programming */ + usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL, + &sc->sc_line_state_task[0].hdr, + &sc->sc_line_state_task[1].hdr); +} + +static void +usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff) +{ + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + usb2_com_line_state(sc, UCOM_LS_BREAK, 0); + else + usb2_com_line_state(sc, 0, UCOM_LS_BREAK); +} + +static void +usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff) +{ + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + usb2_com_line_state(sc, UCOM_LS_DTR, 0); + else + usb2_com_line_state(sc, 0, UCOM_LS_DTR); +} + +static void +usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff) +{ + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + usb2_com_line_state(sc, UCOM_LS_RTS, 0); + else + usb2_com_line_state(sc, 0, UCOM_LS_RTS); +} + +static void +usb2_com_cfg_status_change(struct usb2_proc_msg *_task) +{ + struct usb2_com_cfg_task *task = + (struct usb2_com_cfg_task *)_task; + struct usb2_com_softc *sc = task->sc; + struct tty *tp; + uint8_t new_msr; + uint8_t new_lsr; + uint8_t onoff; + + tp = sc->sc_tty; + + mtx_assert(sc->sc_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); + } +} + +void +usb2_com_status_change(struct usb2_com_softc *sc) +{ + mtx_assert(sc->sc_mtx, MA_OWNED); + + if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { + return; + } + DPRINTF("\n"); + + usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL, + &sc->sc_status_task[0].hdr, + &sc->sc_status_task[1].hdr); +} + +static void +usb2_com_cfg_param(struct usb2_proc_msg *_task) +{ + struct usb2_com_param_task *task = + (struct usb2_com_param_task *)_task; + struct usb2_com_softc *sc = task->sc; + + if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { + return; + } + if (sc->sc_callback->usb2_com_cfg_param == NULL) { + return; + } + + (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy); + + /* wait a little */ + usb2_pause_mtx(sc->sc_mtx, hz / 10); +} + +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_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; + } + } + + /* Disable transfers */ + sc->sc_flag &= ~UCOM_FLAG_GP_DATA; + + /* Queue baud rate programming command first */ + usb2_com_queue_command(sc, usb2_com_cfg_param, t, + &sc->sc_param_task[0].hdr, + &sc->sc_param_task[1].hdr); + + /* Queue transfer enable command last */ + usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, + &sc->sc_start_task[0].hdr, + &sc->sc_start_task[1].hdr); + + 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_outwakeup(struct tty *tp) +{ + struct usb2_com_softc *sc = tty_softc(tp); + + mtx_assert(sc->sc_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); +} + +/*------------------------------------------------------------------------* + * 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_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_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); +} + +static void +usb2_com_free(void *xsc) +{ + struct usb2_com_softc *sc = xsc; + + mtx_lock(sc->sc_mtx); + sc->sc_ttyfreed = 1; + usb2_cv_signal(&sc->sc_cv); + mtx_unlock(sc->sc_mtx); +} diff --git a/sys/dev/usb/serial/usb_serial.h b/sys/dev/usb/serial/usb_serial.h new file mode 100644 index 0000000..c7d57a0 --- /dev/null +++ b/sys/dev/usb/serial/usb_serial.h @@ -0,0 +1,198 @@ +/* $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_device; +struct usb2_com_softc; +struct usb2_device_request; +struct thread; + +/* + * 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_cfg_task { + struct usb2_proc_msg hdr; + struct usb2_com_softc *sc; +}; + +struct usb2_com_param_task { + struct usb2_proc_msg hdr; + struct usb2_com_softc *sc; + struct termios termios_copy; +}; + +struct usb2_com_super_softc { + struct usb2_process sc_tq; +}; + +struct usb2_com_softc { + /* + * NOTE: To avoid loosing level change information we use two + * tasks instead of one for all commands. + * + * Level changes are transitions like: + * + * ON->OFF + * OFF->ON + * OPEN->CLOSE + * CLOSE->OPEN + */ + struct usb2_com_cfg_task sc_start_task[2]; + struct usb2_com_cfg_task sc_open_task[2]; + struct usb2_com_cfg_task sc_close_task[2]; + struct usb2_com_cfg_task sc_line_state_task[2]; + struct usb2_com_cfg_task sc_status_task[2]; + struct usb2_com_param_task sc_param_task[2]; + 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_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 */ + /* programmed line state bits */ + uint8_t sc_pls_set; /* set bits */ + uint8_t sc_pls_clr; /* cleared bits */ + uint8_t sc_pls_curr; /* last state */ +#define UCOM_LS_DTR 0x01 +#define UCOM_LS_RTS 0x02 +#define UCOM_LS_BREAK 0x04 +}; + +#define usb2_com_cfg_do_request(udev,com,req,ptr,flags,timo) \ + usb2_do_request_proc(udev,&(com)->sc_super->sc_tq,req,ptr,flags,NULL,timo) + +int usb2_com_attach(struct usb2_com_super_softc *, + struct usb2_com_softc *, uint32_t, void *, + const struct usb2_com_callback *callback, struct mtx *); +void usb2_com_detach(struct usb2_com_super_softc *, + struct usb2_com_softc *, uint32_t); +void usb2_com_status_change(struct usb2_com_softc *); +uint8_t usb2_com_get_data(struct usb2_com_softc *, struct usb2_page_cache *, + uint32_t, uint32_t, uint32_t *); +void usb2_com_put_data(struct usb2_com_softc *, struct usb2_page_cache *, + uint32_t, uint32_t); +uint8_t usb2_com_cfg_is_gone(struct usb2_com_softc *); +#endif /* _USB2_SERIAL_H_ */ diff --git a/sys/dev/usb/serial/uslcom.c b/sys/dev/usb/serial/uslcom.c new file mode 100644 index 0000000..3145151 --- /dev/null +++ b/sys/dev/usb/serial/uslcom.c @@ -0,0 +1,539 @@ +/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * 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 "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR uslcom_debug + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if USB_DEBUG +static int uslcom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); +SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW, + &uslcom_debug, 0, "Debug level"); +#endif + +#define USLCOM_BULK_BUF_SIZE 1024 +#define USLCOM_CONFIG_INDEX 0 +#define USLCOM_IFACE_INDEX 0 + +#define USLCOM_SET_DATA_BITS(x) ((x) << 8) + +#define USLCOM_WRITE 0x41 +#define USLCOM_READ 0xc1 + +#define USLCOM_UART 0x00 +#define USLCOM_BAUD_RATE 0x01 +#define USLCOM_DATA 0x03 +#define USLCOM_BREAK 0x05 +#define USLCOM_CTRL 0x07 + +#define USLCOM_UART_DISABLE 0x00 +#define USLCOM_UART_ENABLE 0x01 + +#define USLCOM_CTRL_DTR_ON 0x0001 +#define USLCOM_CTRL_DTR_SET 0x0100 +#define USLCOM_CTRL_RTS_ON 0x0002 +#define USLCOM_CTRL_RTS_SET 0x0200 +#define USLCOM_CTRL_CTS 0x0010 +#define USLCOM_CTRL_DSR 0x0020 +#define USLCOM_CTRL_DCD 0x0080 + +#define USLCOM_BAUD_REF 0x384000 + +#define USLCOM_STOP_BITS_1 0x00 +#define USLCOM_STOP_BITS_2 0x02 + +#define USLCOM_PARITY_NONE 0x00 +#define USLCOM_PARITY_ODD 0x10 +#define USLCOM_PARITY_EVEN 0x20 + +#define USLCOM_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */ + +#define USLCOM_BREAK_OFF 0x00 +#define USLCOM_BREAK_ON 0x01 + +enum { + USLCOM_BULK_DT_WR, + USLCOM_BULK_DT_RD, + USLCOM_N_TRANSFER, +}; + +struct uslcom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_msr; + uint8_t sc_lsr; +}; + +static device_probe_t uslcom_probe; +static device_attach_t uslcom_attach; +static device_detach_t uslcom_detach; + +static usb2_callback_t uslcom_write_callback; +static usb2_callback_t uslcom_read_callback; + +static void uslcom_open(struct usb2_com_softc *); +static void uslcom_close(struct usb2_com_softc *); +static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t); +static void uslcom_set_rts(struct usb2_com_softc *, uint8_t); +static void uslcom_set_break(struct usb2_com_softc *, uint8_t); +static int uslcom_pre_param(struct usb2_com_softc *, struct termios *); +static void uslcom_param(struct usb2_com_softc *, struct termios *); +static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); +static void uslcom_start_read(struct usb2_com_softc *); +static void uslcom_stop_read(struct usb2_com_softc *); +static void uslcom_start_write(struct usb2_com_softc *); +static void uslcom_stop_write(struct usb2_com_softc *); + +static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = { + + [USLCOM_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = USLCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &uslcom_write_callback, + }, + + [USLCOM_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = USLCOM_BULK_BUF_SIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &uslcom_read_callback, + }, +}; + +struct usb2_com_callback uslcom_callback = { + .usb2_com_cfg_open = &uslcom_open, + .usb2_com_cfg_close = &uslcom_close, + .usb2_com_cfg_get_status = &uslcom_get_status, + .usb2_com_cfg_set_dtr = &uslcom_set_dtr, + .usb2_com_cfg_set_rts = &uslcom_set_rts, + .usb2_com_cfg_set_break = &uslcom_set_break, + .usb2_com_cfg_param = &uslcom_param, + .usb2_com_pre_param = &uslcom_pre_param, + .usb2_com_start_read = &uslcom_start_read, + .usb2_com_stop_read = &uslcom_stop_read, + .usb2_com_start_write = &uslcom_start_write, + .usb2_com_stop_write = &uslcom_stop_write, +}; + +static const struct usb2_device_id uslcom_devs[] = { + { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) }, + { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) }, + { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) }, + { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) }, + { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) }, + { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) }, +}; + +static device_method_t uslcom_methods[] = { + DEVMETHOD(device_probe, uslcom_probe), + DEVMETHOD(device_attach, uslcom_attach), + DEVMETHOD(device_detach, uslcom_detach), + {0, 0} +}; + +static devclass_t uslcom_devclass; + +static driver_t uslcom_driver = { + .name = "uslcom", + .methods = uslcom_methods, + .size = sizeof(struct uslcom_softc), +}; + +DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0); +MODULE_DEPEND(uslcom, ucom, 1, 1, 1); +MODULE_DEPEND(uslcom, usb, 1, 1, 1); +MODULE_VERSION(uslcom, 1); + +static int +uslcom_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 != USLCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); +} + +static int +uslcom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct uslcom_softc *sc = device_get_softc(dev); + int error; + + DPRINTFN(11, "\n"); + + device_set_usb2_desc(dev); + + sc->sc_udev = uaa->device; + + error = usb2_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, + USLCOM_N_TRANSFER, sc, &Giant); + if (error) { + DPRINTF("one or more missing USB endpoints, " + "error=%s\n", usb2_errstr(error)); + goto detach; + } + /* clear stall at first run */ + usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &uslcom_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + uslcom_detach(dev); + return (ENXIO); +} + +static int +uslcom_detach(device_t dev) +{ + struct uslcom_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, USLCOM_N_TRANSFER); + + return (0); +} + +static void +uslcom_open(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_UART; + USETW(req.wValue, USLCOM_UART_ENABLE); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("UART enable failed (ignored)\n"); + } +} + +static void +uslcom_close(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_UART; + USETW(req.wValue, USLCOM_UART_DISABLE); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("UART disable failed (ignored)\n"); + } +} + +static void +uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t ctl; + + DPRINTF("onoff = %d\n", onoff); + + ctl = onoff ? USLCOM_CTRL_DTR_ON : 0; + ctl |= USLCOM_CTRL_DTR_SET; + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_CTRL; + USETW(req.wValue, ctl); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Setting DTR failed (ignored)\n"); + } +} + +static void +uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t ctl; + + DPRINTF("onoff = %d\n", onoff); + + ctl = onoff ? USLCOM_CTRL_RTS_ON : 0; + ctl |= USLCOM_CTRL_RTS_SET; + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_CTRL; + USETW(req.wValue, ctl); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Setting DTR failed (ignored)\n"); + } +} + +static int +uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + if (t->c_ospeed <= 0 || t->c_ospeed > 921600) + return (EINVAL); + return (0); +} + +static void +uslcom_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t data; + + DPRINTF("\n"); + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_BAUD_RATE; + USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Set baudrate failed (ignored)\n"); + } + + if (t->c_cflag & CSTOPB) + data = USLCOM_STOP_BITS_2; + else + data = USLCOM_STOP_BITS_1; + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) + data |= USLCOM_PARITY_ODD; + else + data |= USLCOM_PARITY_EVEN; + } else + data |= USLCOM_PARITY_NONE; + switch (t->c_cflag & CSIZE) { + case CS5: + data |= USLCOM_SET_DATA_BITS(5); + break; + case CS6: + data |= USLCOM_SET_DATA_BITS(6); + break; + case CS7: + data |= USLCOM_SET_DATA_BITS(7); + break; + case CS8: + data |= USLCOM_SET_DATA_BITS(8); + break; + } + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_DATA; + USETW(req.wValue, data); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Set format failed (ignored)\n"); + } + return; +} + +static void +uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct uslcom_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + *lsr = sc->sc_lsr; + *msr = sc->sc_msr; +} + +static void +uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb2_device_request req; + uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF; + + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_BREAK; + USETW(req.wValue, brk); + USETW(req.wIndex, USLCOM_PORT_NO); + USETW(req.wLength, 0); + + if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Set BREAK failed (ignored)\n"); + } +} + +static void +uslcom_write_callback(struct usb2_xfer *xfer) +{ + struct uslcom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: +tr_setup: + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + USLCOM_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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +uslcom_read_callback(struct usb2_xfer *xfer) +{ + struct uslcom_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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +uslcom_start_read(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); +} + +static void +uslcom_stop_read(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); +} + +static void +uslcom_start_write(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); +} + +static void +uslcom_stop_write(struct usb2_com_softc *ucom) +{ + struct uslcom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); +} diff --git a/sys/dev/usb/serial/uvisor.c b/sys/dev/usb/serial/uvisor.c new file mode 100644 index 0000000..854f972 --- /dev/null +++ b/sys/dev/usb/serial/uvisor.c @@ -0,0 +1,610 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR uvisor_debug + +#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_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; + +enum { + UVISOR_BULK_DT_WR, + UVISOR_BULK_DT_RD, + UVISOR_N_TRANSFER, +}; + +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 + + 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_read_callback; + +static usb2_error_t uvisor_init(struct uvisor_softc *, struct usb2_device *, + struct usb2_config *); +static void uvisor_cfg_open(struct usb2_com_softc *); +static void uvisor_cfg_close(struct usb2_com_softc *); +static void uvisor_start_read(struct usb2_com_softc *); +static void uvisor_stop_read(struct usb2_com_softc *); +static void uvisor_start_write(struct usb2_com_softc *); +static void uvisor_stop_write(struct usb2_com_softc *); + +static const struct usb2_config uvisor_config[UVISOR_N_TRANSFER] = { + + [UVISOR_BULK_DT_WR] = { + .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, + }, + + [UVISOR_BULK_DT_RD] = { + .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, + }, +}; + +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, ucom, 1, 1, 1); +MODULE_DEPEND(uvisor, usb, 1, 1, 1); + +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)); + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_RD]); + + 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, buffer, 0, 1000); + if (err) { + DPRINTFN(0, "close notification failed, error=%s\n", + usb2_errstr(err)); + } +} + +static void +uvisor_start_read(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]); +} + +static void +uvisor_stop_read(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]); +} + +static void +uvisor_start_write(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]); +} + +static void +uvisor_stop_write(struct usb2_com_softc *ucom) +{ + struct uvisor_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]); +} + +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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} diff --git a/sys/dev/usb/serial/uvscom.c b/sys/dev/usb/serial/uvscom.c new file mode 100644 index 0000000..15a9eba --- /dev/null +++ b/sys/dev/usb/serial/uvscom.c @@ -0,0 +1,707 @@ +/* $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 "usbdevs.h" +#include +#include +#include +#include + +#define USB_DEBUG_VAR uvscom_debug + +#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 */ + +enum { + UVSCOM_BULK_DT_WR, + UVSCOM_BULK_DT_RD, + UVSCOM_INTR_DT_RD, + UVSCOM_N_TRANSFER, +}; + +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_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_read_callback; +static usb2_callback_t uvscom_intr_callback; + +static void uvscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void uvscom_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static void uvscom_cfg_set_break(struct usb2_com_softc *, uint8_t); +static int uvscom_pre_param(struct usb2_com_softc *, struct termios *); +static void uvscom_cfg_param(struct usb2_com_softc *, struct termios *); +static int uvscom_pre_open(struct usb2_com_softc *); +static void uvscom_cfg_open(struct usb2_com_softc *); +static void uvscom_cfg_close(struct usb2_com_softc *); +static void uvscom_start_read(struct usb2_com_softc *); +static void uvscom_stop_read(struct usb2_com_softc *); +static void uvscom_start_write(struct usb2_com_softc *); +static void uvscom_stop_write(struct usb2_com_softc *); +static void uvscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t); +static uint16_t uvscom_cfg_read_status(struct uvscom_softc *); + +static const struct usb2_config uvscom_config[UVSCOM_N_TRANSFER] = { + + [UVSCOM_BULK_DT_WR] = { + .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, + }, + + [UVSCOM_BULK_DT_RD] = { + .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, + }, + + [UVSCOM_INTR_DT_RD] = { + .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, + }, +}; + +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_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, ucom, 1, 1, 1); +MODULE_DEPEND(uvscom, usb, 1, 1, 1); +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; + + 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 */ + usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]); + + 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(&Giant); + usb2_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]); + mtx_unlock(&Giant); + + 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[UVSCOM_INTR_DT_RD]) { + usb2_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]); + } + 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: +tr_setup: + 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) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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: +tr_setup: + 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 */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + 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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void +uvscom_start_read(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]); +} + +static void +uvscom_stop_read(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]); +} + +static void +uvscom_start_write(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]); +} + +static void +uvscom_stop_write(struct usb2_com_softc *ucom) +{ + struct uvscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]); +} + +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; +} + +static void +uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value) +{ + struct usb2_device_request req; + usb2_error_t err; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = index; + USETW(req.wValue, value); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } +} + +static uint16_t +uvscom_cfg_read_status(struct uvscom_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t err; + uint8_t data[2]; + + 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, data, 0, 1000); + if (err) { + DPRINTFN(0, "device request failed, err=%s " + "(ignored)\n", usb2_errstr(err)); + } + return (data[0] | (data[1] << 8)); +} diff --git a/sys/dev/usb/sound/uaudio.c b/sys/dev/usb/sound/uaudio.c new file mode 100644 index 0000000..b79e398 --- /dev/null +++ b/sys/dev/usb/sound/uaudio.c @@ -0,0 +1,3750 @@ +/* $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 "usbdevs.h" +#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" + +static int uaudio_default_rate = 96000; +static int uaudio_default_bits = 32; +static int uaudio_default_channels = 2; + +#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"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, + &uaudio_default_rate, 0, "uaudio default sample rate"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, + &uaudio_default_bits, 0, "uaudio default sample bits"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, + &uaudio_default_channels, 0, "uaudio default sample channels"); +#endif + +#define UAUDIO_MINFRAMES 16 /* must be factor of 8 due HS-USB */ +#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ +#define UAUDIO_RECURSE_LIMIT 24 /* rounds */ + +#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 intr_size; /* in bytes */ + 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; + +static void uaudio_chan_fill_info_sub(struct uaudio_softc *, + struct usb2_device *, uint32_t, uint16_t, uint8_t, uint8_t); +static void uaudio_chan_fill_info(struct uaudio_softc *, + struct usb2_device *); +static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *, + struct uaudio_mixer_node *); +static void uaudio_mixer_add_ctl(struct uaudio_softc *, + struct uaudio_mixer_node *); +static void uaudio_mixer_add_input(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_output(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_mixer(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_selector(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static uint32_t uaudio_mixer_feature_get_bmaControls( + const struct usb2_audio_feature_unit *, uint8_t); +static void uaudio_mixer_add_feature(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_processing_updown(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_processing(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio_mixer_add_extension(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static struct usb2_audio_cluster uaudio_mixer_get_cluster(uint8_t, + const struct uaudio_terminal_node *); +static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *, + struct uaudio_mixer_node *); +static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *, + struct uaudio_mixer_node *); +static const struct uaudio_terminal_node *uaudio_mixer_get_input( + const struct uaudio_terminal_node *, uint8_t); +static const struct uaudio_terminal_node *uaudio_mixer_get_output( + const struct uaudio_terminal_node *, uint8_t); +static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *, + const uint8_t *, uint8_t, struct uaudio_search_result *); +static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, + uint8_t, uint8_t, struct uaudio_search_result *); +static void uaudio_mixer_fill_info(struct uaudio_softc *, + struct usb2_device *, void *); +static uint16_t uaudio_mixer_get(struct usb2_device *, uint8_t, + struct uaudio_mixer_node *); +static void uaudio_mixer_ctl_set(struct uaudio_softc *, + struct uaudio_mixer_node *, uint8_t, int32_t val); +static usb2_error_t uaudio_set_speed(struct usb2_device *, uint8_t, uint32_t); +static int uaudio_mixer_signext(uint8_t, int); +static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val); +static const void *uaudio_mixer_verify_desc(const void *, uint32_t); +static void uaudio_mixer_init(struct uaudio_softc *); +static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t); +static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb2_fifo *); +static void umidi_start_read(struct usb2_fifo *); +static void umidi_stop_read(struct usb2_fifo *); +static void umidi_start_write(struct usb2_fifo *); +static void umidi_stop_write(struct usb2_fifo *); +static int umidi_open(struct usb2_fifo *, int, struct thread *); +static int umidi_ioctl(struct usb2_fifo *, u_long cmd, void *, int, struct thread *); +static void umidi_close(struct usb2_fifo *, int, struct thread *); +static void umidi_init(device_t dev); +static int32_t umidi_probe(device_t dev); +static int32_t umidi_detach(device_t dev); + +#if USB_DEBUG +static void uaudio_chan_dump_ep_desc( + const usb2_endpoint_descriptor_audio_t *); +static void uaudio_mixer_dump_cluster(uint8_t, + const struct uaudio_terminal_node *); +static const char *uaudio_mixer_get_terminal_name(uint16_t); +#endif + +static const struct usb2_config + uaudio_cfg_record[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_MINFRAMES, + .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_MINFRAMES, + .mh.flags = {.short_xfer_ok = 1,}, + .mh.callback = &uaudio_chan_record_callback, + }, +}; + +static const struct usb2_config + uaudio_cfg_play[UAUDIO_NCHANBUFS] = { + [0] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UAUDIO_MINFRAMES, + .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_MINFRAMES, + .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); + + if (uaa->use_generic == 0) + 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; + + 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]; + + 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"); + usb2_pause_mtx(NULL, 2 * hz); + 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); + } +} + +#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; + + if (ep_dir == UE_DIR_IN) + chan->usb2_cfg = + uaudio_cfg_record; + else + chan->usb2_cfg = + uaudio_cfg_play; + + sample_size = ((chan->p_asf1d->bNrChannels * + chan->p_asf1d->bBitResolution) / 8); + + /* + * NOTE: "chan->bytes_per_frame" + * should not be zero! + */ + 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; + } + } +} + +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_isoc_fps(udev); + uint8_t bits = uaudio_default_bits; + uint8_t y; + uint8_t channels = uaudio_default_channels; + uint8_t x; + + bits -= (bits % 8); + if ((bits == 0) || (bits > 32)) { + /* set a valid value */ + bits = 32; + } + rate -= (rate % fps); + if ((rate == 0) || (rate > 192000)) { + /* set a valid value */ + rate = 192000 - (192000 % fps); + } + if ((channels == 0) || (channels > 2)) { + /* set a valid value */ + channels = 2; + } + 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); + } +} + +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; + uint32_t blockcount; + uint32_t n; + uint32_t offset; + + /* allow dynamic sizing of play buffer */ + total = ch->intr_size; + + /* allow dynamic sizing of play buffer */ + blockcount = total / ch->bytes_per_frame; + + /* align units */ + blockcount -= (blockcount % UAUDIO_MINFRAMES); + + /* range check - min */ + if (blockcount == 0) { + blockcount = UAUDIO_MINFRAMES; + } + /* 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; + } +} + +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; + uint32_t blockcount; + uint32_t offset0; + uint32_t offset1; + + /* allow dynamic sizing of play buffer */ + total = ch->intr_size; + + /* allow dynamic sizing of play buffer */ + blockcount = total / ch->bytes_per_frame; + + /* align units */ + blockcount -= (blockcount % UAUDIO_MINFRAMES); + + /* range check - min */ + if (blockcount == 0) { + blockcount = UAUDIO_MINFRAMES; + } + /* 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); + uint32_t buf_size; + uint8_t endpoint; + uint8_t iface_index; + uint8_t alt_index; + usb2_error_t err; + + /* compute required buffer size */ + buf_size = (ch->bytes_per_frame * UAUDIO_MINFRAMES); + + /* setup interrupt interval */ + ch->intr_size = buf_size; + + /* double buffering */ + buf_size *= 2; + + ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); + if (ch->buf == NULL) { + goto error; + } + if (sndbuf_setup(b, ch->buf, buf_size) != 0) { + goto error; + } + ch->start = ch->buf; + ch->end = ch->buf + buf_size; + 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) +{ + /* we only support one size */ + blocksize = ch->intr_size; + blockcount = 2; + + if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) || + (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) { + DPRINTFN(1, "resizing to %u x " + "%u bytes\n", blockcount, blocksize); + 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"); + } +} + +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 +} + +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 +} + +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 +} + +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; + } +} + +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); +} + +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); + } + } +} + +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); +} + +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; + } +} + +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); + } +} + +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; + } +} + +#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--; +} + +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--); +} + +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" is allowed to be corrupt */ + wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength; + + /* get USB audio revision */ + 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); + } +} + +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]; + + DPRINTF("\n"); + + 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; + } + break; + + default: /* Error */ + DPRINTF("error=%s\n", usb2_errstr(xfer->error)); + if (xfer->error == USB_ERR_CANCELLED) { + /* do nothing - we are detaching */ + break; + } + 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) +{ + 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, "type=0x%03x val=%d min=%d max=%d val=%d\n", + mc->type, val, mc->minval, mc->maxval, 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]); +} + +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]; + } + } + } +} + +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); + } + } +} + +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); + } +} + +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); + } +} + +/* + * 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]); +} + +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"); + } +} + +static void +umidi_start_write(struct usb2_fifo *fifo) +{ + struct umidi_chan *chan = fifo->priv_sc0; + + usb2_transfer_start(chan->xfer[0]); +} + +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]); + } +} + +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); + } +} + + +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); +} + +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, usb, 1, 1, 1); +MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(uaudio, 1); diff --git a/sys/dev/usb/sound/uaudio.h b/sys/dev/usb/sound/uaudio.h new file mode 100644 index 0000000..e763c6d --- /dev/null +++ b/sys/dev/usb/sound/uaudio.h @@ -0,0 +1,63 @@ +/* $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/usb/sound/uaudio_pcm.c b/sys/dev/usb/sound/uaudio_pcm.c new file mode 100644 index 0000000..a9d3a48 --- /dev/null +++ b/sys/dev/usb/sound/uaudio_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/usb/sound/uaudio_reg.h b/sys/dev/usb/sound/uaudio_reg.h new file mode 100644 index 0000000..2bf68a1 --- /dev/null +++ b/sys/dev/usb/sound/uaudio_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/usb/storage/ata-usb.c b/sys/dev/usb/storage/ata-usb.c new file mode 100644 index 0000000..01ce320 --- /dev/null +++ b/sys/dev/usb/storage/ata-usb.c @@ -0,0 +1,1102 @@ +/*- + * 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 "usbdevs.h" +#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, usb, 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; + + 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); + } +} + +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); +} + +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); +} + +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); +} + +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); +} + +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; + + /* drop the USB transfer lock while doing the ATA interrupt */ + mtx_unlock(&sc->locked_mtx); + + ata_interrupt(device_get_softc(request->parent)); + + mtx_lock(&sc->locked_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); + } +} + +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); +} + +/* + * 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; +} + +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/usb/storage/rio500_usb.h b/sys/dev/usb/storage/rio500_usb.h new file mode 100644 index 0000000..5b53e2c --- /dev/null +++ b/sys/dev/usb/storage/rio500_usb.h @@ -0,0 +1,48 @@ +/*- + ---------------------------------------------------------------------- + + Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) + + Redistribution and use in source and binary forms, with or without + modification, are permitted under any licence of your choise which + meets the open source licence definiton + http://www.opensource.org/opd.html such as the GNU licence or the + BSD licence. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License or the BSD license for more details. + + ---------------------------------------------------------------------- + + Modified for FreeBSD by Iwasa Kazmi + + ---------------------------------------------------------------------- */ + +/* $FreeBSD$ */ + +#include +#ifndef USB_VENDOR_DIAMOND +#define USB_VENDOR_DIAMOND 0x841 +#endif +#ifndef USB_PRODUCT_DIAMOND_RIO500USB +#define USB_PRODUCT_DIAMOND_RIO500USB 0x1 +#endif + +struct RioCommand +{ + uint16_t length; + int request; + int requesttype; + int value; + int index; + void *buffer; + int timeout; +}; + +#define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand) +#define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand) + +#define RIO_DIR_OUT 0x0 +#define RIO_DIR_IN 0x1 diff --git a/sys/dev/usb/storage/umass.c b/sys/dev/usb/storage/umass.c new file mode 100644 index 0000000..156e677 --- /dev/null +++ b/sys/dev/usb/storage/umass.c @@ -0,0 +1,3619 @@ +#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 "usbdevs.h" +#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_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_ALCOR, USB_PRODUCT_ALCOR_TRANSCEND, 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_MYSON, USB_PRODUCT_MYSON_STARREADER, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + NO_SYNCHRONIZE_CACHE + }, + {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_SDS_HOTFIND_D, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE + }, + {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 + }, + {USB_VENDOR_MEIZU, USB_PRODUCT_MEIZU_M6_SL, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + NO_INQUIRY | NO_SYNCHRONIZE_CACHE + }, + {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 *); +static void umass_init_shuttle(struct umass_softc *); +static void umass_reset(struct umass_softc *); +static void umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *, + uint8_t, uint8_t); +static void umass_command_start(struct umass_softc *, uint8_t, void *, + uint32_t, uint32_t, umass_callback_t *, union ccb *); +static uint8_t umass_bbb_get_max_lun(struct umass_softc *); +static void umass_cbi_start_status(struct umass_softc *); +static void umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *, + uint8_t, uint8_t); +static int umass_cam_attach_sim(struct umass_softc *); +static void umass_cam_rescan_callback(struct cam_periph *, union ccb *); +static void umass_cam_rescan(struct umass_softc *); +static void umass_cam_attach(struct umass_softc *); +static void umass_cam_detach_sim(struct umass_softc *); +static void umass_cam_action(struct cam_sim *, union ccb *); +static void umass_cam_poll(struct cam_sim *); +static void umass_cam_cb(struct umass_softc *, union ccb *, uint32_t, + uint8_t); +static void umass_cam_sense_cb(struct umass_softc *, union ccb *, uint32_t, + uint8_t); +static void umass_cam_quirk_cb(struct umass_softc *, union ccb *, uint32_t, + uint8_t); +static uint8_t umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t); +static uint8_t umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t); +static uint8_t umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t); +static uint8_t umass_atapi_transform(struct umass_softc *, uint8_t *, + uint8_t); +static uint8_t umass_no_transform(struct umass_softc *, uint8_t *, uint8_t); +static uint8_t umass_std_transform(struct umass_softc *, union ccb *, uint8_t + *, uint8_t); + +#if USB_DEBUG +static void umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *); +static void umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *); +static void umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t); +static void umass_dump_buffer(struct umass_softc *, uint8_t *, uint32_t, + uint32_t); +#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 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, NULL, 0); +MODULE_DEPEND(umass, usb, 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; + + /* + * 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); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), + 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; + +#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, &sc->sc_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, + &sc->sc_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, 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(&sc->sc_mtx); +#endif + umass_cam_detach_sim(sc); + +#if (__FreeBSD_version >= 700037) + mtx_unlock(&sc->sc_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]); +} + +/* + * 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); + } +} + +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); +} + +static void +umass_cancel_ccb(struct umass_softc *sc) +{ + union ccb *ccb; + + mtx_assert(&sc->sc_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); + } +} + +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); +} + +/* + * 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); +} + +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); +} + +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); +} + +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); +} + +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); + } +} + +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); + } +} + +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); +} + +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); +} + +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); +} + +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; + + if (xfer->flags.ext_buffer) { + usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); + } + 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); +} + +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); +} + +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 */ + + /* + * 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) + &sc->sc_mtx /* mutex */ , +#endif + 1 /* maximum device openings */ , + 0 /* maximum tagged device openings */ , + devq); + + if (sc->sc_sim == NULL) { + cam_simq_free(devq); + return (ENOMEM); + } + +#if (__FreeBSD_version >= 700037) + mtx_lock(&sc->sc_mtx); +#endif + +#if (__FreeBSD_version >= 700048) + if (xpt_bus_register(sc->sc_sim, sc->sc_dev, sc->sc_unit) != CAM_SUCCESS) { + mtx_unlock(&sc->sc_mtx); + return (ENOMEM); + } +#else + if (xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) { +#if (__FreeBSD_version >= 700037) + mtx_unlock(&sc->sc_mtx); +#endif + return (ENOMEM); + } +#endif + +#if (__FreeBSD_version >= 700037) + mtx_unlock(&sc->sc_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); +} + +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(&sc->sc_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(&sc->sc_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(&sc->sc_mtx); +#endif + + /* The scan is in progress now. */ +} + +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); + } +} + +/* umass_cam_detach + * detach from the CAM layer + */ + +static void +umass_cam_detach_sim(struct umass_softc *sc) +{ + if (sc->sc_sim != NULL) { + if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) { + /* accessing the softc is not possible after this */ + sc->sc_sim->softc = UMASS_GONE; + cam_sim_free(sc->sc_sim, /* free_devq */ TRUE); + } else { + panic("%s: CAM layer is busy!\n", + sc->sc_name); + } + sc->sc_sim = NULL; + } +} + +/* 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(&sc->sc_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(&sc->sc_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); +} + + +/* 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); + } +} + +/* + * 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); +} + +/* + * 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" : ""))); +} + +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" : "")))); +} + +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" : "")))); +} + +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); +} + +#endif diff --git a/sys/dev/usb/storage/urio.c b/sys/dev/usb/storage/urio.c new file mode 100644 index 0000000..82e16d9 --- /dev/null +++ b/sys/dev/usb/storage/urio.c @@ -0,0 +1,479 @@ +/*- + * 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 "usbdevs.h" +#include +#include +#include +#include +#include + +#define USB_DEBUG_VAR urio_debug + +#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, usb, 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; + + 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); + } +} + +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); + } +} + +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]); +} + +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]); +} + +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]); +} + +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]); +} + +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); + } +} + +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/usb/storage/ustorage_fs.c b/sys/dev/usb/storage/ustorage_fs.c new file mode 100644 index 0000000..2eec249 --- /dev/null +++ b/sys/dev/usb/storage/ustorage_fs.c @@ -0,0 +1,1897 @@ +/* $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 "usbdevs.h" +#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 usb_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(usb_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, usb, 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; + + /* + * 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]); + } +} + +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); +} + +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); + } +} + +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; + } +} + +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; + } +} + +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; + } +} + +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; + } +} + +/* 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) +{ +#if 0 + 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; + } +#endif + 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/usb/template/usb_template.c b/sys/dev/usb/template/usb_template.c new file mode 100644 index 0000000..31c853a --- /dev/null +++ b/sys/dev/usb/template/usb_template.c @@ -0,0 +1,1312 @@ +/* $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(usb_template, usb, 1, 1, 1); +MODULE_VERSION(usb_template, 1); + +/* function prototypes */ + +static void usb2_make_raw_desc(struct usb2_temp_setup *, const uint8_t *); +static void usb2_make_endpoint_desc(struct usb2_temp_setup *, + const struct usb2_temp_endpoint_desc *); +static void usb2_make_interface_desc(struct usb2_temp_setup *, + const struct usb2_temp_interface_desc *); +static void usb2_make_config_desc(struct usb2_temp_setup *, + const struct usb2_temp_config_desc *); +static void usb2_make_device_desc(struct usb2_temp_setup *, + const struct usb2_temp_device_desc *); +static uint8_t usb2_hw_ep_match(const struct usb2_hw_ep_profile *, uint8_t, + uint8_t); +static uint8_t usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *, + struct usb2_hw_ep_scratch_sub *, uint8_t); +static uint8_t usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *, uint8_t, + uint8_t); +static usb2_error_t usb2_hw_ep_resolve(struct usb2_device *, + struct usb2_descriptor *); +static const struct usb2_temp_device_desc *usb2_temp_get_tdd(struct usb2_device *); +static void *usb2_temp_get_device_desc(struct usb2_device *); +static void *usb2_temp_get_qualifier_desc(struct usb2_device *); +static void *usb2_temp_get_config_desc(struct usb2_device *, uint16_t *, + uint8_t); +static const void *usb2_temp_get_string_desc(struct usb2_device *, uint16_t, + uint8_t); +static const void *usb2_temp_get_vendor_desc(struct usb2_device *, + const struct usb2_device_request *); +static const void *usb2_temp_get_hub_desc(struct usb2_device *); +static void usb2_temp_get_desc(struct usb2_device *, + struct usb2_device_request *, const void **, uint16_t *); +static usb2_error_t usb2_temp_setup(struct usb2_device *, + const struct usb2_temp_device_desc *); +static void usb2_temp_unsetup(struct usb2_device *); +static usb2_error_t usb2_temp_setup_by_index(struct usb2_device *, + uint16_t index); +static void usb2_temp_init(void *); + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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++; +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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; + } + } +} + +/*------------------------------------------------------------------------* + * 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; + } + } +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +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; +} + +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/usb/template/usb_template.h b/sys/dev/usb/template/usb_template.h new file mode 100644 index 0000000..361de3a --- /dev/null +++ b/sys/dev/usb/template/usb_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/usb/template/usb_template_cdce.c b/sys/dev/usb/template/usb_template_cdce.c new file mode 100644 index 0000000..c215c1d --- /dev/null +++ b/sys/dev/usb/template/usb_template_cdce.c @@ -0,0 +1,292 @@ +#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_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, + +#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_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_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, + ð_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_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/usb/template/usb_template_msc.c b/sys/dev/usb/template/usb_template_msc.c new file mode 100644 index 0000000..a3d88b0 --- /dev/null +++ b/sys/dev/usb/template/usb_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/usb/template/usb_template_mtp.c b/sys/dev/usb/template/usb_template_mtp.c new file mode 100644 index 0000000..271c202 --- /dev/null +++ b/sys/dev/usb/template/usb_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/usb/ufm_ioctl.h b/sys/dev/usb/ufm_ioctl.h new file mode 100644 index 0000000..921b3d4 --- /dev/null +++ b/sys/dev/usb/ufm_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/usb/usb.h b/sys/dev/usb/usb.h new file mode 100644 index 0000000..104ee36 --- /dev/null +++ b/sys/dev/usb/usb.h @@ -0,0 +1,619 @@ +/* $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. + */ + +/* + * This file contains standard definitions for the following USB + * protocol versions: + * + * USB v1.0 + * USB v1.1 + * USB v2.0 + * USB v3.0 + */ + +#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 */ + +/* Definition of software USB power modes */ +#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 */ + +#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_PORT_RESUME_DELAY 20 /* 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_PORT_RESUME_DELAY (20*2) /* 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 /* language ID string index */ +#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_DEBUG 0x0A +#define UDESC_IFACE_ASSOC 0x0B /* interface association */ +#define UDESC_BOS 0x0F /* binary object store */ +#define UDESC_DEVICE_CAPABILITY 0x10 +#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 UDESC_ENDPOINT_SS_COMP 0x30 /* super speed */ +#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 +#define UR_SET_SEL 0x30 +#define UR_ISOCH_DELAY 0x31 + +/* 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 +#define UR_SET_HUB_DEPTH 0x0c +#define UR_GET_PORT_ERR_COUNT 0x0d + +/* Feature numbers */ +#define UF_ENDPOINT_HALT 0 +#define UF_DEVICE_REMOTE_WAKEUP 1 +#define UF_TEST_MODE 2 +#define UF_U1_ENABLE 0x30 +#define UF_U2_ENABLE 0x31 +#define UF_LTM_ENABLE 0x32 + +/* 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_LINK_STATE 5 +#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 + +/* SuperSpeed HUB specific features */ +#define UHF_PORT_U1_TIMEOUT 23 +#define UHF_PORT_U2_TIMEOUT 24 +#define UHF_C_PORT_LINK_STATE 25 +#define UHF_C_PORT_CONFIG_ERROR 26 +#define UHF_PORT_REMOTE_WAKE_MASK 27 +#define UHF_BH_PORT_RESET 28 +#define UHF_C_BH_PORT_RESET 29 +#define UHF_FORCE_LINKPM_ACCEPT 30 + +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_USB_3_0 0x0300 +#define UD_IS_USB2(d) ((d)->bcdUSB[1] == 0x02) +#define UD_IS_USB3(d) ((d)->bcdUSB[1] == 0x03) + 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; + +/* Binary Device Object Store (BOS) */ +struct usb2_bos_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord wTotalLength; + uByte bNumDeviceCaps; +} __packed; + +/* Binary Device Object Store Capability */ +struct usb2_bos_cap_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDevCapabilityType; +#define USB_DEVCAP_RESERVED 0x00 +#define USB_DEVCAP_WUSB 0x01 +#define USB_DEVCAP_USB2EXT 0x02 +#define USB_DEVCAP_SUPER_SPEED 0x03 +#define USB_DEVCAP_CONTAINER_ID 0x04 + /* data ... */ +} __packed; + +struct usb2_devcap_usb2ext_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDevCapabilityType; + uByte bmAttributes; +#define USB_V2EXT_LPM 0x02 +} __packed; + +struct usb2_devcap_ss_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDevCapabilityType; + uByte bmAttributes; + uWord wSpeedsSupported; + uByte bFunctionaltySupport; + uByte bU1DevExitLat; + uByte bU2DevExitLat; +} __packed; + +struct usb2_devcap_container_id_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDevCapabilityType; + uByte bReserved; + uByte ContainerID; +} __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; + +struct usb2_interface_assoc_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bFirstInterface; + uByte bInterfaceCount; + uByte bFunctionClass; + uByte bFunctionSubClass; + uByte bFunctionProtocol; + uByte iFunction; +} __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_endpoint_ss_comp_descriptor { + uByte bLength; + uByte bDescriptorType; + uWord bMaxBurst; + uByte bmAttributes; + uWord wBytesPerInterval; +} __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) + uByte PortPowerCtrlMask[1]; /* deprecated */ +} __packed; + +struct usb2_hub_ss_descriptor { + uByte bDescLength; + uByte bDescriptorType; + uByte bNbrPorts; /* max 15 */ + uWord wHubCharacteristics; + uByte bPwrOn2PwrGood; /* delay in 2 ms units */ + uByte bHubContrCurrent; + uByte bHubHdrDecLat; + uWord wHubDelay; + uByte DeviceRemovable[2]; /* max 15 ports */ +} __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_POWER 0x0100 +#define UPS_LOW_SPEED 0x0200 +#define UPS_HIGH_SPEED 0x0400 +#define UPS_PORT_TEST 0x0800 +#define UPS_PORT_INDICATOR 0x1000 +#define UPS_PORT_MODE_DEVICE 0x8000 /* currently FreeBSD specific */ + 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/usb/usb_bus.h b/sys/dev/usb/usb_bus.h new file mode 100644 index 0000000..59287c4 --- /dev/null +++ b/sys/dev/usb/usb_bus.h @@ -0,0 +1,104 @@ +/* $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_process roothub_proc; + struct root_hold_token *bus_roothold; + /* + * There are two callback processes. One for Giant locked + * callbacks. One for non-Giant locked callbacks. This should + * avoid congestion and reduce response time in most cases. + */ + struct usb2_process giant_callback_proc; + struct usb2_process non_giant_callback_proc; + struct usb2_bus_msg explore_msg[2]; + struct usb2_bus_msg detach_msg[2]; + struct usb2_bus_msg attach_msg[2]; + struct usb2_bus_msg roothub_msg[2]; + /* + * This mutex protects the USB hardware: + */ + struct mtx bus_mtx; + struct usb2_perm perm; + struct usb2_xfer_queue intr_q; + struct usb2_callout power_wdog; /* power management */ + + device_t parent; + 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; + + uint32_t hw_power_state; /* see USB_HW_POWER_XXX */ + 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/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c new file mode 100644 index 0000000..809c3bf --- /dev/null +++ b/sys/dev/usb/usb_busdma.c @@ -0,0 +1,1426 @@ +/* $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 + +static void usb2_dma_tag_create(struct usb2_dma_tag *, uint32_t, uint32_t); +static void usb2_dma_tag_destroy(struct usb2_dma_tag *); + +#ifdef __FreeBSD__ +static void usb2_dma_lock_cb(void *, bus_dma_lock_op_t); +static int32_t usb2_m_copy_in_cb(void *, void *, uint32_t); +static void usb2_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); +static void usb2_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); +static void usb2_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, + uint8_t); +#endif + +#ifdef __NetBSD__ +static int32_t usb2_m_copy_in_cb(void *, caddr_t, uint32_t); +static void usb2_pc_common_mem_cb(struct usb2_page_cache *, + bus_dma_segment_t *, int, int, uint8_t); +#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; + } +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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; + } +} + + +#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 */ +} + +/*------------------------------------------------------------------------* + * 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 */ BUS_DMA_KEEP_PG_OFFSET, + /* lockfn */ &usb2_dma_lock_cb, + /* lockarg */ NULL, + &tag)) { + tag = NULL; + } + udt->tag = tag; +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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--; +#if (USB_DEBUG != 0) + if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { + /* + * This check verifies that the physical address is correct: + */ + DPRINTFN(0, "Page offset was not preserved!\n"); + error = 1; + goto done; + } +#endif + 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); +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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; + + /* + * We have to unload the previous loaded DMA + * pages before trying to load a new one! + */ + bus_dmamap_unload(pc->tag, pc->map); + + /* + * 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 { + + /* + * We have to unload the previous loaded DMA + * pages before trying to load a new one! + */ + bus_dmamap_unload(pc->tag, pc->map); + + /* + * 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) +{ + if (pc->page_offset_end == pc->page_offset_buf) { + /* nothing has been loaded into this page cache! */ + return; + } + bus_dmamap_sync(pc->tag, pc->map, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); +} + +/*------------------------------------------------------------------------* + * usb2_pc_cpu_flush - flush CPU cache + *------------------------------------------------------------------------*/ +void +usb2_pc_cpu_flush(struct usb2_page_cache *pc) +{ + if (pc->page_offset_end == pc->page_offset_buf) { + /* nothing has been loaded into this page cache! */ + return; + } + bus_dmamap_sync(pc->tag, pc->map, + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +#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; +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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--; +#if (USB_DEBUG != 0) + if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { + /* + * This check verifies that the physical address is correct: + */ + DPRINTFN(0, "Page offset was not preserved!\n"); + error = 1; + goto done; + } +#endif + 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); +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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) { + + /* + * We have to unload the previous loaded DMA + * pages before trying to load a new one! + */ + bus_dmamap_unload(pc->tag, pc->map); + + /* 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; + + if (len == 0) { + /* nothing has been loaded into this page cache */ + return; + } + bus_dmamap_sync(pc->tag, pc->map, 0, len, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); +} + +/*------------------------------------------------------------------------* + * 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; + + if (len == 0) { + /* nothing has been loaded into this page cache */ + return; + } + bus_dmamap_sync(pc->tag, pc->map, 0, len, + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +#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++; + } +} + +/*------------------------------------------------------------------------* + * 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 + } +} + +/*------------------------------------------------------------------------* + * 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->xroot; + + mtx_assert(info->xfer_mtx, MA_OWNED); + + if (xfer->error) { + /* some error happened */ + USB_BUS_LOCK(info->bus); + usb2_transfer_done(xfer, 0); + USB_BUS_UNLOCK(info->bus); + 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) { + USB_BUS_LOCK(info->bus); + usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); + USB_BUS_UNLOCK(info->bus); + 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); +} + +/*------------------------------------------------------------------------* + * 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->xfer_mtx, MA_OWNED); + + /* copy error */ + info->dma_error = udpt->dma_error; + + /* enter workloop again */ + usb2_command_wrapper(&info->dma_q, + info->dma_q.curr); +} + +/*------------------------------------------------------------------------* + * 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->isread) { + usb2_pc_cpu_invalidate(pc); + } else { + usb2_pc_cpu_flush(pc); + } + pc++; + } +} + +/*------------------------------------------------------------------------* + * 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->isread) { + usb2_pc_cpu_invalidate(pc); + } + pc++; + } +} diff --git a/sys/dev/usb/usb_busdma.h b/sys/dev/usb/usb_busdma.h new file mode 100644 index 0000000..3c1600b --- /dev/null +++ b/sys/dev/usb/usb_busdma.h @@ -0,0 +1,183 @@ +/* $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/usb/usb_cdc.h b/sys/dev/usb/usb_cdc.h new file mode 100644 index 0000000..d1e3dcb --- /dev/null +++ b/sys/dev/usb/usb_cdc.h @@ -0,0 +1,191 @@ +/* $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 + +#endif /* _USB_CDC_H_ */ diff --git a/sys/dev/usb/usb_compat_linux.c b/sys/dev/usb/usb_compat_linux.c new file mode 100644 index 0000000..a544a98 --- /dev/null +++ b/sys/dev/usb/usb_compat_linux.c @@ -0,0 +1,1653 @@ +/* $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 *); +static int usb_start_wait_urb(struct urb *, uint32_t, uint16_t *); +static const struct usb_device_id *usb_linux_lookup_id( + const struct usb_device_id *, struct usb2_attach_arg *); +static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *); +static struct usb_device *usb_linux_create_usb_device(struct usb2_device *, + device_t); +static void usb_linux_cleanup_interface(struct usb_device *, + struct usb_interface *); +static void usb_linux_complete(struct usb2_xfer *); +static int usb_unlink_urb_sub(struct urb *, uint8_t); + +/*------------------------------------------------------------------------* + * 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; + + 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) +{ + ; /* indent fix */ + switch (usb2_get_speed(dev->bsd_udev)) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return (USB_MAX_FULL_SPEED_ISOC_FRAMES); + default: + return (USB_MAX_HIGH_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); + } +} + +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(); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * usb_buffer_free + *------------------------------------------------------------------------*/ +void +usb_buffer_free(struct usb_device *dev, uint32_t size, + void *addr, uint8_t dma_addr) +{ + free(addr, M_USBDEV); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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)); +} + +/*------------------------------------------------------------------------* + * usb_kill_urb + *------------------------------------------------------------------------*/ +void +usb_kill_urb(struct urb *urb) +{ + if (usb_unlink_urb_sub(urb, 1)) { + /* ignore */ + } +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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++; + } +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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/usb/usb_compat_linux.h b/sys/dev/usb/usb_compat_linux.h new file mode 100644 index 0000000..8ebb7e3 --- /dev/null +++ b/sys/dev/usb/usb_compat_linux.h @@ -0,0 +1,472 @@ +/* $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/usb/usb_controller.h b/sys/dev/usb/usb_controller.h new file mode 100644 index 0000000..80633d9 --- /dev/null +++ b/sys/dev/usb/usb_controller.h @@ -0,0 +1,199 @@ +/* $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); + void (*device_suspend) (struct usb2_device *udev); + void (*device_resume) (struct usb2_device *udev); + void (*set_hw_power) (struct usb2_bus *bus); + /* + * The following flag is set if one or more control transfers are + * active: + */ +#define USB_HW_POWER_CONTROL 0x01 + /* + * The following flag is set if one or more bulk transfers are + * active: + */ +#define USB_HW_POWER_BULK 0x02 + /* + * The following flag is set if one or more interrupt transfers are + * active: + */ +#define USB_HW_POWER_INTERRUPT 0x04 + /* + * The following flag is set if one or more isochronous transfers + * are active: + */ +#define USB_HW_POWER_ISOC 0x08 + /* + * The following flag is set if one or more non-root-HUB devices + * are present on the given USB bus: + */ +#define USB_HW_POWER_NON_ROOT_HUB 0x10 + + /* 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); + + /* USB Device and Host mode - Optional */ + + void (*roothub_exec) (struct usb2_bus *); +}; + +/* + * 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 */ + + 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); +void usb2_bus_roothub_exec(struct usb2_bus *bus); +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/usb/usb_core.c b/sys/dev/usb/usb_core.c new file mode 100644 index 0000000..a4191ad --- /dev/null +++ b/sys/dev/usb/usb_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(usb, 1); diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h new file mode 100644 index 0000000..cc55316 --- /dev/null +++ b/sys/dev/usb/usb_core.h @@ -0,0 +1,467 @@ +/* $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 "usb_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_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) + +#define USB_BUS_LOCK(_b) mtx_lock(&(_b)->bus_mtx) +#define USB_BUS_UNLOCK(_b) mtx_unlock(&(_b)->bus_mtx) +#define USB_BUS_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_mtx, _t) +#define USB_XFER_LOCK(_x) mtx_lock((_x)->xroot->xfer_mtx) +#define USB_XFER_UNLOCK(_x) mtx_unlock((_x)->xroot->xfer_mtx) +#define USB_XFER_LOCK_ASSERT(_x, _t) mtx_assert((_x)->xroot->xfer_mtx, _t) +/* 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_xfer_root *xroot; /* 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; /* bus index */ + uint8_t dev_index; /* device index */ + uint8_t iface_index; /* interface index */ + uint8_t fifo_index; /* FIFO index */ + uint8_t is_read; /* set if location has read access */ + uint8_t is_write; /* set if location has write access */ + uint8_t is_uref; /* set if USB refcount decr. needed */ + uint8_t is_usbfs; /* set if USB-FS is active */ +}; + +/* 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_mode(struct usb2_device *udev); +uint8_t usb2_get_speed(struct usb2_device *udev); +uint32_t usb2_get_isoc_fps(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); +uint8_t usb2_transfer_pending(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); +void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode); + +#endif /* _USB2_CORE_H_ */ diff --git a/sys/dev/usb/usb_debug.c b/sys/dev/usb/usb_debug.c new file mode 100644 index 0000000..b7eeea7 --- /dev/null +++ b/sys/dev/usb/usb_debug.c @@ -0,0 +1,152 @@ +/* $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 + +/* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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"); +} + +/*------------------------------------------------------------------------* + * 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"); + } +} + +/*------------------------------------------------------------------------* + * usb2_dump_xfer + * + * This function dumps information about an USB transfer. + *------------------------------------------------------------------------*/ +void +usb2_dump_xfer(struct usb2_xfer *xfer) +{ + struct usb2_device *udev; + printf("usb2_dump_xfer: xfer=%p\n", xfer); + if (xfer == NULL) { + return; + } + if (xfer->pipe == NULL) { + printf("xfer %p: pipe=NULL\n", + xfer); + return; + } + udev = xfer->xroot->udev; + printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d " + "pipe=%p ep=0x%02x attr=0x%02x\n", + xfer, udev, + UGETW(udev->ddesc.idVendor), + UGETW(udev->ddesc.idProduct), + udev->address, xfer->pipe, + xfer->pipe->edesc->bEndpointAddress, + xfer->pipe->edesc->bmAttributes); +} diff --git a/sys/dev/usb/usb_debug.h b/sys/dev/usb/usb_debug.h new file mode 100644 index 0000000..92dcbd5 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_defs.h b/sys/dev/usb/usb_defs.h new file mode 100644 index 0000000..64caf39 --- /dev/null +++ b/sys/dev/usb/usb_defs.h @@ -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. + */ + +#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_ROOT_HUB_ADDR 1 /* index */ + +#define USB_MIN_DEVICES 2 /* unused + root HUB */ + +#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 "There cannot be less FIFOs than USB endpoints." +#endif +#if (USB_FIFO_MAX & 1) +#error "Number of FIFOs must be odd." +#endif +#if (USB_EP_MAX < (2*16)) +#error "Number of hardware USB endpoints cannot be less than 32." +#endif +#if (USB_MAX_DEVICES < USB_MIN_DEVICES) +#error "Minimum number of devices is greater than maximum number of devices." +#endif +#if (USB_ROOT_HUB_ADDR >= USB_MIN_DEVICES) +#error "The root hub address must be less than USB_MIN_DEVICES." +#endif +#endif /* _USB2_DEFS_H_ */ diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c new file mode 100644 index 0000000..f8e2c96 --- /dev/null +++ b/sys/dev/usb/usb_dev.c @@ -0,0 +1,2814 @@ +/* $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 **); +static uint32_t usb2_path_convert(const char *); +static int usb2_check_access(int, struct usb2_perm *); +static int usb2_fifo_open(struct usb2_fifo *, struct file *, + struct thread *, int); +static void usb2_fifo_close(struct usb2_fifo *, struct thread *, int); +static void usb2_dev_init(void *); +static void usb2_dev_init_post(void *); +static void usb2_dev_uninit(void *); +static int usb2_fifo_uiomove(struct usb2_fifo *, void *, int, + struct uio *); +static void usb2_fifo_check_methods(struct usb2_fifo_methods *); +static void usb2_clone(void *, USB_UCRED char *, int, struct cdev **); +static struct usb2_fifo *usb2_fifo_alloc(void); +static struct usb2_pipe *usb2_dev_get_pipe(struct usb2_device *, uint8_t, + uint8_t, uint8_t); + +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); + + } +} + +/*------------------------------------------------------------------------* + * 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 dev_ep_index; + + if (fp) { + /* check if we need uref */ + ploc->is_uref = devloc ? 0 : 1; + /* get devloc - already verified */ + devloc = USB_P2U(fp->f_data); + /* get file flags */ + fflags = fp->f_flag; + } else { + /* 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->fifo_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; + } + /* 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; + ploc->is_usbfs = 0; + /* NOTE: variable overloading: */ + dev_ep_index = ploc->fifo_index; + } else { + /* initialise "is_usbfs" flag */ + ploc->is_usbfs = 0; + dev_ep_index = 255; /* dummy */ + + /* check for write */ + if (fflags & FWRITE) { + ppf = ploc->udev->fifo; + f = ppf[ploc->fifo_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; + } + /* check if USB-FS is active */ + if (f->fs_ep_max != 0) { + ploc->is_usbfs = 1; + } + /* + * Get real endpoint index associated with + * this FIFO: + */ + dev_ep_index = f->dev_ep_index; + } else { + ploc->txfifo = NULL; + ploc->is_write = 0; /* no ref */ + } + + /* check for read */ + if (fflags & FREAD) { + ppf = ploc->udev->fifo; + f = ppf[ploc->fifo_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; + } + /* check if USB-FS is active */ + if (f->fs_ep_max != 0) { + ploc->is_usbfs = 1; + } + /* + * Get real endpoint index associated with + * this FIFO: + */ + dev_ep_index = f->dev_ep_index; + } else { + ploc->rxfifo = NULL; + ploc->is_read = 0; /* no ref */ + } + } + + /* check if we require an interface */ + ploc->iface = usb2_get_iface(ploc->udev, ploc->iface_index); + if (dev_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; + } + } + /* when everything is OK we increment the refcounts */ + if (ploc->is_write) { + DPRINTFN(2, "ref write\n"); + ploc->txfifo->refcount++; + } + if (ploc->is_read) { + DPRINTFN(2, "ref read\n"); + ploc->rxfifo->refcount++; + } + if (ploc->is_uref) { + DPRINTFN(2, "ref udev - needed\n"); + ploc->udev->refcount++; + } + 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_uref_location + * + * This function is used to upgrade an USB reference to include the + * USB device reference on a USB location. + * + * Return values: + * 0: Success, refcount incremented on the given USB device. + * Else: Failure. + *------------------------------------------------------------------------*/ +static usb2_error_t +usb2_uref_location(struct usb2_location *ploc) +{ + /* + * Check if we already got an USB reference on this location: + */ + if (ploc->is_uref) { + return (0); /* success */ + } + mtx_lock(&usb2_ref_lock); + if (ploc->bus != devclass_get_softc(usb2_devclass_ptr, ploc->bus_index)) { + DPRINTFN(2, "bus changed at %u\n", ploc->bus_index); + goto error; + } + if (ploc->udev != ploc->bus->devices[ploc->dev_index]) { + DPRINTFN(2, "device changed at %u\n", ploc->dev_index); + goto error; + } + if (ploc->udev->refcount == USB_DEV_REF_MAX) { + DPRINTFN(2, "no dev ref\n"); + goto error; + } + DPRINTFN(2, "ref udev\n"); + ploc->udev->refcount++; + mtx_unlock(&usb2_ref_lock); + + /* set "uref" */ + ploc->is_uref = 1; + + /* + * 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); +} + +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; + + /* NOTE: variable overloading: */ + uint8_t dev_ep_index = ploc->fifo_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; + 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; + 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); +} + +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; + } + } +} + +/*------------------------------------------------------------------------* + * 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"); +} + +/*------------------------------------------------------------------------* + * 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; + + if (ep_index != 0) { + /* + * Non-control endpoints are always + * associated with an interface: + */ + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (EINVAL); + } + if (iface->idesc == NULL) { + return (EINVAL); + } + } else { + iface = NULL; + } + /* scan down the permissions tree */ + if ((iface != NULL) && + (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); + } + /* + * NOTE: Variable overloading. "usb2_fifo_create" will update + * the FIFO index. Right here we can assume that the + * "fifo_index" is the same like the endpoint number without + * direction mask, if the "fifo_index" is less than 16. + */ + err = usb2_check_thread_perm(loc.udev, td, fflags, + loc.iface_index, loc.fifo_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; +} + +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); +} + +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 0666, because we + * check the permissions again in the open routine against the + * real USB permissions which are not 0666. Else USB access + * will be limited to one user and one group. + */ + usb2_dev = make_dev(&usb2_devsw, 0, UID_ROOT, GID_OPERATOR, + 0666, 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"); + } +} + +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); +} + +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 /* need uref */ );; + + /* 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 (ENOIOCTL); + } + 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; + struct usb2_fifo *f; + int fflags; + int err; + + err = usb2_ref_device(fp, &loc, 1 /* no uref */ );; + if (err) { + return (ENXIO); + } + fflags = fp->f_flag; + + DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd); + + f = NULL; /* set default value */ + err = ENOIOCTL; /* set default value */ + + if (fflags & FWRITE) { + f = loc.txfifo; + err = usb2_ioctl_f_sub(f, cmd, addr, td); + } + if (fflags & FREAD) { + f = loc.rxfifo; + err = usb2_ioctl_f_sub(f, cmd, addr, td); + } + if (err == ENOIOCTL) { + err = (f->methods->f_ioctl) (f, cmd, addr, fflags, td); + if (err == ENOIOCTL) { + if (usb2_uref_location(&loc)) { + err = ENXIO; + goto done; + } + err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags, td); + } + } + if (err == ENOIOCTL) { + err = ENOTTY; + } +done: + 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; + + revents = usb2_ref_device(fp, &loc, 1 /* no uref */ );; + if (revents) { + return (POLLHUP); + } + fflags = fp->f_flag; + + /* Figure out who needs service */ + + if ((events & (POLLOUT | POLLWRNORM)) && + (fflags & FWRITE)) { + + f = loc.txfifo; + + mtx_lock(f->priv_mtx); + + if (!loc.is_usbfs) { + 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 (!loc.is_usbfs) { + 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); + + if (!loc.is_usbfs) { + /* 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); + + /* check for permanent read error */ + if (f->flag_iserror) { + err = EIO; + goto done; + } + /* check if USB-FS interface is active */ + if (loc.is_usbfs) { + /* + * The queue is used for events that should be + * retrieved using the "USB_FS_COMPLETE" ioctl. + */ + err = EINVAL; + goto done; + } + while (uio->uio_resid > 0) { + + USB_IF_DEQUEUE(&f->used_q, m); + + 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; + } + if (f->methods->f_filter_read) { + /* + * Sometimes it is convenient to process data at the + * expense of a userland process instead of a kernel + * process. + */ + (f->methods->f_filter_read) (f, m); + } + 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); + + /* check for permanent write error */ + if (f->flag_iserror) { + err = EIO; + goto done; + } + /* check if USB-FS interface is active */ + if (loc.is_usbfs) { + /* + * The queue is used for events that should be + * retrieved using the "USB_FS_COMPLETE" ioctl. + */ + err = EINVAL; + goto done; + } + if (f->queue_data == NULL) { + /* start write transfer, if not already started */ + (f->methods->f_start_write) (f); + } + /* we allow writing zero length data */ + do { + USB_IF_DEQUEUE(&f->free_q, m); + + 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; + } + 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; + } + if (f->methods->f_filter_write) { + /* + * Sometimes it is convenient to process data at the + * expense of a userland process instead of a kernel + * process. + */ + (f->methods->f_filter_write) (f, m); + } + 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); + } +} + +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); + } +} + +/*------------------------------------------------------------------------* + * 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 (ENOIOCTL); +} + +static void +usb2_fifo_dummy_cmd(struct usb2_fifo *fifo) +{ + fifo->flag_flushing = 0; /* not flushing */ +} + +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_ioctl_post == NULL) + pm->f_ioctl_post = &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; +} + +/*------------------------------------------------------------------------* + * 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_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_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)); +} + +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); +} + +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; + } + } +} + +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; + } + } +} + +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); +} + +/*------------------------------------------------------------------------* + * 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_POLL(&f->used_q, m); + + if (m) { + *plen = m->cur_data_len; + *pptr = m->cur_data_ptr; + + return (1); + } + return (0); +} + +void +usb2_fifo_get_data_error(struct usb2_fifo *f) +{ + f->flag_iserror = 1; + usb2_fifo_wakeup(f); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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/usb/usb_dev.h b/sys/dev/usb/usb_dev.h new file mode 100644 index 0000000..6203572 --- /dev/null +++ b/sys/dev/usb/usb_dev.h @@ -0,0 +1,168 @@ +/* $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; +struct usb2_mbuf; + +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); +typedef void (usb2_fifo_filter_t)(struct usb2_fifo *fifo, struct usb2_mbuf *m); + +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" and "usb2_fifo_filter_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; + /* + * NOTE: The post-ioctl callback is called after the USB reference + * gets locked in the IOCTL handler: + */ + usb2_fifo_ioctl_t *f_ioctl_post; + 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; + usb2_fifo_filter_t *f_filter_read; + usb2_fifo_filter_t *f_filter_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_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_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/usb/usb_device.c b/sys/dev/usb/usb_device.c new file mode 100644 index 0000000..439ad2b --- /dev/null +++ b/sys/dev/usb/usb_device.c @@ -0,0 +1,2192 @@ +/* $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 "usbdevs.h" + +#define USB_DEBUG_VAR usb2_debug + +#include +#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 *, uint8_t, + struct usb2_endpoint_descriptor *, struct usb2_pipe *); +static void usb2_free_pipe_data(struct usb2_device *, uint8_t, uint8_t); +static void usb2_free_iface_data(struct usb2_device *); +static void usb2_detach_device_sub(struct usb2_device *, device_t *, + uint8_t); +static uint8_t usb2_probe_and_attach_sub(struct usb2_device *, + struct usb2_attach_arg *); +static void usb2_init_attach_arg(struct usb2_device *, + struct usb2_attach_arg *); +static void usb2_suspend_resume_sub(struct usb2_device *, device_t, + uint8_t); +static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm); +static void usb2_check_strings(struct usb2_device *); +static usb2_error_t usb2_fill_iface_data(struct usb2_device *, uint8_t, + uint8_t); +static void usb2_notify_addq(const char *type, struct usb2_device *); +static void usb2_fifo_free_wrap(struct usb2_device *, uint8_t, uint8_t); + +/* 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) { + USB_BUS_LOCK(udev->bus); + (udev->bus->methods->clear_stall) (udev, pipe); + USB_BUS_UNLOCK(udev->bus); + } +} + +/*------------------------------------------------------------------------* + * 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++; + } +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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 generic FIFOs for this interface, except control + * endpoint FIFOs: + */ + 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); + } + USB_BUS_LOCK(udev->bus); + + /* 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 */ + USB_BUS_UNLOCK(udev->bus); + 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); + } + USB_BUS_UNLOCK(udev->bus); + 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"); +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +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; +} + +/*------------------------------------------------------------------------* + * 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"); + } +} + +/*------------------------------------------------------------------------* + * usb2_suspend_resume + * + * 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); + + USB_BUS_LOCK(udev->bus); + /* filter the suspend events */ + if (udev->flags.suspended == do_suspend) { + USB_BUS_UNLOCK(udev->bus); + /* nothing to do */ + return (0); + } + udev->flags.suspended = do_suspend; + USB_BUS_UNLOCK(udev->bus); + + /* 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 */ + USB_BUS_UNLOCK(udev->bus); + mtx_lock(udev->default_mtx); + + /* Start clear stall callback */ + usb2_transfer_start(udev->default_xfer[1]); + + /* Change lock */ + mtx_unlock(udev->default_mtx); + USB_BUS_LOCK(udev->bus); +} + +/*------------------------------------------------------------------------* + * 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. + * + * Device index zero is not used and device index 1 should + * always be the root hub. + */ + for (device_index = USB_ROOT_HUB_ADDR; + (device_index != bus->devices_max) && + (bus->devices[device_index] != NULL); + device_index++) /* nop */; + + if (device_index == bus->devices_max) { + 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; + /* + * We need to force the power mode to "on" because there are plenty + * of USB devices out there that do not work very well with + * automatic suspend and resume! + */ + udev->power_mode = USB_POWER_MODE_ON; + udev->pwr_save.last_xfer_time = ticks; + + /* 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; + + /* speed combination should be checked by the parent HUB */ + + hub = udev->parent_hub; + + /* 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_MS_TO_TICKS(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); + /* XXX try to re-enumerate the device */ + err = usb2_req_re_enumerate(udev, &Giant); + if (err) { + 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; + uint8_t set_config_failed = 0; + + /* + * 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) { + if (udev->ddesc.bNumConfigurations != 0) { + if (!set_config_failed) { + set_config_failed = 1; + /* XXX try to re-enumerate the device */ + err = usb2_req_re_enumerate( + udev, &Giant); + if (err == 0) + goto repeat_set_config; + } + DPRINTFN(0, "Failure selecting " + "configuration index %u: %s, port %u, " + "addr %u (ignored)\n", + config_index, usb2_errstr(err), udev->port_no, + udev->address); + } + /* + * Some USB devices do not have any + * configurations. Ignore any set config + * failures! + */ + err = 0; + } 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) == 0) { + DPRINTFN(0, "Found possible auto-install " + "disk (trying next config)\n"); + config_index++; + goto repeat_set_config; + } + } + } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 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; + + printf("ugen%u.%u: <%s> at %s (disconnected)\n", + device_get_unit(bus->bdev), + udev->device_index, udev->manufacturer, + device_get_nameunit(bus->bdev)); + + /* + * 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); + + /* template unsetup, if any */ + (usb2_temp_unsetup_p) (udev); + + /* + * Make sure that our clear-stall messages are not queued + * anywhere: + */ + USB_BUS_LOCK(udev->bus); + usb2_proc_mwait(&udev->bus->non_giant_callback_proc, + &udev->cs_msg[0], &udev->cs_msg[1]); + USB_BUS_UNLOCK(udev->bus); + + 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); +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +#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 "usbdevs.h" +#include "usbdevs_data.h" +#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); + } +} + +/* + * Returns: + * See: USB_MODE_XXX + */ +uint8_t +usb2_get_mode(struct usb2_device *udev) +{ + return (udev->flags.usb2_mode); +} + +/* + * Returns: + * See: USB_SPEED_XXX + */ +uint8_t +usb2_get_speed(struct usb2_device *udev) +{ + return (udev->speed); +} + +uint32_t +usb2_get_isoc_fps(struct usb2_device *udev) +{ + ; /* indent fix */ + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return (1000); + default: + return (8000); + } +} + +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); +} + +/*------------------------------------------------------------------------* + * usb2_fifo_free_wrap + * + * This function will free the FIFOs. + * + * Flag values, if "iface_index" is equal to "USB_IFACE_INDEX_ANY". + * 0: Free all FIFOs except generic control endpoints. + * 1: Free all FIFOs. + * + * Flag values, if "iface_index" is not equal to "USB_IFACE_INDEX_ANY". + * Not used. + *------------------------------------------------------------------------*/ +static void +usb2_fifo_free_wrap(struct usb2_device *udev, + uint8_t iface_index, uint8_t flag) +{ + struct usb2_fifo *f; + 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; + } + /* Check if the interface index matches */ + if (iface_index == f->iface_index) { + if (f->methods != &usb2_ugen_methods) { + /* + * Don't free any non-generic FIFOs in + * this case. + */ + continue; + } + if ((f->dev_ep_index == 0) && + (f->fs_xfer == NULL)) { + /* no need to free this FIFO */ + continue; + } + } else if (iface_index == USB_IFACE_INDEX_ANY) { + if ((f->methods == &usb2_ugen_methods) && + (f->dev_ep_index == 0) && (flag == 0) && + (f->fs_xfer == NULL)) { + /* no need to free this FIFO */ + continue; + } + } else { + /* no need to free this FIFO */ + continue; + } + /* free this FIFO */ + usb2_fifo_free(f); + } +} + +/*------------------------------------------------------------------------* + * usb2_peer_can_wakeup + * + * Return values: + * 0: Peer cannot do resume signalling. + * Else: Peer can do resume signalling. + *------------------------------------------------------------------------*/ +uint8_t +usb2_peer_can_wakeup(struct usb2_device *udev) +{ + const struct usb2_config_descriptor *cdp; + + cdp = udev->cdesc; + if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) { + return (cdp->bmAttributes & UC_REMOTE_WAKEUP); + } + return (0); /* not supported */ +} diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h new file mode 100644 index 0000000..11686e2 --- /dev/null +++ b/sys/dev/usb/usb_device.h @@ -0,0 +1,187 @@ +/* $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 is used for power-save purposes. The data + * in this structure is protected by the USB BUS lock. + */ +struct usb2_power_save { + int last_xfer_time; /* copy of "ticks" */ + uint32_t type_refs[4]; /* transfer reference count */ + uint32_t read_refs; /* data read references */ + uint32_t write_refs; /* data write references */ + uint8_t suspended; /* set if USB device is suspended */ +}; + +/* + * 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_power_save pwr_save;/* power save data */ + + 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); +uint8_t usb2_peer_can_wakeup(struct usb2_device *udev); + +#endif /* _USB2_DEVICE_H_ */ diff --git a/sys/dev/usb/usb_dynamic.c b/sys/dev/usb/usb_dynamic.c new file mode 100644 index 0000000..6983f64 --- /dev/null +++ b/sys/dev/usb/usb_dynamic.c @@ -0,0 +1,155 @@ +/* $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_test_huawei_autoinst_t usb2_test_huawei_autoinst_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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_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; +} + +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; + } +} + +static uint8_t +usb2_test_huawei_autoinst_w(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + return (USB_ERR_INVAL); +} + +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); +} + +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); +} + +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); +} + +void +usb2_test_huawei_unload(void *arg) +{ + /* reset function pointers */ + + usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w; + + /* wait for CPU to exit the loaded functions, if any */ + + /* XXX this is a tradeoff */ + + pause("WAIT", 16*hz); +} diff --git a/sys/dev/usb/usb_dynamic.h b/sys/dev/usb/usb_dynamic.h new file mode 100644 index 0000000..2c45d09 --- /dev/null +++ b/sys/dev/usb/usb_dynamic.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. + */ + +#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 usb2_error_t (usb2_test_huawei_autoinst_t)(struct usb2_device *udev, + struct usb2_attach_arg *uaa); +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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p; +extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p; +extern devclass_t usb2_devclass_ptr; + +/* function prototypes */ + +void usb2_test_huawei_unload(void *); +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/usb/usb_endian.h b/sys/dev/usb/usb_endian.h new file mode 100644 index 0000000..2f49008 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_error.c b/sys/dev/usb/usb_error.c new file mode 100644 index 0000000..daece10 --- /dev/null +++ b/sys/dev/usb/usb_error.c @@ -0,0 +1,73 @@ +/* $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 + +static const char* usb_errstr_table[USB_ERR_MAX] = { + [USB_ERR_NORMAL_COMPLETION] = "USB_ERR_NORMAL_COMPLETION", + [USB_ERR_PENDING_REQUESTS] = "USB_ERR_PENDING_REQUESTS", + [USB_ERR_NOT_STARTED] = "USB_ERR_NOT_STARTED", + [USB_ERR_INVAL] = "USB_ERR_INVAL", + [USB_ERR_NOMEM] = "USB_ERR_NOMEM", + [USB_ERR_CANCELLED] = "USB_ERR_CANCELLED", + [USB_ERR_BAD_ADDRESS] = "USB_ERR_BAD_ADDRESS", + [USB_ERR_BAD_BUFSIZE] = "USB_ERR_BAD_BUFSIZE", + [USB_ERR_BAD_FLAG] = "USB_ERR_BAD_FLAG", + [USB_ERR_NO_CALLBACK] = "USB_ERR_NO_CALLBACK", + [USB_ERR_IN_USE] = "USB_ERR_IN_USE", + [USB_ERR_NO_ADDR] = "USB_ERR_NO_ADDR", + [USB_ERR_NO_PIPE] = "USB_ERR_NO_PIPE", + [USB_ERR_ZERO_NFRAMES] = "USB_ERR_ZERO_NFRAMES", + [USB_ERR_ZERO_MAXP] = "USB_ERR_ZERO_MAXP", + [USB_ERR_SET_ADDR_FAILED] = "USB_ERR_SET_ADDR_FAILED", + [USB_ERR_NO_POWER] = "USB_ERR_NO_POWER", + [USB_ERR_TOO_DEEP] = "USB_ERR_TOO_DEEP", + [USB_ERR_IOERROR] = "USB_ERR_IOERROR", + [USB_ERR_NOT_CONFIGURED] = "USB_ERR_NOT_CONFIGURED", + [USB_ERR_TIMEOUT] = "USB_ERR_TIMEOUT", + [USB_ERR_SHORT_XFER] = "USB_ERR_SHORT_XFER", + [USB_ERR_STALLED] = "USB_ERR_STALLED", + [USB_ERR_INTERRUPTED] = "USB_ERR_INTERRUPTED", + [USB_ERR_DMA_LOAD_FAILED] = "USB_ERR_DMA_LOAD_FAILED", + [USB_ERR_BAD_CONTEXT] = "USB_ERR_BAD_CONTEXT", + [USB_ERR_NO_ROOT_HUB] = "USB_ERR_NO_ROOT_HUB", + [USB_ERR_NO_INTR_THREAD] = "USB_ERR_NO_INTR_THREAD", + [USB_ERR_NOT_LOCKED] = "USB_ERR_NOT_LOCKED", +}; + +/*------------------------------------------------------------------------* + * 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_errstr_table[err] : "USB_ERR_UNKNOWN"); +} diff --git a/sys/dev/usb/usb_error.h b/sys/dev/usb/usb_error.h new file mode 100644 index 0000000..91f0245 --- /dev/null +++ b/sys/dev/usb/usb_error.h @@ -0,0 +1,63 @@ +/* $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_ + +enum { /* keep in sync with usb_errstr_table */ + USB_ERR_NORMAL_COMPLETION = 0, + USB_ERR_PENDING_REQUESTS, /* 1 */ + USB_ERR_NOT_STARTED, /* 2 */ + USB_ERR_INVAL, /* 3 */ + USB_ERR_NOMEM, /* 4 */ + USB_ERR_CANCELLED, /* 5 */ + USB_ERR_BAD_ADDRESS, /* 6 */ + USB_ERR_BAD_BUFSIZE, /* 7 */ + USB_ERR_BAD_FLAG, /* 8 */ + USB_ERR_NO_CALLBACK, /* 9 */ + USB_ERR_IN_USE, /* 10 */ + USB_ERR_NO_ADDR, /* 11 */ + USB_ERR_NO_PIPE, /* 12 */ + USB_ERR_ZERO_NFRAMES, /* 13 */ + USB_ERR_ZERO_MAXP, /* 14 */ + USB_ERR_SET_ADDR_FAILED, /* 15 */ + USB_ERR_NO_POWER, /* 16 */ + USB_ERR_TOO_DEEP, /* 17 */ + USB_ERR_IOERROR, /* 18 */ + USB_ERR_NOT_CONFIGURED, /* 19 */ + USB_ERR_TIMEOUT, /* 20 */ + USB_ERR_SHORT_XFER, /* 21 */ + USB_ERR_STALLED, /* 22 */ + USB_ERR_INTERRUPTED, /* 23 */ + USB_ERR_DMA_LOAD_FAILED, /* 24 */ + USB_ERR_BAD_CONTEXT, /* 25 */ + USB_ERR_NO_ROOT_HUB, /* 26 */ + USB_ERR_NO_INTR_THREAD, /* 27 */ + USB_ERR_NOT_LOCKED, /* 28 */ + USB_ERR_MAX +}; + +#endif /* _USB2_ERROR_H_ */ diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c new file mode 100644 index 0000000..15ca909 --- /dev/null +++ b/sys/dev/usb/usb_generic.c @@ -0,0 +1,2195 @@ +/* $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_ioctl_t ugen_ioctl_post; +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 *, + const struct usb2_config *, uint8_t); +static int ugen_open_pipe_write(struct usb2_fifo *); +static int ugen_open_pipe_read(struct usb2_fifo *); +static int ugen_set_config(struct usb2_fifo *, uint8_t); +static int ugen_set_interface(struct usb2_fifo *, uint8_t, uint8_t); +static int ugen_get_cdesc(struct usb2_fifo *, struct usb2_gen_descriptor *); +static int ugen_get_sdesc(struct usb2_fifo *, struct usb2_gen_descriptor *); +static int ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd); +static int usb2_gen_fill_deviceinfo(struct usb2_fifo *, + struct usb2_device_info *); +static int ugen_re_enumerate(struct usb2_fifo *); +static int ugen_iface_ioctl(struct usb2_fifo *, u_long, void *, int); +static uint8_t ugen_fs_get_complete(struct usb2_fifo *, uint8_t *); +static int ugen_fs_uninit(struct usb2_fifo *f); + +/* structures */ + +struct usb2_fifo_methods usb2_ugen_methods = { + .f_open = &ugen_open, + .f_close = &ugen_close, + .f_ioctl = &ugen_ioctl, + .f_ioctl_post = &ugen_ioctl_post, + .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); + switch (usb2_get_speed(f->udev)) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + f->nframes = UGEN_HW_FRAMES; + f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; + break; + default: + f->nframes = UGEN_HW_FRAMES * 8; + f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; + break; + } + + 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 */ + DPRINTFN(6, "no FIFOs\n"); + } +} + +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); + } + 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); + } + 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]); +} + +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]); +} + +static void +ugen_stop_io(struct usb2_fifo *f) +{ + /* stop transfers */ + usb2_transfer_stop(f->xfer[0]); + usb2_transfer_stop(f->xfer[1]); +} + +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; + } +} + +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; + } +} + +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); + } +} + +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); + } +} + +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; + } +} + +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; + } +} + +static int +ugen_set_config(struct usb2_fifo *f, uint8_t index) +{ + DPRINTFN(2, "index %u\n", index); + + 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); + } + /* make sure all FIFO's are gone */ + /* else there can be a deadlock */ + if (ugen_fs_uninit(f)) { + /* ignore any errors */ + DPRINTFN(6, "no FIFOs\n"); + } + /* 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->udev->flags.usb2_mode != USB_MODE_HOST) { + /* not possible in device side mode */ + return (ENOTTY); + } + /* make sure all FIFO's are gone */ + /* else there can be a deadlock */ + if (ugen_fs_uninit(f)) { + /* ignore any errors */ + DPRINTFN(6, "no FIFOs\n"); + } + /* 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 (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 (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); +} + +/*------------------------------------------------------------------------* + * ugen_get_iface_driver + * + * This function generates an USB interface description for userland. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static int +ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd) +{ + struct usb2_device *udev = f->udev; + struct usb2_interface *iface; + const char *ptr; + const char *desc; + unsigned int len; + unsigned int maxlen; + char buf[128]; + int error; + + DPRINTFN(6, "\n"); + + if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { + /* userland pointer should not be zero */ + return (EINVAL); + } + + iface = usb2_get_iface(udev, ugd->ugd_iface_index); + if ((iface == NULL) || (iface->idesc == NULL)) { + /* invalid interface index */ + return (EINVAL); + } + + /* read out device nameunit string, if any */ + if ((iface->subdev != NULL) && + device_is_attached(iface->subdev) && + (ptr = device_get_nameunit(iface->subdev)) && + (desc = device_get_desc(iface->subdev))) { + + /* print description */ + snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); + + /* range checks */ + maxlen = ugd->ugd_maxlen - 1; + len = strlen(buf); + if (len > maxlen) + len = maxlen; + + /* update actual length, including terminating zero */ + ugd->ugd_actlen = len + 1; + + /* copy out interface description */ + error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); + } else { + /* zero length string is default */ + error = copyout("", ugd->ugd_data, 1); + } + return (error); +} + +/*------------------------------------------------------------------------* + * 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; + + 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 (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; + + /* + * This request can be useful for testing USB drivers: + */ + error = priv_check(curthread, PRIV_DRIVER); + if (error) { + return (error); + } + /* get the device unconfigured */ + error = ugen_set_config(f, USB_UNCONFIG_INDEX); + if (error) { + return (error); + } + /* do a bus-reset */ + mtx_lock(f->priv_mtx); + error = usb2_req_re_enumerate(udev, f->priv_mtx); + mtx_unlock(f->priv_mtx); + + if (error) { + return (ENXIO); + } + /* restore configuration to index 0 */ + error = ugen_set_config(f, 0); + if (error) { + return (error); + } + return (0); +} + +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; + 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); +} + +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_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags, + struct thread *td) +{ + 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_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; + + DPRINTFN(6, "cmd=0x%08lx\n", cmd); + + 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_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; + } 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 = ENOIOCTL; + break; + } + + DPRINTFN(6, "error=%d\n", error); + + 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; + uint8_t old_mode; + + if ((udev == NULL) || + (udev->parent_hub == NULL)) { + return (EINVAL); + } + err = priv_check(curthread, PRIV_ROOT); + if (err) + return (err); + + /* get old power mode */ + old_mode = udev->power_mode; + + /* if no change, then just return */ + if (old_mode == mode) + return (0); + + switch (mode) { + case USB_POWER_MODE_OFF: + /* get the device unconfigured */ + err = ugen_set_config(f, USB_UNCONFIG_INDEX); + if (err) { + DPRINTFN(0, "Could not unconfigure " + "device (ignored)\n"); + } + + /* 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: + case USB_POWER_MODE_SAVE: + break; + + case USB_POWER_MODE_RESUME: + err = usb2_req_clear_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + mode = USB_POWER_MODE_SAVE; + break; + + case USB_POWER_MODE_SUSPEND: + err = usb2_req_set_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + mode = USB_POWER_MODE_SAVE; + break; + + default: + return (EINVAL); + } + + if (err) + return (ENXIO); /* I/O failure */ + + /* if we are powered off we need to re-enumerate first */ + if (old_mode == USB_POWER_MODE_OFF) { + err = ugen_re_enumerate(f); + if (err) + return (err); + } + + /* set new power mode */ + usb2_set_power_mode(udev, 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 = ENOIOCTL; + break; + } + return (error); +} + +static int +ugen_ioctl_post(struct usb2_fifo *f, u_long cmd, void *addr, int fflags, + struct thread *td) +{ + 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; + struct usb2_fs_init *pinit; + struct usb2_fs_uninit *puninit; + 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; + + DPRINTFN(6, "cmd=0x%08lx\n", cmd); + + 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_GET_IFACE_DRIVER: + error = ugen_get_iface_driver(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_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; + + 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->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; + + default: + mtx_lock(f->priv_mtx); + error = ugen_iface_ioctl(f, cmd, addr, fflags); + mtx_unlock(f->priv_mtx); + break; + } + 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; + } +} diff --git a/sys/dev/usb/usb_generic.h b/sys/dev/usb/usb_generic.h new file mode 100644 index 0000000..3a4e7c9 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_handle_request.c b/sys/dev/usb/usb_handle_request.c new file mode 100644 index 0000000..05a739a --- /dev/null +++ b/sys/dev/usb/usb_handle_request.c @@ -0,0 +1,756 @@ +/* $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 *, uint8_t); +static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *, uint8_t); +static usb2_error_t usb2_handle_request(struct usb2_xfer *); +static usb2_error_t usb2_handle_set_config(struct usb2_xfer *, uint8_t); +static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *, uint8_t, + uint8_t); +static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *, void **, + uint16_t *, struct usb2_device_request, uint16_t, + uint8_t); + +/*------------------------------------------------------------------------* + * 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->xroot->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); +} + +/*------------------------------------------------------------------------* + * 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) +{ + struct usb2_device *udev = xfer->xroot->udev; + usb2_error_t err = 0; + + /* + * We need to protect against other threads doing probe and + * attach: + */ + USB_XFER_UNLOCK(xfer); + mtx_lock(&Giant); /* XXX */ + sx_xlock(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(udev, conf_no)) { + DPRINTF("set config %d failed\n", conf_no); + err = USB_ERR_STALLED; + goto done; + } + if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { + DPRINTF("probe and attach failed\n"); + err = USB_ERR_STALLED; + goto done; + } +done: + mtx_unlock(&Giant); /* XXX */ + sx_unlock(udev->default_sx + 1); + USB_XFER_LOCK(xfer); + 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->xroot->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: + */ + USB_XFER_UNLOCK(xfer); + 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 = USB_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 = USB_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); + USB_XFER_LOCK(xfer); + return (0); + +tr_stalled: + mtx_unlock(&Giant); + sx_unlock(udev->default_sx + 1); + USB_XFER_LOCK(xfer); + 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) +{ + struct usb2_device *udev = xfer->xroot->udev; + usb2_error_t err; + + USB_XFER_UNLOCK(xfer); + err = usb2_set_endpoint_stall(udev, + usb2_get_pipe_by_addr(udev, ep), do_stall); + USB_XFER_LOCK(xfer); + 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); + } + USB_BUS_LOCK(udev->bus); + halted = pipe->is_stalled; + USB_BUS_UNLOCK(udev->bus); + + 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->xroot->udev; + bus = udev->bus; + + USB_BUS_LOCK(bus); + + if (is_on) { + udev->flags.remote_wakeup = 1; + } else { + udev->flags.remote_wakeup = 0; + } + + USB_BUS_UNLOCK(bus); + + /* In case we are out of sync, update the power state. */ + + usb2_bus_power_update(udev->bus); + + 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->xroot->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; + + USB_BUS_LOCK(udev->bus); + if (udev->flags.remote_wakeup) { + wValue |= UDS_REMOTE_WAKEUP; + } + if (udev->flags.self_powered) { + wValue |= UDS_SELF_POWERED; + } + USB_BUS_UNLOCK(udev->bus); + + 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/usb/usb_handle_request.h b/sys/dev/usb/usb_handle_request.h new file mode 100644 index 0000000..6cc0503 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_hid.c b/sys/dev/usb/usb_hid.c new file mode 100644 index 0000000..2adf208 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_hid.h b/sys/dev/usb/usb_hid.h new file mode 100644 index 0000000..3fb7368 --- /dev/null +++ b/sys/dev/usb/usb_hid.h @@ -0,0 +1,95 @@ +/* $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/usb/usb_hub.c b/sys/dev/usb/usb_hub.c new file mode 100644 index 0000000..a0b4e851 --- /dev/null +++ b/sys/dev/usb/usb_hub.c @@ -0,0 +1,1842 @@ +/* $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 +#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 */ +#define UHUB_N_TRANSFER 1 + +#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 + +static int usb2_power_timeout = 30; /* seconds */ + +SYSCTL_INT(_hw_usb2, OID_AUTO, power_timeout, CTLFLAG_RW, + &usb2_power_timeout, 0, "USB power timeout"); + +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[UHUB_N_TRANSFER]; /* interrupt xfer */ + uint8_t sc_flags; +#define UHUB_FLAG_DID_EXPLORE 0x01 + 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 device_suspend_t uhub_suspend; +static device_resume_t uhub_resume; + +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 void usb2_dev_resume_peer(struct usb2_device *udev); +static void usb2_dev_suspend_peer(struct usb2_device *udev); + +static const struct usb2_config uhub_config[UHUB_N_TRANSFER] = { + + [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, + }, +}; + +/* + * 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, uhub_suspend), + DEVMETHOD(device_resume, uhub_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_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: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* + * Do a clear-stall. The "stall_pipe" flag + * will get cleared before next callback by + * the USB stack. + */ + xfer->flags.stall_pipe = 1; + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + } + break; + } +} + +/*------------------------------------------------------------------------* + * 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); + + if (sc->sc_st.port_status & UPS_SUSPEND) { + DPRINTF("Port %d was still " + "suspended, clearing.\n", portno); + err = usb2_req_clear_port_feature(sc->sc_udev, + &Giant, portno, UHF_PORT_SUSPEND); + } + /* USB Host Mode */ + + /* wait for maximum device power up time */ + + usb2_pause_mtx(&Giant, + USB_MS_TO_TICKS(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 + */ + switch (udev->speed) { + case USB_SPEED_HIGH: + if (sc->sc_st.port_status & UPS_HIGH_SPEED) + speed = USB_SPEED_HIGH; + else if (sc->sc_st.port_status & UPS_LOW_SPEED) + speed = USB_SPEED_LOW; + else + speed = USB_SPEED_FULL; + break; + case USB_SPEED_FULL: + if (sc->sc_st.port_status & UPS_LOW_SPEED) + speed = USB_SPEED_LOW; + else + speed = USB_SPEED_FULL; + break; + case USB_SPEED_LOW: + speed = USB_SPEED_LOW; + break; + default: + /* same speed like parent */ + speed = udev->speed; + break; + } + /* + * Figure out the device mode + * + * NOTE: This part is currently FreeBSD specific. + */ + if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) + usb2_mode = USB_MODE_DEVICE; + else + usb2_mode = 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) { + DPRINTF("clearing suspend failed.\n"); + goto done; + } + /* get fresh status */ + + err = uhub_read_port_status(sc, portno); + if (err) { + DPRINTF("reading port status failed.\n"); + goto done; + } + /* get current state */ + + if (sc->sc_st.port_status & UPS_SUSPEND) { + is_suspend = 1; + } else { + is_suspend = 0; + } + + DPRINTF("suspended=%u\n", is_suspend); + + /* do the suspend or resume */ + + if (child) { + /* + * This code handle two cases: 1) Host Mode - we can only + * receive resume here 2) Device Mode - we can receive + * suspend and resume here + */ + if (is_suspend == 0) + usb2_dev_resume_peer(child); + else if (child->flags.usb2_mode == USB_MODE_DEVICE) + usb2_dev_suspend_peer(child); + } +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); + } + if (udev->pwr_save.suspended) { + /* need to wait until the child signals resume */ + DPRINTF("Device is suspended!\n"); + return (0); + } + 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_OVERCURRENT_INDICATOR) { + DPRINTF("Overcurrent on port %u.\n", portno); + err = usb2_req_clear_port_feature( + udev, &Giant, portno, UHF_C_PORT_OVER_CURRENT); + if (err) { + /* most likely the HUB is gone */ + break; + } + } + if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { + /* + * Fake a connect status change so that the + * status gets checked initially! + */ + sc->sc_st.port_change |= + UPS_C_CONNECT_STATUS; + } + 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; + } + + /* initial status checked */ + sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; + + /* return success */ + 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; + + 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, UHUB_N_TRANSFER, 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_MS_TO_TICKS(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, USB_MS_TO_TICKS(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 */ + + USB_XFER_LOCK(sc->sc_xfer[0]); + usb2_transfer_start(sc->sc_xfer[0]); + USB_XFER_UNLOCK(sc->sc_xfer[0]); + + /* Enable automatic power save on all USB HUBs */ + + usb2_set_power_mode(udev, USB_POWER_MODE_SAVE); + + return (0); + +error: + usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); + + 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, UHUB_N_TRANSFER); + + free(hub, M_USBDEV); + sc->sc_udev->hub = NULL; + return (0); +} + +static int +uhub_suspend(device_t dev) +{ + DPRINTF("\n"); + /* Sub-devices are not suspended here! */ + return (0); +} + +static int +uhub_resume(device_t dev) +{ + DPRINTF("\n"); + /* Sub-devices are not resumed here! */ + return (0); +} + +static void +uhub_driver_added(device_t dev, driver_t *driver) +{ + usb2_needs_explore_all(); +} + +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; +} + +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; + uint8_t speed; + + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + speed = usb2_get_speed(udev); + + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + if (speed == 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; + break; + default: + 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; + break; + } + 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; +} + +/*------------------------------------------------------------------------* + * 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++; + } +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK_ASSERT(bus, 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); +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_BUS_LOCK(bus); + if (do_probe) { + bus->do_probe = 1; + } + if (usb2_proc_msignal(&bus->explore_proc, + &bus->explore_msg[0], &bus->explore_msg[1])) { + /* ignore */ + } + USB_BUS_UNLOCK(bus); +} + +/*------------------------------------------------------------------------* + * 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--; + } +} + +/*------------------------------------------------------------------------* + * usb2_bus_power_update + * + * This function will ensure that all USB devices on the given bus are + * properly suspended or resumed according to the device transfer + * state. + *------------------------------------------------------------------------*/ +void +usb2_bus_power_update(struct usb2_bus *bus) +{ + usb2_needs_explore(bus, 0 /* no probe */ ); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_power_ref + * + * This function will modify the power save reference counts and + * wakeup the USB device associated with the given USB transfer, if + * needed. + *------------------------------------------------------------------------*/ +void +usb2_transfer_power_ref(struct usb2_xfer *xfer, int val) +{ + static const uint32_t power_mask[4] = { + [UE_CONTROL] = USB_HW_POWER_CONTROL, + [UE_BULK] = USB_HW_POWER_BULK, + [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, + [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, + }; + struct usb2_device *udev; + uint8_t needs_explore; + uint8_t needs_hw_power; + uint8_t xfer_type; + + udev = xfer->xroot->udev; + + if (udev->device_index == USB_ROOT_HUB_ADDR) { + /* no power save for root HUB */ + return; + } + USB_BUS_LOCK(udev->bus); + + xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE; + + udev->pwr_save.last_xfer_time = ticks; + udev->pwr_save.type_refs[xfer_type] += val; + + if (xfer->flags_int.control_xfr) { + udev->pwr_save.read_refs += val; + if (xfer->flags_int.usb2_mode == USB_MODE_HOST) { + /* + * it is not allowed to suspend during a control + * transfer + */ + udev->pwr_save.write_refs += val; + } + } else if (USB_GET_DATA_ISREAD(xfer)) { + udev->pwr_save.read_refs += val; + } else { + udev->pwr_save.write_refs += val; + } + + if (udev->pwr_save.suspended) + needs_explore = + (udev->pwr_save.write_refs != 0) || + ((udev->pwr_save.read_refs != 0) && + (usb2_peer_can_wakeup(udev) == 0)); + else + needs_explore = 0; + + if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { + DPRINTF("Adding type %u to power state\n", xfer_type); + udev->bus->hw_power_state |= power_mask[xfer_type]; + needs_hw_power = 1; + } else { + needs_hw_power = 0; + } + + USB_BUS_UNLOCK(udev->bus); + + if (needs_explore) { + DPRINTF("update\n"); + usb2_bus_power_update(udev->bus); + } else if (needs_hw_power) { + DPRINTF("needs power\n"); + if (udev->bus->methods->set_hw_power != NULL) { + (udev->bus->methods->set_hw_power) (udev->bus); + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_powerd + * + * This function implements the USB power daemon and is called + * regularly from the USB explore thread. + *------------------------------------------------------------------------*/ +void +usb2_bus_powerd(struct usb2_bus *bus) +{ + struct usb2_device *udev; + unsigned int temp; + unsigned int limit; + unsigned int mintime; + uint32_t type_refs[5]; + uint8_t x; + uint8_t rem_wakeup; + + limit = usb2_power_timeout; + if (limit == 0) + limit = hz; + else if (limit > 255) + limit = 255 * hz; + else + limit = limit * hz; + + DPRINTF("bus=%p\n", bus); + + USB_BUS_LOCK(bus); + + /* + * The root HUB device is never suspended + * and we simply skip it. + */ + for (x = USB_ROOT_HUB_ADDR + 1; + x != bus->devices_max; x++) { + + udev = bus->devices[x]; + if (udev == NULL) + continue; + + rem_wakeup = usb2_peer_can_wakeup(udev); + + temp = ticks - udev->pwr_save.last_xfer_time; + + if ((udev->power_mode == USB_POWER_MODE_ON) || + (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || + (udev->pwr_save.write_refs != 0) || + ((udev->pwr_save.read_refs != 0) && + (rem_wakeup == 0))) { + + /* check if we are suspended */ + if (udev->pwr_save.suspended != 0) { + USB_BUS_UNLOCK(bus); + usb2_dev_resume_peer(udev); + USB_BUS_LOCK(bus); + } + } else if (temp >= limit) { + + /* check if we are not suspended */ + if (udev->pwr_save.suspended == 0) { + USB_BUS_UNLOCK(bus); + usb2_dev_suspend_peer(udev); + USB_BUS_LOCK(bus); + } + } + } + + /* reset counters */ + + mintime = 0 - 1; + type_refs[0] = 0; + type_refs[1] = 0; + type_refs[2] = 0; + type_refs[3] = 0; + type_refs[4] = 0; + + /* Re-loop all the devices to get the actual state */ + + for (x = USB_ROOT_HUB_ADDR + 1; + x != bus->devices_max; x++) { + + udev = bus->devices[x]; + if (udev == NULL) + continue; + + /* we found a non-Root-Hub USB device */ + type_refs[4] += 1; + + /* "last_xfer_time" can be updated by a resume */ + temp = ticks - udev->pwr_save.last_xfer_time; + + /* + * Compute minimum time since last transfer for the complete + * bus: + */ + if (temp < mintime) + mintime = temp; + + if (udev->pwr_save.suspended == 0) { + type_refs[0] += udev->pwr_save.type_refs[0]; + type_refs[1] += udev->pwr_save.type_refs[1]; + type_refs[2] += udev->pwr_save.type_refs[2]; + type_refs[3] += udev->pwr_save.type_refs[3]; + } + } + + if (mintime >= (1 * hz)) { + /* recompute power masks */ + DPRINTF("Recomputing power masks\n"); + bus->hw_power_state = 0; + if (type_refs[UE_CONTROL] != 0) + bus->hw_power_state |= USB_HW_POWER_CONTROL; + if (type_refs[UE_BULK] != 0) + bus->hw_power_state |= USB_HW_POWER_BULK; + if (type_refs[UE_INTERRUPT] != 0) + bus->hw_power_state |= USB_HW_POWER_INTERRUPT; + if (type_refs[UE_ISOCHRONOUS] != 0) + bus->hw_power_state |= USB_HW_POWER_ISOC; + if (type_refs[4] != 0) + bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB; + } + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) { + /* always update hardware power! */ + (bus->methods->set_hw_power) (bus); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_dev_resume_peer + * + * This function will resume an USB peer and do the required USB + * signalling to get an USB device out of the suspended state. + *------------------------------------------------------------------------*/ +static void +usb2_dev_resume_peer(struct usb2_device *udev) +{ + struct usb2_bus *bus; + int err; + + /* be NULL safe */ + if (udev == NULL) + return; + + /* check if already resumed */ + if (udev->pwr_save.suspended == 0) + return; + + /* we need a parent HUB to do resume */ + if (udev->parent_hub == NULL) + return; + + DPRINTF("udev=%p\n", udev); + + if ((udev->flags.usb2_mode == USB_MODE_DEVICE) && + (udev->flags.remote_wakeup == 0)) { + /* + * If the host did not set the remote wakeup feature, we can + * not wake it up either! + */ + DPRINTF("remote wakeup is not set!\n"); + return; + } + /* get bus pointer */ + bus = udev->bus; + + /* resume parent hub first */ + usb2_dev_resume_peer(udev->parent_hub); + + /* resume current port (Valid in Host and Device Mode) */ + err = usb2_req_clear_port_feature(udev->parent_hub, + &Giant, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Resuming port failed!\n"); + return; + } + /* resume settle time */ + usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); + + if (bus->methods->device_resume != NULL) { + /* resume USB device on the USB controller */ + (bus->methods->device_resume) (udev); + } + USB_BUS_LOCK(bus); + /* set that this device is now resumed */ + udev->pwr_save.suspended = 0; + /* make sure that we don't go into suspend right away */ + udev->pwr_save.last_xfer_time = ticks; + + /* make sure the needed power masks are on */ + if (udev->pwr_save.type_refs[UE_CONTROL] != 0) + bus->hw_power_state |= USB_HW_POWER_CONTROL; + if (udev->pwr_save.type_refs[UE_BULK] != 0) + bus->hw_power_state |= USB_HW_POWER_BULK; + if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) + bus->hw_power_state |= USB_HW_POWER_INTERRUPT; + if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) + bus->hw_power_state |= USB_HW_POWER_ISOC; + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) { + /* always update hardware power! */ + (bus->methods->set_hw_power) (bus); + } + sx_xlock(udev->default_sx + 1); + /* notify all sub-devices about resume */ + err = usb2_suspend_resume(udev, 0); + sx_unlock(udev->default_sx + 1); + + /* check if peer has wakeup capability */ + if (usb2_peer_can_wakeup(udev)) { + /* clear remote wakeup */ + err = usb2_req_clear_device_feature(udev, + &Giant, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Clearing device " + "remote wakeup failed: %s!\n", + usb2_errstr(err)); + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_dev_suspend_peer + * + * This function will suspend an USB peer and do the required USB + * signalling to get an USB device into the suspended state. + *------------------------------------------------------------------------*/ +static void +usb2_dev_suspend_peer(struct usb2_device *udev) +{ + struct usb2_device *hub; + struct usb2_device *child; + uint32_t temp; + int err; + uint8_t x; + uint8_t nports; + uint8_t suspend_parent; + +repeat: + /* be NULL safe */ + if (udev == NULL) + return; + + /* check if already suspended */ + if (udev->pwr_save.suspended) + return; + + /* we need a parent HUB to do suspend */ + if (udev->parent_hub == NULL) + return; + + DPRINTF("udev=%p\n", udev); + + /* check if all devices on the parent hub are suspended */ + hub = udev->parent_hub; + if (hub != NULL) { + nports = hub->hub->nports; + suspend_parent = 1; + + for (x = 0; x != nports; x++) { + + child = usb2_bus_port_get_device(hub->bus, + hub->hub->ports + x); + + if (child == NULL) + continue; + + if (child->pwr_save.suspended) + continue; + + if (child == udev) + continue; + + /* another device on the HUB is not suspended */ + suspend_parent = 0; + + break; + } + } else { + suspend_parent = 0; + } + + sx_xlock(udev->default_sx + 1); + /* notify all sub-devices about suspend */ + err = usb2_suspend_resume(udev, 1); + sx_unlock(udev->default_sx + 1); + + if (usb2_peer_can_wakeup(udev)) { + /* allow device to do remote wakeup */ + err = usb2_req_set_device_feature(udev, + &Giant, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Setting device " + "remote wakeup failed!\n"); + } + } + USB_BUS_LOCK(udev->bus); + /* + * Set that this device is suspended. This variable must be set + * before calling USB controller suspend callbacks. + */ + udev->pwr_save.suspended = 1; + USB_BUS_UNLOCK(udev->bus); + + if (udev->bus->methods->device_suspend != NULL) { + + /* suspend device on the USB controller */ + (udev->bus->methods->device_suspend) (udev); + + /* do DMA delay */ + temp = usb2_get_dma_delay(udev->bus); + usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(temp)); + + } + /* suspend current port */ + err = usb2_req_set_port_feature(udev->parent_hub, + &Giant, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Suspending port failed\n"); + return; + } + if (suspend_parent) { + udev = udev->parent_hub; + goto repeat; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_power_mode + * + * This function will set the power mode, see USB_POWER_MODE_XXX for a + * USB device. + *------------------------------------------------------------------------*/ +void +usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode) +{ + /* filter input argument */ + if ((power_mode != USB_POWER_MODE_ON) && + (power_mode != USB_POWER_MODE_OFF)) { + power_mode = USB_POWER_MODE_SAVE; + } + udev->power_mode = power_mode; /* update copy of power mode */ + + usb2_bus_power_update(udev->bus); + + return; +} diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h new file mode 100644 index 0000000..87d85b3 --- /dev/null +++ b/sys/dev/usb/usb_hub.h @@ -0,0 +1,80 @@ +/* $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); +void usb2_bus_power_update(struct usb2_bus *bus); +void usb2_bus_powerd(struct usb2_bus *bus); + +#endif /* _USB2_HUB_H_ */ diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m new file mode 100644 index 0000000..f6d67d4 --- /dev/null +++ b/sys/dev/usb/usb_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 usb; + +# 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/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h new file mode 100644 index 0000000..4b91643 --- /dev/null +++ b/sys/dev/usb/usb_ioctl.h @@ -0,0 +1,292 @@ +/* $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 + +/* Building "kdump" depends on these includes */ + +#include +#include + +#define USB_DEVICE_NAME "usb" +#define USB_GENERIC_NAME "ugen" + +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_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 + * "usb_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 _IOWR('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_IFACE_DRIVER _IOWR('U', 121, struct usb2_gen_descriptor) +#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/usb/usb_lookup.c b/sys/dev/usb/usb_lookup.c new file mode 100644 index 0000000..a8fa271 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_lookup.h b/sys/dev/usb/usb_lookup.h new file mode 100644 index 0000000..e447292 --- /dev/null +++ b/sys/dev/usb/usb_lookup.h @@ -0,0 +1,122 @@ +/* $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/usb/usb_mbuf.c b/sys/dev/usb/usb_mbuf.c new file mode 100644 index 0000000..3ae6ee6 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_mbuf.h b/sys/dev/usb/usb_mbuf.h new file mode 100644 index 0000000..109340c --- /dev/null +++ b/sys/dev/usb/usb_mbuf.h @@ -0,0 +1,102 @@ +/* $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; + uint8_t last_packet:1; + uint8_t unused:7; +}; + +/* + * 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/usb/usb_mfunc.h b/sys/dev/usb/usb_mfunc.h new file mode 100644 index 0000000..0fad203 --- /dev/null +++ b/sys/dev/usb/usb_mfunc.h @@ -0,0 +1,78 @@ +/* $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_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/usb/usb_msctest.c b/sys/dev/usb/usb_msctest.c new file mode 100644 index 0000000..35b71ec --- /dev/null +++ b/sys/dev/usb/usb_msctest.c @@ -0,0 +1,578 @@ +/* $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 +#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,}, + .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); +} + +static void +bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) +{ + sc->state = xfer_index; + usb2_transfer_start(sc->xfer[xfer_index]); +} + +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; + } + } +} + +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; + sc->cbw.bCDBLength = sc->cmd_len; + 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; + } +} + +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; + } +} + +static void +bbb_data_rd_cs_callback(struct usb2_xfer *xfer) +{ + bbb_data_clear_stall_callback(xfer, ST_STATUS, + ST_DATA_RD); +} + +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); +} + +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; + } +} + +/*------------------------------------------------------------------------* + * 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, + uint8_t do_eject) +{ + 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 */ + sc->cbw.CBWCDB[1] = 0; + sc->cbw.CBWCDB[2] = 0; + sc->cbw.CBWCDB[3] = 0; + sc->cbw.CBWCDB[4] = 0x24; /* length */ + sc->cbw.CBWCDB[5] = 0; + err = bbb_command_start(sc, DIR_IN, 0, + sc->buffer, 0x24, 6, USB_MS_HZ); + + if ((sc->actlen != 0) && (err == 0)) { + sid_type = sc->buffer[0] & 0x1F; + if (sid_type == 0x05) { + /* CD-ROM */ + if (do_eject) { + /* 0: opcode: SCSI START/STOP */ + sc->cbw.CBWCDB[0] = 0x1b; + /* 1: byte2: Not immediate */ + sc->cbw.CBWCDB[1] = 0x00; + /* 2..3: reserved */ + sc->cbw.CBWCDB[2] = 0x00; + sc->cbw.CBWCDB[3] = 0x00; + /* 4: Load/Eject command */ + sc->cbw.CBWCDB[4] = 0x02; + /* 5: control */ + sc->cbw.CBWCDB[5] = 0x00; + err = bbb_command_start(sc, DIR_OUT, 0, + NULL, 0, 6, USB_MS_HZ); + + DPRINTFN(0, "Eject CD command " + "status: %s\n", usb2_errstr(err)); + } + err = 0; + goto done; + } + } else if ((err != 2) && --timeout) { + usb2_pause_mtx(&sc->mtx, 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); +} diff --git a/sys/dev/usb/usb_msctest.h b/sys/dev/usb/usb_msctest.h new file mode 100644 index 0000000..5bf64d0 --- /dev/null +++ b/sys/dev/usb/usb_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, uint8_t do_eject); + +#endif /* _USB2_MSCTEST_H_ */ diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c new file mode 100644 index 0000000..381f546 --- /dev/null +++ b/sys/dev/usb/usb_parse.c @@ -0,0 +1,225 @@ +/* $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) +{ + uint8_t *desc_next; + uint8_t *start; + uint8_t *end; + uint8_t *desc; + + /* be NULL safe */ + if (cd == NULL) + return (NULL); + + /* We assume that the "wTotalLength" has been checked. */ + start = (uint8_t *)cd; + end = start + UGETW(cd->wTotalLength); + desc = (uint8_t *)_desc; + + /* Get start of next USB descriptor. */ + if (desc == NULL) + desc = start; + else + desc = desc + desc[0]; + + /* Check that the next USB descriptor is within the range. */ + if ((desc < start) || (desc >= end)) + return (NULL); /* out of range, or EOD */ + + /* Check that the second next USB descriptor is within range. */ + desc_next = desc + desc[0]; + if ((desc_next < start) || (desc_next > end)) + return (NULL); /* out of range */ + + /* Check minimum descriptor length. */ + if (desc[0] < 3) + return (NULL); /* too short descriptor */ + + /* Return start of next descriptor. */ + return ((struct usb2_descriptor *)desc); +} + +/*------------------------------------------------------------------------* + * 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/usb/usb_parse.h b/sys/dev/usb/usb_parse.h new file mode 100644 index 0000000..a9e6509 --- /dev/null +++ b/sys/dev/usb/usb_parse.h @@ -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. + */ + +#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/usb/usb_pci.h b/sys/dev/usb/usb_pci.h new file mode 100644 index 0000000..9297c29 --- /dev/null +++ b/sys/dev/usb/usb_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/usb/usb_process.c b/sys/dev/usb/usb_process.c new file mode 100644 index 0000000..0354284 --- /dev/null +++ b/sys/dev/usb/usb_process.c @@ -0,0 +1,426 @@ +/* $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); +} + +/*------------------------------------------------------------------------* + * usb2_proc_create + * + * 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 + *------------------------------------------------------------------------*/ +int +usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx, + const char *pmesg, 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, pmesg)) { + DPRINTFN(0, "Unable to create USB process."); + up->up_ptr = NULL; + goto error; + } + return (0); + +error: + usb2_proc_free(up); + return (ENOMEM); +} + +/*------------------------------------------------------------------------* + * usb2_proc_free + * + * 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_free(struct usb2_process *up) +{ + /* check if not initialised */ + if (up->up_mtx == NULL) + 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; +} + +/*------------------------------------------------------------------------* + * 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; + + /* check if gone, return dummy value */ + if (up->up_gone) + return (_pm0); + + 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) +{ + if (up->up_gone) + return (1); + + mtx_assert(up->up_mtx, MA_OWNED); + return (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; + + /* check if gone */ + if (up->up_gone) + return; + + 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); + } +} + +/*------------------------------------------------------------------------* + * 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) +{ + /* check if not initialised */ + if (up->up_mtx == NULL) + return; + /* handle special case with Giant */ + 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); +} diff --git a/sys/dev/usb/usb_process.h b/sys/dev/usb/usb_process.h new file mode 100644 index 0000000..756b929 --- /dev/null +++ b/sys/dev/usb/usb_process.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_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_is_gone(struct usb2_process *up); +int usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx, + const char *pmesg, uint8_t prio); +void usb2_proc_drain(struct usb2_process *up); +void usb2_proc_mwait(struct usb2_process *up, void *pm0, void *pm1); +void usb2_proc_free(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/usb/usb_request.c b/sys/dev/usb/usb_request.c new file mode 100644 index 0000000..3d19442 --- /dev/null +++ b/sys/dev/usb/usb_request.c @@ -0,0 +1,1486 @@ +/* $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 +#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->xroot->udev->default_cv); + break; + } +} + +/*------------------------------------------------------------------------* + * 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_device *udev; + struct usb2_pipe *pipe; + struct usb2_pipe *pipe_end; + struct usb2_pipe *pipe_first; + uint8_t to = USB_EP_MAX; + + udev = xfer->xroot->udev; + + USB_BUS_LOCK(udev->bus); + + /* round robin pipe clear stall */ + + pipe = udev->pipe_curr; + pipe_end = udev->pipes + USB_EP_MAX; + pipe_first = 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; + USB_BUS_UNLOCK(udev->bus); + + usb2_start_hardware(xfer); + + USB_BUS_LOCK(udev->bus); + break; + } + pipe++; + if (--to) + goto tr_setup; + break; + + default: + if (xfer->error == USB_ERR_CANCELLED) { + break; + } + goto tr_setup; + } + + /* store current pipe */ + udev->pipe_curr = pipe; + USB_BUS_UNLOCK(udev->bus); +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_XFER_LOCK(xfer); + + 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) { + USB_XFER_UNLOCK(xfer); + err = usb2_copy_in_user(xfer->frbuffers + 1, + 0, data, temp); + USB_XFER_LOCK(xfer); + 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->xroot->xfer_mtx, + USB_MS_TO_TICKS(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(udev->default_cv, + xfer->xroot->xfer_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) { + USB_XFER_UNLOCK(xfer); + err = usb2_copy_out_user(xfer->frbuffers + 1, + 0, data, temp); + USB_XFER_LOCK(xfer); + 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); + } + USB_XFER_UNLOCK(xfer); + +done: + sx_xunlock(udev->default_sx); + + if (mtx) { + mtx_lock(mtx); + } + return ((usb2_error_t)err); +} + +/*------------------------------------------------------------------------* + * usb2_do_request_proc - factored out code + * + * This function is factored out code. It does basically the same like + * usb2_do_request_flags, except it will check the status of the + * passed process argument before doing the USB request. If the + * process is draining the USB_ERR_IOERROR code will be returned. It + * is assumed that the mutex associated with the process is locked + * when calling this function. + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc, + struct usb2_device_request *req, void *data, uint32_t flags, + uint16_t *actlen, uint32_t timeout) +{ + usb2_error_t err; + uint16_t len; + + /* get request data length */ + len = UGETW(req->wLength); + + /* check if the device is being detached */ + if (usb2_proc_is_gone(pproc)) { + err = USB_ERR_IOERROR; + goto done; + } + + /* forward the USB request */ + err = usb2_do_request_flags(udev, pproc->up_mtx, + req, data, flags, actlen, timeout); + +done: + /* on failure we zero the data */ + /* on short packet we zero the unused data */ + if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { + if (err) + memset(data, 0, len); + else if (actlen && *actlen != len) + memset(((uint8_t *)data) + *actlen, 0, len - *actlen); + } + return (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, USB_MS_TO_TICKS(pr_poll_delay)); + n += pr_poll_delay; +#else + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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, USB_MS_TO_TICKS(pr_recovery_delay)); +#else + /* wait for the device to recover from reset */ + usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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_flags(udev, mtx, &req, + desc, 0, NULL, 1000); + + if (err) { + if (!retries) { + goto done; + } + retries--; + + usb2_pause_mtx(mtx, hz / 5); + + 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); + } + if (string_index == 0) { + /* this is the language table */ + buf[0] = 0; + return (USB_ERR_INVAL); + } + if (udev->flags.no_strings) { + buf[0] = 0; + return (USB_ERR_STALLED); + } + err = usb2_req_get_string_desc + (udev, mtx, buf, len, udev->langid, string_index); + if (err) { + buf[0] = 0; + return (err); + } + temp = (uint8_t *)buf; + + if (temp[0] < 2) { + /* string length is too short */ + buf[0] = 0; + 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 { + /* silently skip bad character */ + continue; + } + + /* + * Filter by default - we don't allow greater and less than + * signs because they might confuse the dmesg printouts! + */ + if ((*s == '<') || (*s == '>') || (!isprint(*s))) { + /* silently skip bad character */ + continue; + } + s++; + } + *s = 0; /* zero terminate resulting string */ + 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 + * + * NOTE: After this function returns the hardware is in the + * unconfigured state! The application is responsible for setting a + * new configuration. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) +{ + struct usb2_device *parent_hub; + usb2_error_t err; + uint8_t old_addr; + uint8_t do_retry = 1; + + if (udev->flags.usb2_mode != USB_MODE_HOST) { + return (USB_ERR_INVAL); + } + old_addr = udev->address; + parent_hub = udev->parent_hub; + if (parent_hub == NULL) { + return (USB_ERR_INVAL); + } +retry: + 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; + + /* reset "bMaxPacketSize" */ + udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; + + /* + * 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! (ignored)\n", + old_addr); + } + /* restore device address */ + udev->address = old_addr; + + /* allow device time to set new address */ + usb2_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); + + /* get the device descriptor */ + err = usb2_req_get_desc(udev, mtx, &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; + } + /* get the full device descriptor */ + err = usb2_req_get_device_desc(udev, mtx, &udev->ddesc); + if (err) { + DPRINTFN(0, "addr=%d, getting device " + "descriptor failed!\n", old_addr); + goto done; + } +done: + if (err && do_retry) { + /* give the USB firmware some time to load */ + usb2_pause_mtx(mtx, hz / 2); + /* no more retries after this retry */ + do_retry = 0; + /* try again */ + goto retry; + } + /* restore address */ + udev->address = old_addr; + return (err); +} + +/*------------------------------------------------------------------------* + * usb2_req_clear_device_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_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_device_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_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)); +} diff --git a/sys/dev/usb/usb_request.h b/sys/dev/usb/usb_request.h new file mode 100644 index 0000000..8a360b7 --- /dev/null +++ b/sys/dev/usb/usb_request.h @@ -0,0 +1,103 @@ +/* $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_ + +struct usb2_process; + +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_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc, + 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); +usb2_error_t usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); +usb2_error_t usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); + +#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/usb/usb_revision.h b/sys/dev/usb/usb_revision.h new file mode 100644 index 0000000..06d1b21 --- /dev/null +++ b/sys/dev/usb/usb_revision.h @@ -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. + */ + +#ifndef _USB2_REVISION_H_ +#define _USB2_REVISION_H_ + +/* + * The "USB_SPEED" macro defines all the supported USB speeds. + */ +enum { + USB_SPEED_VARIABLE, + USB_SPEED_LOW, + USB_SPEED_FULL, + USB_SPEED_HIGH, + USB_SPEED_SUPER, + USB_SPEED_MAX +}; + +/* + * The "USB_REV" macro defines all the supported USB revisions. + */ +enum { + USB_REV_UNKNOWN, + USB_REV_PRE_1_0, + USB_REV_1_0, + USB_REV_1_1, + USB_REV_2_0, + USB_REV_2_5, + USB_REV_3_0, + USB_REV_MAX +}; + +/* + * The "USB_MODE" macro defines all the supported USB modes. + */ +enum { + USB_MODE_HOST, + USB_MODE_DEVICE, + USB_MODE_MAX +}; + +#endif /* _USB2_REVISION_H_ */ diff --git a/sys/dev/usb/usb_sw_transfer.c b/sys/dev/usb/usb_sw_transfer.c new file mode 100644 index 0000000..984c233 --- /dev/null +++ b/sys/dev/usb/usb_sw_transfer.c @@ -0,0 +1,170 @@ +/* $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 + +/*------------------------------------------------------------------------* + * 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; + } + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); +} diff --git a/sys/dev/usb/usb_sw_transfer.h b/sys/dev/usb/usb_sw_transfer.h new file mode 100644 index 0000000..d2da0eb --- /dev/null +++ b/sys/dev/usb/usb_sw_transfer.h @@ -0,0 +1,62 @@ +/* $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/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c new file mode 100644 index 0000000..df544a7 --- /dev/null +++ b/sys/dev/usb/usb_transfer.c @@ -0,0 +1,2826 @@ +/* $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}}, + [USB_SPEED_SUPER] = {.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}}, + [USB_SPEED_SUPER] = {.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}}, + [USB_SPEED_SUPER] = {.fixed = {1024, 1024, 1024, 1024}}, + }, + + [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}}, + [USB_SPEED_SUPER] = {.range = {0, 1024}}, + }, +}; + +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 *); +static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t); +static void usb2_control_transfer_init(struct usb2_xfer *); +static uint8_t usb2_start_hardware_sub(struct usb2_xfer *); +static void usb2_callback_proc(struct usb2_proc_msg *); +static void usb2_callback_ss_done_defer(struct usb2_xfer *); +static void usb2_callback_wrapper(struct usb2_xfer_queue *); +static void usb2_dma_delay_done_cb(void *); +static void usb2_transfer_start_cb(void *); +static uint8_t usb2_callback_wrapper_sub(struct usb2_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; + } +} + +/*------------------------------------------------------------------------* + * 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 + *------------------------------------------------------------------------*/ +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->xroot->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->xroot->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; + } + switch (parm->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; + break; + default: + frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; + break; + } + + 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; + + switch (parm->speed) { + case USB_SPEED_SUPER: + case USB_SPEED_VARIABLE: + /* 125us -> 1ms */ + if (xfer->interval < 4) + xfer->interval = 1; + else if (xfer->interval > 16) + xfer->interval = (1<<(16-4)); + else + xfer->interval = + (1 << (xfer->interval-4)); + break; + case USB_SPEED_HIGH: + /* 125us -> 1ms */ + xfer->interval /= 8; + break; + default: + break; + } + if (xfer->interval == 0) { + /* + * One millisecond is the smallest + * interval we support: + */ + 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->xroot->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; + } +} + +/*------------------------------------------------------------------------* + * 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 *xfer_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 (xfer_mtx == NULL) { + DPRINTFN(6, "using global lock\n"); + xfer_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->xfer_mtx = xfer_mtx; + + usb2_dma_tag_setup(&info->dma_parent_tag, + parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, + xfer_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max); + + info->bus = udev->bus; + info->udev = udev; + + 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].xroot = info; + info->done_m[1].hdr.pm_callback = &usb2_callback_proc; + info->done_m[1].xroot = info; + + if (xfer_mtx == &Giant) + info->done_p = + &udev->bus->giant_callback_proc; + else + info->done_p = + &udev->bus->non_giant_callback_proc; + } + /* 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->address = udev->address; + xfer->priv_sc = priv_sc; + xfer->xroot = info; + info->setup_refcount++; + + usb2_callout_init_mtx(&xfer->timeout_handle, + &udev->bus->bus_mtx, 0); + } 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 + * the bus mutex before returning ! + */ + USB_BUS_LOCK(info->bus); + + /* 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; + + USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); + + /* wait for any outstanding DMA operations */ + + if (needs_delay) { + temp = usb2_get_dma_delay(info->bus); + usb2_pause_mtx(&info->bus->bus_mtx, + USB_MS_TO_TICKS(temp)); + } + + /* make sure that our done messages are not queued anywhere */ + usb2_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); + + USB_BUS_UNLOCK(info->bus); + + /* 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); +} + +/*------------------------------------------------------------------------* + * 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) { + USB_XFER_LOCK(xfer); + USB_BUS_LOCK(xfer->xroot->bus); + + /* + * 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 "xfer_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; + + USB_BUS_UNLOCK(xfer->xroot->bus); + USB_XFER_UNLOCK(xfer); + + 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->xroot) { + info = xfer->xroot; + + USB_BUS_LOCK(info->bus); + + 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 { + USB_BUS_UNLOCK(info->bus); + } + } + } + } +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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) { + USB_BUS_LOCK(xfer->xroot->bus); + + usb2_dump_pipe(xfer->pipe); + + USB_BUS_UNLOCK(xfer->xroot->bus); + } +#endif + + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_NOTOWNED); + + /* Only open the USB transfer once! */ + if (!xfer->flags_int.open) { + xfer->flags_int.open = 1; + + DPRINTF("open\n"); + + USB_BUS_LOCK(xfer->xroot->bus); + (xfer->pipe->methods->open) (xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); + } + /* set "transferring" flag */ + xfer->flags_int.transferring = 1; + + /* increment power reference */ + usb2_transfer_power_ref(xfer, 1); + + /* + * Check if the transfer is waiting on a queue, most + * frequently the "done_q": + */ + if (xfer->wait_queue) { + USB_BUS_LOCK(xfer->xroot->bus); + usb2_transfer_dequeue(xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); + } + /* 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); + USB_BUS_LOCK(xfer->xroot->bus); + xfer->flags_int.can_cancel_immed = 1; + /* start the transfer */ + usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); + return; + } + USB_BUS_LOCK(xfer->xroot->bus); + usb2_transfer_done(xfer, USB_ERR_INVAL); + USB_BUS_UNLOCK(xfer->xroot->bus); + 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 */ + USB_BUS_LOCK(xfer->xroot->bus); + usb2_transfer_done(xfer, USB_ERR_INVAL); + USB_BUS_UNLOCK(xfer->xroot->bus); + 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)) { + USB_BUS_LOCK(xfer->xroot->bus); + usb2_transfer_done(xfer, USB_ERR_STALLED); + USB_BUS_UNLOCK(xfer->xroot->bus); + 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->xroot->dma_q, xfer); + return; + } + /* + * Enter the USB transfer into the Host Controller or + * Device Controller schedule: + */ + usb2_pipe_enter(xfer); +} + +/*------------------------------------------------------------------------* + * usb2_pipe_enter - factored out code + *------------------------------------------------------------------------*/ +void +usb2_pipe_enter(struct usb2_xfer *xfer) +{ + struct usb2_pipe *pipe; + + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + USB_BUS_LOCK(xfer->xroot->bus); + + 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); + USB_BUS_UNLOCK(xfer->xroot->bus); + return; + } + } else { + xfer->flags_int.can_cancel_immed = 0; + } + + /* start the transfer */ + usb2_command_wrapper(&pipe->pipe_q, xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_XFER_LOCK_ASSERT(xfer, 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; + } + USB_BUS_LOCK(xfer->xroot->bus); + /* call the USB transfer callback */ + usb2_callback_ss_done_defer(xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_XFER_LOCK_ASSERT(xfer, 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 */ + + USB_BUS_LOCK(xfer->xroot->bus); + 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); + } + } + + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * 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; + + if (xfer == NULL) { + /* transfer is gone */ + return (0); + } + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + if (xfer->flags_int.transferring) { + /* trivial case */ + return (1); + } + USB_BUS_LOCK(xfer->xroot->bus); + if (xfer->wait_queue) { + /* we are waiting on a queue somewhere */ + USB_BUS_UNLOCK(xfer->xroot->bus); + return (1); + } + info = xfer->xroot; + pq = &info->done_q; + + if (pq->curr == xfer) { + /* we are currently scheduled for callback */ + USB_BUS_UNLOCK(xfer->xroot->bus); + return (1); + } + /* we are not pending */ + USB_BUS_UNLOCK(xfer->xroot->bus); + 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->xroot->xfer_mtx != &Giant) { + USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); + } + USB_XFER_LOCK(xfer); + + 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->xroot->cv_drain, xfer->xroot->xfer_mtx); + } + USB_XFER_UNLOCK(xfer); +} + +/*------------------------------------------------------------------------* + * 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; +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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->xroot; + + /* Change locking order */ + USB_BUS_UNLOCK(info->bus); + + /* + * We exploit the fact that the mutex is the same for all + * callbacks that will be called from this thread: + */ + mtx_lock(info->xfer_mtx); + USB_BUS_LOCK(info->bus); + + /* Continue where we lost track */ + usb2_command_wrapper(&info->done_q, + info->done_q.curr); + + mtx_unlock(info->xfer_mtx); +} + +/*------------------------------------------------------------------------* + * 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->xroot; + struct usb2_xfer_queue *pq = &info->done_q; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + 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->xroot; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + if (!mtx_owned(xfer->xroot->xfer_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; + + USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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 */ + USB_BUS_LOCK(xfer->xroot->bus); + goto done; + } + } else { + + if (usb2_callback_wrapper_sub(xfer)) { + /* the callback has been deferred */ + USB_BUS_LOCK(xfer->xroot->bus); + goto done; + } + /* decrement power reference */ + usb2_transfer_power_ref(xfer, -1); + + 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 */ + USB_BUS_LOCK(xfer->xroot->bus); + + /* + * Check if we got started after that we got cancelled, but + * before we managed to do the callback. + */ + 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; + } + +done: + /* + * Check if we are draining. + */ + 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->xroot->cv_drain); + } + + /* do the next callback, if any */ + usb2_command_wrapper(&info->done_q, + info->done_q.curr); +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + DPRINTFN(3, "Completed %p\n", xfer); + + /* queue callback for execution, again */ + usb2_transfer_done(xfer, 0); +} + +/*------------------------------------------------------------------------* + * 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; + } +} + +/*------------------------------------------------------------------------* + * 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); + } +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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->xroot->xfer_mtx)) { + /* + * If the private USB lock is not locked, then we assume + * that the BUS-DMA load stage has been passed: + */ + pq = &xfer->xroot->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->xroot->bus->stats_err.uds_requests + [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; + } else { + xfer->xroot->bus->stats_ok.uds_requests + [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; + } + + /* call the USB transfer callback */ + usb2_callback_ss_done_defer(xfer); +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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; + } +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + /* avoid any races by locking the USB mutex */ + USB_BUS_LOCK(xfer->xroot->bus); + + xfer->flags.stall_pipe = 1; + + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * 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; + } + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + /* avoid any races by locking the USB mutex */ + USB_BUS_LOCK(xfer->xroot->bus); + + xfer->flags.stall_pipe = 0; + + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * 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; + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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; + + info = xfer->xroot; + udev = info->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]->xroot; + if (usb2_proc_msignal( + &info->bus->non_giant_callback_proc, + &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; + } +} + +/*------------------------------------------------------------------------* + * 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) +{ + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* defer delay */ + usb2_callout_reset(&xfer->timeout_handle, + USB_MS_TO_TICKS(ms), cb, xfer); +} + +/*------------------------------------------------------------------------* + * 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"); + USB_BUS_LOCK(xfer->xroot->bus); + (xfer->pipe->methods->close) (xfer); + USB_BUS_UNLOCK(xfer->xroot->bus); + /* 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->xroot->bus); + + DPRINTFN(3, "DMA delay, %u ms, " + "on %p\n", temp, xfer); + + if (temp != 0) { + USB_BUS_LOCK(xfer->xroot->bus); + usb2_transfer_timeout_ms(xfer, + &usb2_dma_delay_done_cb, temp); + USB_BUS_UNLOCK(xfer->xroot->bus); + 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: + */ + USB_BUS_LOCK(xfer->xroot->bus); + 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; + } + } + USB_BUS_UNLOCK(xfer->xroot->bus); +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; + } +} + +/*------------------------------------------------------------------------* + * 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) { + USB_XFER_LOCK(xfer); + 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); + } + } + USB_XFER_UNLOCK(xfer); + } 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; + } +} + +/*------------------------------------------------------------------------* + * 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); + + USB_BUS_LOCK(udev->bus); + pipe->toggle_next = 0; + USB_BUS_UNLOCK(udev->bus); +} + +/*------------------------------------------------------------------------* + * 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); + } + USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); + USB_XFER_LOCK_ASSERT(xfer2, 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->xroot->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); + } + } + mtx_unlock(mtx); +} + + +/*------------------------------------------------------------------------* + * 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 *xroot; + 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) { + xroot = xfer->xroot; + udev = xroot->udev; + + /* + * Poll hardware - signal that we are polling by + * locking the private mutex: + */ + USB_XFER_LOCK(xfer); + (udev->bus->methods->do_poll) (udev->bus); + USB_XFER_UNLOCK(xfer); + + /* poll clear stall start */ + USB_BUS_LOCK(xfer->xroot->bus); + pm = &udev->cs_msg[0].hdr; + (pm->pm_callback) (pm); + USB_BUS_UNLOCK(xfer->xroot->bus); + + if (udev->default_xfer[1]) { + + /* poll timeout */ + usb2_callout_poll(udev->default_xfer[1]); + + /* poll clear stall done thread */ + USB_BUS_LOCK(xfer->xroot->bus); + pm = &udev->default_xfer[1]-> + xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + USB_BUS_UNLOCK(xfer->xroot->bus); + } + /* poll timeout */ + usb2_callout_poll(xfer); + + /* poll done thread */ + USB_BUS_LOCK(xfer->xroot->bus); + pm = &xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + USB_BUS_UNLOCK(xfer->xroot->bus); + } + } +} + +#else + +void +usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) +{ + /* polling not supported */ +} + +#endif diff --git a/sys/dev/usb/usb_transfer.h b/sys/dev/usb/usb_transfer.h new file mode 100644 index 0000000..34124c5 --- /dev/null +++ b/sys/dev/usb/usb_transfer.h @@ -0,0 +1,129 @@ +/* $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 *xroot; +}; + +/* + * 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; /* pointer to callback process */ + void *memory_base; + struct mtx *xfer_mtx; /* cannot be changed during operation */ + 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; /* pointer to USB bus (cached) */ + struct usb2_device *udev; /* pointer to USB device */ + + 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_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); +uint32_t usb2_get_dma_delay(struct usb2_bus *bus); +void usb2_transfer_power_ref(struct usb2_xfer *xfer, int val); + +#endif /* _USB2_TRANSFER_H_ */ diff --git a/sys/dev/usb/usb_util.c b/sys/dev/usb/usb_util.c new file mode 100644 index 0000000..541cd55 --- /dev/null +++ b/sys/dev/usb/usb_util.c @@ -0,0 +1,346 @@ +/* $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 happen 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)); +} + +/*------------------------------------------------------------------------* + * usb2_pause_mtx - factored out code + * + * This function will delay the code by the passed number of system + * ticks. The passed mutex "mtx" will be dropped while waiting, if + * "mtx" is not NULL. + *------------------------------------------------------------------------*/ +void +usb2_pause_mtx(struct mtx *mtx, int _ticks) +{ + if (mtx != NULL) + mtx_unlock(mtx); + + if (cold) { + /* convert to milliseconds */ + _ticks = (_ticks * 1000) / hz; + /* convert to microseconds, rounded up */ + _ticks = (_ticks + 1) * 1000; + DELAY(_ticks); + + } else { + + /* + * Add one to the number of ticks so that we don't return + * too early! + */ + _ticks++; + + if (pause("USBWAIT", _ticks)) { + /* ignore */ + } + } + if (mtx != NULL) + mtx_lock(mtx); +} + +/*------------------------------------------------------------------------* + * 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 */ + } +} + +/*------------------------------------------------------------------------* + * 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 */ +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * usb2_cv_destroy - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_destroy(struct cv *cv) +{ + cv_destroy(cv); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * 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); +} + +/*------------------------------------------------------------------------* + * usb2_cv_broadcast - wrapper function + *------------------------------------------------------------------------*/ +void +usb2_cv_broadcast(struct cv *cv) +{ + wakeup(cv); +} + +/*------------------------------------------------------------------------* + * 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/usb/usb_util.h b/sys/dev/usb/usb_util.h new file mode 100644 index 0000000..e081c31 --- /dev/null +++ b/sys/dev/usb/usb_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, int _ticks); +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/usb/usbdevs b/sys/dev/usb/usbdevs new file mode 100644 index 0000000..0a3d85e --- /dev/null +++ b/sys/dev/usb/usbdevs @@ -0,0 +1,2527 @@ +$FreeBSD$ +/* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ + +/*- + * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * List of known USB vendors + * + * USB.org publishes a VID list of USB-IF member companies at + * http://www.usb.org/developers/tools + * Note that it does not show companies that have obtained a Vendor ID + * without becoming full members. + * + * Please note that these IDs do not do anything. Adding an ID here and + * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name + * available to the source code and does not change any functionality, nor + * does it make your device available to a specific driver. + * It will however make the descriptive string available if a device does not + * provide the string itself. + * + * After adding a vendor ID VNDR and a product ID PRDCT you will have the + * following extra defines: + * #define USB_VENDOR_VNDR 0x???? + * #define USB_PRODUCT_VNDR_PRDCT 0x???? + * + * You may have to add these defines to the respective probe routines to + * make the device recognised by the appropriate device driver. + */ + +vendor UNKNOWN1 0x0053 Unknown vendor +vendor UNKNOWN2 0x0105 Unknown vendor +vendor EGALAX2 0x0123 eGalax, Inc. +vendor HUMAX 0x02ad HUMAX +vendor LTS 0x0386 LTS +vendor BWCT 0x03da Bernd Walter Computer Technology +vendor AOX 0x03e8 AOX +vendor THESYS 0x03e9 Thesys +vendor DATABROADCAST 0x03ea Data Broadcasting +vendor ATMEL 0x03eb Atmel +vendor IWATSU 0x03ec Iwatsu America +vendor MITSUMI 0x03ee Mitsumi +vendor HP 0x03f0 Hewlett Packard +vendor GENOA 0x03f1 Genoa +vendor OAK 0x03f2 Oak +vendor ADAPTEC 0x03f3 Adaptec +vendor DIEBOLD 0x03f4 Diebold +vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical +vendor EPSONIMAGING 0x03f8 Epson Imaging +vendor KEYTRONIC 0x03f9 KeyTronic +vendor OPTI 0x03fb OPTi +vendor ELITEGROUP 0x03fc Elitegroup +vendor XILINX 0x03fd Xilinx +vendor FARALLON 0x03fe Farallon Communications +vendor NATIONAL 0x0400 National Semiconductor +vendor NATIONALREG 0x0401 National Registry +vendor ACERLABS 0x0402 Acer Labs +vendor FTDI 0x0403 Future Technology Devices +vendor NCR 0x0404 NCR +vendor SYNOPSYS2 0x0405 Synopsys +vendor FUJITSUICL 0x0406 Fujitsu-ICL +vendor FUJITSU2 0x0407 Fujitsu Personal Systems +vendor QUANTA 0x0408 Quanta +vendor NEC 0x0409 NEC +vendor KODAK 0x040a Eastman Kodak +vendor WELTREND 0x040b Weltrend +vendor VIA 0x040d VIA +vendor MCCI 0x040e MCCI +vendor MELCO 0x0411 Melco +vendor LEADTEK 0x0413 Leadtek +vendor WINBOND 0x0416 Winbond +vendor PHOENIX 0x041a Phoenix +vendor CREATIVE 0x041e Creative Labs +vendor NOKIA 0x0421 Nokia +vendor ADI 0x0422 ADI Systems +vendor CATC 0x0423 Computer Access Technology +vendor SMC2 0x0424 Standard Microsystems +vendor MOTOROLA_HK 0x0425 Motorola HK +vendor GRAVIS 0x0428 Advanced Gravis Computer +vendor CIRRUSLOGIC 0x0429 Cirrus Logic +vendor INNOVATIVE 0x042c Innovative Semiconductors +vendor MOLEX 0x042f Molex +vendor SUN 0x0430 Sun Microsystems +vendor UNISYS 0x0432 Unisys +vendor TAUGA 0x0436 Taugagreining HF +vendor AMD 0x0438 Advanced Micro Devices +vendor LEXMARK 0x043d Lexmark International +vendor LG 0x043e LG Electronics +vendor NANAO 0x0440 NANAO +vendor GATEWAY 0x0443 Gateway 2000 +vendor NMB 0x0446 NMB +vendor ALPS 0x044e Alps Electric +vendor THRUST 0x044f Thrustmaster +vendor TI 0x0451 Texas Instruments +vendor ANALOGDEVICES 0x0456 Analog Devices +vendor SIS 0x0457 Silicon Integrated Systems Corp. +vendor KYE 0x0458 KYE Systems +vendor DIAMOND2 0x045a Diamond (Supra) +vendor RENESAS 0x045b Renesas +vendor MICROSOFT 0x045e Microsoft +vendor PRIMAX 0x0461 Primax Electronics +vendor MGE 0x0463 MGE UPS Systems +vendor AMP 0x0464 AMP +vendor CHERRY 0x046a Cherry Mikroschalter +vendor MEGATRENDS 0x046b American Megatrends +vendor LOGITECH 0x046d Logitech +vendor BTC 0x046e Behavior Tech. Computer +vendor PHILIPS 0x0471 Philips +vendor SUN2 0x0472 Sun Microsystems (offical) +vendor SANYO 0x0474 Sanyo Electric +vendor SEAGATE 0x0477 Seagate +vendor CONNECTIX 0x0478 Connectix +vendor SEMTECH 0x047a Semtech +vendor KENSINGTON 0x047d Kensington +vendor LUCENT 0x047e Lucent +vendor PLANTRONICS 0x047f Plantronics +vendor KYOCERA 0x0482 Kyocera Wireless Corp. +vendor STMICRO 0x0483 STMicroelectronics +vendor FOXCONN 0x0489 Foxconn +vendor MEIZU 0x0492 Meizu Electronics +vendor YAMAHA 0x0499 YAMAHA +vendor COMPAQ 0x049f Compaq +vendor HITACHI 0x04a4 Hitachi +vendor ACERP 0x04a5 Acer Peripherals +vendor DAVICOM 0x04a6 Davicom +vendor VISIONEER 0x04a7 Visioneer +vendor CANON 0x04a9 Canon +vendor NIKON 0x04b0 Nikon +vendor PAN 0x04b1 Pan International +vendor IBM 0x04b3 IBM +vendor CYPRESS 0x04b4 Cypress Semiconductor +vendor ROHM 0x04b5 ROHM +vendor COMPAL 0x04b7 Compal +vendor EPSON 0x04b8 Seiko Epson +vendor RAINBOW 0x04b9 Rainbow Technologies +vendor IODATA 0x04bb I-O Data +vendor TDK 0x04bf TDK +vendor 3COMUSR 0x04c1 U.S. Robotics +vendor METHODE 0x04c2 Methode Electronics Far East +vendor MAXISWITCH 0x04c3 Maxi Switch +vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research +vendor FUJITSU 0x04c5 Fujitsu +vendor TOSHIBAAM 0x04c6 Toshiba America +vendor MICROMACRO 0x04c7 Micro Macro Technologies +vendor KONICA 0x04c8 Konica +vendor LITEON 0x04ca Lite-On Technology +vendor FUJIPHOTO 0x04cb Fuji Photo Film +vendor PHILIPSSEMI 0x04cc Philips Semiconductors +vendor TATUNG 0x04cd Tatung Co. Of America +vendor SCANLOGIC 0x04ce ScanLogic +vendor MYSON 0x04cf Myson Technology +vendor DIGI2 0x04d0 Digi +vendor ITTCANON 0x04d1 ITT Canon +vendor ALTEC 0x04d2 Altec Lansing +vendor LSI 0x04d4 LSI +vendor MENTORGRAPHICS 0x04d6 Mentor Graphics +vendor ITUNERNET 0x04d8 I-Tuner Networks +vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc. +vendor PANASONIC 0x04da Panasonic (Matsushita) +vendor HUANHSIN 0x04dc Huan Hsin +vendor SHARP 0x04dd Sharp +vendor IIYAMA 0x04e1 Iiyama +vendor SHUTTLE 0x04e6 Shuttle Technology +vendor ELO 0x04e7 Elo TouchSystems +vendor SAMSUNG 0x04e8 Samsung Electronics +vendor NORTHSTAR 0x04eb Northstar +vendor TOKYOELECTRON 0x04ec Tokyo Electron +vendor ANNABOOKS 0x04ed Annabooks +vendor JVC 0x04f1 JVC +vendor CHICONY 0x04f2 Chicony Electronics +vendor ELAN 0x04f3 Elan +vendor NEWNEX 0x04f7 Newnex +vendor BROTHER 0x04f9 Brother Industries +vendor DALLAS 0x04fa Dallas Semiconductor +vendor AIPTEK2 0x04fc AIPTEK International +vendor PFU 0x04fe PFU +vendor FUJIKURA 0x0501 Fujikura/DDK +vendor ACER 0x0502 Acer +vendor 3COM 0x0506 3Com +vendor HOSIDEN 0x0507 Hosiden Corporation +vendor AZTECH 0x0509 Aztech Systems +vendor BELKIN 0x050d Belkin Components +vendor KAWATSU 0x050f Kawatsu Semiconductor +vendor FCI 0x0514 FCI +vendor LONGWELL 0x0516 Longwell +vendor COMPOSITE 0x0518 Composite +vendor STAR 0x0519 Star Micronics +vendor APC 0x051d American Power Conversion +vendor SCIATLANTA 0x051e Scientific Atlanta +vendor TSM 0x0520 TSM +vendor CONNECTEK 0x0522 Advanced Connectek USA +vendor NETCHIP 0x0525 NetChip Technology +vendor ALTRA 0x0527 ALTRA +vendor ATI 0x0528 ATI Technologies +vendor AKS 0x0529 Aladdin Knowledge Systems +vendor TEKOM 0x052b Tekom +vendor CANONDEV 0x052c Canon +vendor WACOMTECH 0x0531 Wacom +vendor INVENTEC 0x0537 Inventec +vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals +vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG +vendor SYNOPSYS 0x053f Synopsys +vendor UNIACCESS 0x0540 Universal Access +vendor VIEWSONIC 0x0543 ViewSonic +vendor XIRLINK 0x0545 Xirlink +vendor ANCHOR 0x0547 Anchor Chips +vendor SONY 0x054c Sony +vendor FUJIXEROX 0x0550 Fuji Xerox +vendor VISION 0x0553 VLSI Vision +vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems +vendor ATEN 0x0557 ATEN International +vendor SAMSUNG2 0x055d Samsung Electronics +vendor MUSTEK 0x055f Mustek Systems +vendor TELEX 0x0562 Telex Communications +vendor CHINON 0x0564 Chinon +vendor PERACOM 0x0565 Peracom Networks +vendor ALCOR2 0x0566 Alcor Micro +vendor XYRATEX 0x0567 Xyratex +vendor WACOM 0x056a WACOM +vendor ETEK 0x056c e-TEK Labs +vendor EIZO 0x056d EIZO +vendor ELECOM 0x056e Elecom +vendor CONEXANT 0x0572 Conexant +vendor HAUPPAUGE 0x0573 Hauppauge Computer Works +vendor BAFO 0x0576 BAFO/Quality Computer Accessories +vendor YEDATA 0x057b Y-E Data +vendor AVM 0x057c AVM +vendor QUICKSHOT 0x057f Quickshot +vendor ROLAND 0x0582 Roland +vendor ROCKFIRE 0x0583 Rockfire +vendor RATOC 0x0584 RATOC Systems +vendor ZYXEL 0x0586 ZyXEL Communication +vendor INFINEON 0x058b Infineon +vendor MICREL 0x058d Micrel +vendor ALCOR 0x058f Alcor Micro +vendor OMRON 0x0590 OMRON +vendor ZORAN 0x0595 Zoran Microelectronics +vendor NIIGATA 0x0598 Niigata +vendor IOMEGA 0x059b Iomega +vendor ATREND 0x059c A-Trend Technology +vendor AID 0x059d Advanced Input Devices +vendor LACIE 0x059f LaCie +vendor FUJIFILM 0x05a2 Fuji Film +vendor ARC 0x05a3 ARC +vendor ORTEK 0x05a4 Ortek +vendor BOSE 0x05a7 Bose +vendor OMNIVISION 0x05a9 OmniVision +vendor INSYSTEM 0x05ab In-System Design +vendor APPLE 0x05ac Apple Computer +vendor YCCABLE 0x05ad Y.C. Cable +vendor DIGITALPERSONA 0x05ba DigitalPersona +vendor 3G 0x05bc 3G Green Green Globe +vendor RAFI 0x05bd RAFI +vendor TYCO 0x05be Tyco +vendor KAWASAKI 0x05c1 Kawasaki +vendor DIGI 0x05c5 Digi International +vendor QUALCOMM2 0x05c6 Qualcomm +vendor QTRONIX 0x05c7 Qtronix +vendor FOXLINK 0x05c8 Foxlink +vendor RICOH 0x05ca Ricoh +vendor ELSA 0x05cc ELSA +vendor SCIWORX 0x05ce sci-worx +vendor BRAINBOXES 0x05d1 Brainboxes Limited +vendor ULTIMA 0x05d8 Ultima +vendor AXIOHM 0x05d9 Axiohm Transaction Solutions +vendor MICROTEK 0x05da Microtek +vendor SUNTAC 0x05db SUN Corporation +vendor LEXAR 0x05dc Lexar Media +vendor ADDTRON 0x05dd Addtron +vendor SYMBOL 0x05e0 Symbol Technologies +vendor SYNTEK 0x05e1 Syntek +vendor GENESYS 0x05e3 Genesys Logic +vendor FUJI 0x05e5 Fuji Electric +vendor KEITHLEY 0x05e6 Keithley Instruments +vendor EIZONANAO 0x05e7 EIZO Nanao +vendor KLSI 0x05e9 Kawasaki LSI +vendor FFC 0x05eb FFC +vendor ANKO 0x05ef Anko Electronic +vendor PIENGINEERING 0x05f3 P.I. Engineering +vendor AOC 0x05f6 AOC International +vendor CHIC 0x05fe Chic Technology +vendor BARCO 0x0600 Barco Display Systems +vendor BRIDGE 0x0607 Bridge Information +vendor SOLIDYEAR 0x060b Solid Year +vendor BIORAD 0x0614 Bio-Rad Laboratories +vendor MACALLY 0x0618 Macally +vendor ACTLABS 0x061c Act Labs +vendor ALARIS 0x0620 Alaris +vendor APEX 0x0624 Apex +vendor CREATIVE3 0x062a Creative Labs +vendor VIVITAR 0x0636 Vivitar +vendor GUNZE 0x0637 Gunze Electronics USA +vendor AVISION 0x0638 Avision +vendor TEAC 0x0644 TEAC +vendor SGI 0x065e Silicon Graphics +vendor SANWASUPPLY 0x0663 Sanwa Supply +vendor LINKSYS 0x066b Linksys +vendor ACERSA 0x066e Acer Semiconductor America +vendor SIGMATEL 0x066f Sigmatel +vendor DRAYTEK 0x0675 DrayTek +vendor AIWA 0x0677 Aiwa +vendor ACARD 0x0678 ACARD Technology +vendor PROLIFIC 0x067b Prolific Technology +vendor SIEMENS 0x067c Siemens +vendor AVANCELOGIC 0x0680 Avance Logic +vendor SIEMENS2 0x0681 Siemens +vendor MINOLTA 0x0686 Minolta +vendor CHPRODUCTS 0x068e CH Products +vendor HAGIWARA 0x0693 Hagiwara Sys-Com +vendor CTX 0x0698 Chuntex +vendor ASKEY 0x069a Askey Computer +vendor SAITEK 0x06a3 Saitek +vendor ALCATELT 0x06b9 Alcatel Telecom +vendor AGFA 0x06bd AGFA-Gevaert +vendor ASIAMD 0x06be Asia Microelectronic Development +vendor BIZLINK 0x06c4 Bizlink International +vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. +vendor AASHIMA 0x06d6 Aashima Technology +vendor MULTITECH 0x06e0 MultiTech +vendor ADS 0x06e1 ADS Technologies +vendor ALCATELM 0x06e4 Alcatel Microelectronics +vendor SIRIUS 0x06ea Sirius Technologies +vendor GUILLEMOT 0x06f8 Guillemot +vendor BOSTON 0x06fd Boston Acoustics +vendor SMC 0x0707 Standard Microsystems +vendor PUTERCOM 0x0708 Putercom +vendor MCT 0x0711 MCT +vendor IMATION 0x0718 Imation +vendor SONYERICSSON 0x0731 Sony Ericsson +vendor EICON 0x0734 Eicon Networks +vendor SYNTECH 0x0745 Syntech Information +vendor DIGITALSTREAM 0x074e Digital Stream +vendor AUREAL 0x0755 Aureal Semiconductor +vendor MIDIMAN 0x0763 Midiman +vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc. +vendor SURECOM 0x0769 Surecom Technology +vendor LINKSYS2 0x077b Linksys +vendor GRIFFIN 0x077d Griffin Technology +vendor SANDISK 0x0781 SanDisk +vendor JENOPTIK 0x0784 Jenoptik +vendor LOGITEC 0x0789 Logitec +vendor BRIMAX 0x078e Brimax +vendor AXIS 0x0792 Axis Communications +vendor ABL 0x0794 ABL Electronics +vendor SAGEM 0x079b Sagem +vendor SUNCOMM 0x079c Sun Communications, Inc. +vendor ALFADATA 0x079d Alfadata Computer +vendor NATIONALTECH 0x07a2 National Technical Systems +vendor ONNTO 0x07a3 Onnto +vendor BE 0x07a4 Be +vendor ADMTEK 0x07a6 ADMtek +vendor COREGA 0x07aa Corega +vendor FREECOM 0x07ab Freecom +vendor MICROTECH 0x07af Microtech +vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) +vendor OLYMPUS 0x07b4 Olympus +vendor ABOCOM 0x07b8 AboCom Systems +vendor KEISOKUGIKEN 0x07c1 Keisokugiken +vendor ONSPEC 0x07c4 OnSpec +vendor APG 0x07c5 APG Cash Drawer +vendor BUG 0x07c8 B.U.G. +vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International +vendor AVERMEDIA 0x07ca AVerMedia Technologies +vendor SIIG 0x07cc SIIG +vendor CASIO 0x07cf CASIO +vendor DLINK2 0x07d1 D-Link +vendor APTIO 0x07d2 Aptio Products +vendor ARASAN 0x07da Arasan Chip Systems +vendor ALLIEDCABLE 0x07e6 Allied Cable +vendor STSN 0x07ef STSN +vendor CENTURY 0x07f7 Century Corp +vendor ZOOM 0x0803 Zoom Telephonics +vendor PCS 0x0810 Personal Communication Systems +vendor BROADLOGIC 0x0827 BroadLogic +vendor HANDSPRING 0x082d Handspring +vendor PALM 0x0830 Palm Computing +vendor SOURCENEXT 0x0833 SOURCENEXT +vendor ACTIONSTAR 0x0835 Action Star Enterprise +vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin +vendor ACCTON 0x083a Accton Technology +vendor DIAMOND 0x0841 Diamond +vendor NETGEAR 0x0846 BayNETGEAR +vendor TOPRE 0x0853 Topre Corporation +vendor ACTIVEWIRE 0x0854 ActiveWire +vendor BBELECTRONICS 0x0856 B&B Electronics +vendor PORTGEAR 0x085a PortGear +vendor NETGEAR2 0x0864 Netgear +vendor SYSTEMTALKS 0x086e System Talks +vendor METRICOM 0x0870 Metricom +vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America +vendor JATON 0x087d Jaton +vendor APT 0x0880 APT Technologies +vendor BOCARESEARCH 0x0885 Boca Research +vendor ANDREA 0x08a8 Andrea Electronics +vendor BURRBROWN 0x08bb Burr-Brown Japan +vendor 2WIRE 0x08c8 2Wire +vendor AIPTEK 0x08ca AIPTEK International +vendor SMARTBRIDGES 0x08d1 SmartBridges +vendor BILLIONTON 0x08dd Billionton Systems +vendor EXTENDED 0x08e9 Extended Systems +vendor MSYSTEMS 0x08ec M-Systems +vendor AUTHENTEC 0x08ff AuthenTec +vendor AUDIOTECHNICA 0x0909 Audio-Technica +vendor TRUMPION 0x090a Trumpion Microelectronics +vendor FEIYA 0x090c Feiya +vendor ALATION 0x0910 Alation Systems +vendor GLOBESPAN 0x0915 Globespan +vendor CONCORDCAMERA 0x0919 Concord Camera +vendor GARMIN 0x091e Garmin International +vendor GOHUBS 0x0921 GoHubs +vendor XEROX 0x0924 Xerox +vendor BIOMETRIC 0x0929 American Biometric Company +vendor TOSHIBA 0x0930 Toshiba +vendor PLEXTOR 0x093b Plextor +vendor INTREPIDCS 0x093c Intrepid +vendor YANO 0x094f Yano +vendor KINGSTON 0x0951 Kingston Technology +vendor BLUEWATER 0x0956 BlueWater Systems +vendor AGILENT 0x0957 Agilent Technologies +vendor GUDE 0x0959 Gude ADS +vendor PORTSMITH 0x095a Portsmith +vendor ACERW 0x0967 Acer +vendor ADIRONDACK 0x0976 Adirondack Wire & Cable +vendor BECKHOFF 0x0978 Beckhoff +vendor MINDSATWORK 0x097a Minds At Work +vendor POINTCHIPS 0x09a6 PointChips +vendor INTERSIL 0x09aa Intersil +vendor ALTIUS 0x09b3 Altius Solutions +vendor ARRIS 0x09c1 Arris Interactive +vendor ACTIVCARD 0x09c3 ACTIVCARD +vendor ACTISYS 0x09c4 ACTiSYS +vendor NOVATEL2 0x09d7 Novatel Wireless +vendor AFOURTECH 0x09da A-FOUR TECH +vendor AIMEX 0x09dc AIMEX +vendor ADDONICS 0x09df Addonics Technologies +vendor AKAI 0x09e8 AKAI professional M.I. +vendor ARESCOM 0x09f5 ARESCOM +vendor BAY 0x09f9 Bay Associates +vendor ALTERA 0x09fb Altera +vendor CSR 0x0a12 Cambridge Silicon Radio +vendor TREK 0x0a16 Trek Technology +vendor ASAHIOPTICAL 0x0a17 Asahi Optical +vendor BOCASYSTEMS 0x0a43 Boca Systems +vendor SHANTOU 0x0a46 ShanTou +vendor MEDIAGEAR 0x0a48 MediaGear +vendor BROADCOM 0x0a5c Broadcom +vendor GREENHOUSE 0x0a6b GREENHOUSE +vendor GEOCAST 0x0a79 Geocast Network Systems +vendor IDQUANTIQUE 0x0aba id Quantique +vendor ZYDAS 0x0ace Zydas Technology Corporation +vendor NEODIO 0x0aec Neodio +vendor OPTION 0x0af0 Option N.V: +vendor ASUS 0x0b05 ASUSTeK Computer +vendor TODOS 0x0b0c Todos Data System +vendor SIIG2 0x0b39 SIIG +vendor TEKRAM 0x0b3b Tekram Technology +vendor HAL 0x0b41 HAL Corporation +vendor EMS 0x0b43 EMS Production +vendor NEC2 0x0b62 NEC +vendor ATI2 0x0b6f ATI +vendor ZEEVO 0x0b7a Zeevo, Inc. +vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc. +vendor ASIX 0x0b95 ASIX Electronics +vendor O2MICRO 0x0b97 O2 Micro, Inc. +vendor USR 0x0baf U.S. Robotics +vendor AMBIT 0x0bb2 Ambit Microsystems +vendor HTC 0x0bb4 HTC +vendor REALTEK 0x0bda Realtek +vendor ADDONICS2 0x0bf6 Addonics Technology +vendor FSC 0x0bf8 Fujitsu Siemens Computers +vendor AGATE 0x0c08 Agate Technologies +vendor DMI 0x0c0b DMI +vendor CHICONY2 0x0c45 Chicony +vendor SEALEVEL 0x0c52 Sealevel System +vendor LUWEN 0x0c76 Luwen +vendor KYOCERA2 0x0c88 Kyocera Wireless Corp. +vendor ZCOM 0x0cde Z-Com +vendor ATHEROS2 0x0cf3 Atheros Communications +vendor TANGTOP 0x0d3d Tangtop +vendor SMC3 0x0d5c Standard Microsystems +vendor ADDON 0x0d7d Add-on Technology +vendor ACDC 0x0d7e American Computer & Digital Components +vendor ABC 0x0d8c ABC +vendor CONCEPTRONIC 0x0d8e Conceptronic +vendor SKANHEX 0x0d96 Skanhex Technology, Inc. +vendor MSI 0x0db0 Micro Star International +vendor ELCON 0x0db7 ELCON Systemtechnik +vendor NETAC 0x0dd8 Netac +vendor SITECOMEU 0x0df6 Sitecom Europe +vendor MOBILEACTION 0x0df7 Mobile Action +vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia +vendor HAWKING 0x0e66 Hawking +vendor FOSSIL 0x0e67 Fossil, Inc +vendor GMATE 0x0e7e G.Mate, Inc +vendor OTI 0x0ea0 Ours Technology +vendor YISO 0x0eab Yiso Wireless Co. +vendor PILOTECH 0x0eaf Pilotech +vendor NOVATECH 0x0eb0 NovaTech +vendor ITEGNO 0x0eba iTegno +vendor WINMAXGROUP 0x0ed1 WinMaxGroup +vendor TOD 0x0ede TOD +vendor EGALAX 0x0eef eGalax, Inc. +vendor AIRPRIME 0x0f3d AirPrime, Inc. +vendor MICROTUNE 0x0f4d Microtune +vendor VTECH 0x0f88 VTech +vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH +vendor RIM 0x0fca Research In Motion +vendor DYNASTREAM 0x0fcf Dynastream Innovations +vendor QUALCOMM 0x1004 Qualcomm +vendor DESKNOTE 0x1019 Desknote +vendor GIGABYTE 0x1044 GIGABYTE +vendor WESTERN 0x1058 Western Digital +vendor MOTOROLA 0x1063 Motorola +vendor CCYU 0x1065 CCYU Technology +vendor CURITEL 0x106c Curitel Communications Inc +vendor SILABS2 0x10a6 SILABS2 +vendor USI 0x10ab USI +vendor PLX 0x10b5 PLX +vendor ASANTE 0x10bd Asante +vendor SILABS 0x10c4 Silicon Labs +vendor ANALOG 0x1110 Analog Devices +vendor TENX 0x1130 Ten X Technology, Inc. +vendor ISSC 0x1131 Integrated System Solution Corp. +vendor JRC 0x1145 Japan Radio Company +vendor SPHAIRON 0x114b Sphairon Access Systems GmbH +vendor DELORME 0x1163 DeLorme +vendor SERVERWORKS 0x1166 ServerWorks +vendor ACERCM 0x1189 Acer Communications & Multimedia +vendor SIERRA 0x1199 Sierra Wireless +vendor TOPFIELD 0x11db Topfield Co., Ltd +vendor SIEMENS3 0x11f5 Siemens +vendor PROLIFIC2 0x11f6 Prolific +vendor ALCATEL 0x11f7 Alcatel +vendor UNKNOWN3 0x1233 Unknown vendor +vendor TSUNAMI 0x1241 Tsunami +vendor PHEENET 0x124a Pheenet +vendor TARGUS 0x1267 Targus +vendor TWINMOS 0x126f TwinMOS +vendor TENDA 0x1286 Tenda +vendor CREATIVE2 0x1292 Creative Labs +vendor BELKIN2 0x1293 Belkin Components +vendor CYBERTAN 0x129b CyberTAN Technology +vendor HUAWEI 0x12d1 Huawei Technologies +vendor ARANEUS 0x12d8 Araneus Information Systems +vendor TAPWAVE 0x12ef Tapwave +vendor AINCOMM 0x12fd Aincomm +vendor MOBILITY 0x1342 Mobility +vendor DICKSMITH 0x1371 Dick Smith Electronics +vendor NETGEAR3 0x1385 Netgear +vendor BALTECH 0x13ad Baltech +vendor CISCOLINKSYS 0x13b1 Cisco-Linksys +vendor SHARK 0x13d2 Shark +vendor NOVATEL 0x1410 Novatel Wireless +vendor MERLIN 0x1416 Merlin +vendor WISTRONNEWEB 0x1435 Wistron NeWeb +vendor RADIOSHACK 0x1453 Radio Shack +vendor HUAWEI3COM 0x1472 Huawei-3Com +vendor SILICOM 0x1485 Silicom +vendor RALINK 0x148f Ralink Technology +vendor IMAGINATION 0x149a Imagination Technologies +vendor CONCEPTRONIC2 0x14b2 Conceptronic +vendor PLANEX3 0x14ea Planex Communications +vendor SILICONPORTALS 0x1527 Silicon Portals +vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd. +vendor UBLOX 0x1546 U-blox +vendor PNY 0x154b PNY +vendor OQO 0x1557 OQO +vendor UMEDIA 0x157e U-MEDIA Communications +vendor FIBERLINE 0x1582 Fiberline +vendor SPARKLAN 0x15a9 SparkLAN +vendor SOHOWARE 0x15e8 SOHOware +vendor UMAX 0x1606 UMAX Data Systems +vendor INSIDEOUT 0x1608 Inside Out Networks +vendor GOODWAY 0x1631 Good Way Technology +vendor ENTREGA 0x1645 Entrega +vendor ACTIONTEC 0x1668 Actiontec Electronics +vendor ATHEROS 0x168c Atheros Communications +vendor GIGASET 0x1690 Gigaset +vendor GLOBALSUN 0x16ab Global Sun Technology +vendor ANYDATA 0x16d5 AnyDATA Corporation +vendor JABLOTRON 0x16d6 Jablotron +vendor CMOTECH 0x16d8 C-motech +vendor AXESSTEL 0x1726 Axesstel Co., Ltd. +vendor LINKSYS4 0x1737 Linksys +vendor SENAO 0x1740 Senao +vendor METAGEEK 0x1781 MetaGeek +vendor AMIT 0x18c5 AMIT +vendor QCOM 0x18e8 Qcom +vendor LINKSYS3 0x1915 Linksys +vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated +vendor STELERA 0x1a8d Stelera Wireless +vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik +vendor DLINK 0x2001 D-Link +vendor PLANEX2 0x2019 Planex Communications +vendor ERICSSON 0x2282 Ericsson +vendor MOTOROLA2 0x22b8 Motorola +vendor TRIPPLITE 0x2478 Tripp-Lite +vendor HIROSE 0x2631 Hirose Electric +vendor NHJ 0x2770 NHJ +vendor PLANEX 0x2c02 Planex Communications +vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd +vendor AEI 0x3334 AEI +vendor HANK 0x3353 Hank Connection +vendor PQI 0x3538 PQI +vendor DAISY 0x3579 Daisy Technology +vendor NI 0x3923 National Instruments +vendor MICRONET 0x3980 Micronet Communications +vendor IODATA2 0x40bb I-O Data +vendor IRIVER 0x4102 iRiver +vendor DELL 0x413c Dell +vendor WCH 0x4348 QinHeng Electronics +vendor ACEECA 0x4766 Aceeca +vendor AVERATEC 0x50c2 Averatec +vendor SWEEX 0x5173 Sweex +vendor ONSPEC2 0x55aa OnSpec Electronic Inc. +vendor ZINWELL 0x5a57 Zinwell +vendor SITECOM 0x6189 Sitecom +vendor ARKMICRO 0x6547 Arkmicro Technologies Inc. +vendor 3COM2 0x6891 3Com +vendor INTEL 0x8086 Intel +vendor SITECOM2 0x9016 Sitecom +vendor MOSCHIP 0x9710 MosChip Semiconductor +vendor 3COM3 0xa727 3Com +vendor HP2 0xf003 Hewlett Packard +vendor USRP 0xfffe GNU Radio USRP + +/* + * List of known products. Grouped by vendor. + */ + +/* 3Com products */ +product 3COM HOMECONN 0x009d HomeConnect Camera +product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter +product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter +product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter +product 3COM 3C460 0x11f8 HomeConnect 3C460 +product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro +product 3COM 3C460B 0x4601 HomeConnect 3C460B +product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 +product 3COM3 AR5523_1 0x6893 AR5523 +product 3COM3 AR5523_2 0x6895 AR5523 +product 3COM3 AR5523_3 0x6897 AR5523 + +product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem +product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA +product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera +product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro + +/* AboCom products */ +product ABOCOM XX1 0x110c XX1 +product ABOCOM XX2 0x200c XX2 +product ABOCOM URE450 0x4000 URE450 Ethernet Adapter +product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter +product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter +product ABOCOM XX4 0x4004 XX4 +product ABOCOM XX5 0x4007 XX5 +product ABOCOM XX6 0x400b XX6 +product ABOCOM XX7 0x400c XX7 +product ABOCOM RTL8151 0x401a RTL8151 +product ABOCOM XX8 0x4102 XX8 +product ABOCOM XX9 0x4104 XX9 +product ABOCOM UF200 0x420a UF200 Ethernet +product ABOCOM WL54 0x6001 WL54 +product ABOCOM XX10 0xabc1 XX10 +product ABOCOM BWU613 0xb000 BWU613 +product ABOCOM HWU54DM 0xb21b HWU54DM +product ABOCOM RT2573_2 0xb21c RT2573 +product ABOCOM RT2573_3 0xb21d RT2573 +product ABOCOM RT2573_4 0xb21e RT2573 +product ABOCOM WUG2700 0xb21f WUG2700 + +/* Accton products */ +product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter +product ACCTON 2664W 0x3501 2664W +product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter +product ACCTON SMCWUSBG 0x4505 SMCWUSB-G +product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN +product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter +product ACCTON ZD1211B 0xe501 ZD1211B + +/* Aceeca products */ +product ACEECA MEZ1000 0x0001 MEZ1000 RDA + +/* Acer Communications & Multimedia (oemd by Surecom) */ +product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter + +/* Acer Labs products */ +product ACERLABS M5632 0x5632 USB 2.0 Data Link + +/* Acer Peripherals, Inc. products */ +product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U +product ACERP ACERSCAN_320U 0x2022 Acerscan 320U +product ACERP ACERSCAN_640U 0x2040 Acerscan 640U +product ACERP ACERSCAN_620U 0x2060 Acerscan 620U +product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U +product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT +product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U +product ACERP ATAPI 0x6003 ATA/ATAPI Adapter +product ACERP AWL300 0x9000 AWL300 Wireless Adapter +product ACERP AWL400 0x9001 AWL400 Wireless Adapter + +/* Acer Warp products */ +product ACERW WARPLINK 0x0204 Warplink + +/* Actiontec, Inc. products */ +product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter +product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A +product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b +product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter + +/* ACTiSYS products */ +product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR + +/* ActiveWire, Inc. products */ +product ACTIVEWIRE IOBOARD 0x0100 I/O Board +product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware + +/* Adaptec products */ +product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN + +/* Addtron products */ +product ADDTRON AWU120 0xff31 AWU-120 + +/* ADMtek products */ +product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet +product ADMTEK PEGASUS 0x0986 AN986 Ethernet +product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet +product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet +product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet + +/* ADDON products */ +/* PNY OEMs these */ +product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive +product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive +product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive +product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) + +/* Addonics products */ +product ADDONICS2 CABLE_205 0xa001 Cable 205 + +/* ADS products */ +product ADS UBS10BT 0x0008 UBS-10BT Ethernet +product ADS UBS10BTX 0x0009 UBS-10BT Ethernet + +/* AEI products */ +product AEI FASTETHERNET 0x1701 Fast Ethernet + +/* Agate Technologies products */ +product AGATE QDRIVE 0x0378 Q-Drive + +/* AGFA products */ +product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U +product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U +product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch +product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U +product AGFA SNAPSCANE40 0x208d SnapScan e40 +product AGFA SNAPSCANE50 0x208f SnapScan e50 +product AGFA SNAPSCANE20 0x2091 SnapScan e20 +product AGFA SNAPSCANE25 0x2095 SnapScan e25 +product AGFA SNAPSCANE26 0x2097 SnapScan e26 +product AGFA SNAPSCANE52 0x20fd SnapScan e52 + +/* Ain Communication Technology products */ +product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter + +/* AIPTEK products */ +product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega +product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3 + +/* AirPrime products */ +product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card + +/* AKS products */ +product AKS USBHASP 0x0001 USB-HASP 0.06 + +/* Alcor Micro, Inc. products */ +product ALCOR2 KBD_HUB 0x2802 Kbd Hub + +product ALCOR TRANSCEND 0x6387 Transcend JetFlash Drive +product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub +product ALCOR AU9814 0x9215 AU9814 Hub +product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader +product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard +product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub + +/* Altec Lansing products */ +product ALTEC ADA70 0x0070 ADA70 Speakers +product ALTEC ASC495 0xff05 ASC495 Speakers + +/* Allied Telesyn International products */ +product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100 + +/* American Power Conversion products */ +product APC UPS 0x0002 Uninterruptible Power Supply + +/* Ambit Microsystems products */ +product AMBIT WLAN 0x0302 WLAN +product AMBIT NTL_250 0x6098 NTL 250 cable modem + +/* AMIT products */ +product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO + +/* Anchor products */ +product ANCHOR EZUSB 0x2131 EZUSB +product ANCHOR EZLINK 0x2720 EZLINK + +/* AnyData products */ +product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem +product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem + +/* AOX, Inc. products */ +product AOX USB101 0x0008 Ethernet + +/* American Power Conversion products */ +product APC UPS 0x0002 Uninterruptible Power Supply + +/* Apple Computer products */ +product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard +product APPLE OPTMOUSE 0x0302 Optical mouse +product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse +product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard +product APPLE SPEAKERS 0x1101 Speakers +product APPLE IPOD 0x1201 iPod +product APPLE IPOD2G 0x1202 iPod 2G +product APPLE IPOD3G 0x1203 iPod 3G +product APPLE IPOD_04 0x1204 iPod '04' +product APPLE IPODMINI 0x1205 iPod Mini +product APPLE IPOD_06 0x1206 iPod '06' +product APPLE IPOD_07 0x1207 iPod '07' +product APPLE IPOD_08 0x1208 iPod '08' +product APPLE IPODVIDEO 0x1209 iPod Video +product APPLE IPODNANO 0x120a iPod Nano +product APPLE IPHONE 0x1290 iPhone +product APPLE IPHONE_3G 0x1292 iPhone 3G +product APPLE ETHERNET 0x1402 Ethernet A1277 + +/* Arkmicro Technologies */ +product ARKMICRO ARK3116 0x0232 ARK3116 Serial + +/* Asahi Optical products */ +product ASAHIOPTICAL OPTIO230 0x0004 Digital camera +product ASAHIOPTICAL OPTIO330 0x0006 Digital camera + +/* Asante products */ +product ASANTE EA 0x1427 Ethernet + +/* ASIX Electronics products */ +product ASIX AX88172 0x1720 10/100 Ethernet +product ASIX AX88178 0x1780 AX88178 +product ASIX AX88772 0x7720 AX88772 + +/* ASUS products */ +product ASUS WL167G 0x1707 WL-167g Wireless Adapter +product ASUS WL159G 0x170c WL-159g +product ASUS A9T_WIFI 0x171b A9T wireless +product ASUS RT2573_1 0x1723 RT2573 +product ASUS RT2573_2 0x1724 RT2573 +product ASUS LCM 0x1726 LCM display +product ASUS P535 0x420f ASUS P535 PDA + +/* ATen products */ +product ATEN UC1284 0x2001 Parallel printer +product ATEN UC10T 0x2002 10Mbps Ethernet +product ATEN UC110T 0x2007 UC-110T Ethernet +product ATEN UC232A 0x2008 Serial +product ATEN UC210T 0x2009 UC-210T Ethernet +product ATEN DSB650C 0x4000 DSB-650C + +/* Atheros Communications products */ +product ATHEROS AR5523 0x0001 AR5523 +product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware) +product ATHEROS2 AR5523_1 0x0001 AR5523 +product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware) +product ATHEROS2 AR5523_2 0x0003 AR5523 +product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware) +product ATHEROS2 AR5523_3 0x0005 AR5523 +product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware) + +/* Atmel Comp. products */ +product ATMEL UHB124 0x3301 UHB124 hub +product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter +product ATMEL BW002 0x7605 BW002 Wireless Adapter +product ATMEL WL1130USB 0x7613 WL-1130 USB +product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter + +/* Avision products */ +product AVISION 1200U 0x0268 1200U scanner + +/* Axesstel products */ +product AXESSTEL DATAMODEM 0x1000 Data Modem + +/* Baltech products */ +product BALTECH CARDREADER 0x9999 Card reader + +/* B&B Electronics products */ +product BBELECTRONICS USOTL4 0xAC01 RS-422/485 + +/* Belkin products */ +/*product BELKIN F5U111 0x???? F5U111 Ethernet*/ +product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter +product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth +product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth +product BELKIN F5U103 0x0103 F5U103 Serial +product BELKIN F5U109 0x0109 F5U109 Serial +product BELKIN USB2SCSI 0x0115 USB to SCSI +product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter +product BELKIN USB2LAN 0x0121 USB to LAN +product BELKIN F5U208 0x0208 F5U208 VideoBus II +product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub +product BELKIN F5U257 0x0257 F5U257 Serial +product BELKIN F5U409 0x0409 F5U409 Serial +product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS +product BELKIN F5U120 0x1203 F5U120-PC Hub +product BELKIN ZD1211B 0x4050 ZD1211B +product BELKIN F5D5055 0x5055 F5D5055 +product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter +product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter +product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter +/* Also sold as 'Ativa 802.11g wireless card' */ +product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter +product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter +product BELKIN2 F5U002 0x0002 F5U002 Parallel printer + +/* Billionton products */ +product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet +product BILLIONTON USBLP100 0x0987 USB100LP +product BILLIONTON USBEL100 0x0988 USB100EL +product BILLIONTON USBE100 0x8511 USBE100 +product BILLIONTON USB2AR 0x90ff USB2AR Ethernet + +/* Broadcom products */ +product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle + +/* Brother Industries products */ +product BROTHER HL1050 0x0002 HL-1050 laser printer + +/* Behavior Technology Computer products */ +product BTC BTC7932 0x6782 Keyboard with mouse port + +/* Canon, Inc. products */ +product CANON N656U 0x2206 CanoScan N656U +product CANON N1220U 0x2207 CanoScan N1220U +product CANON D660U 0x2208 CanoScan D660U +product CANON N676U 0x220d CanoScan N676U +product CANON N1240U 0x220e CanoScan N1240U +product CANON LIDE25 0x2220 CanoScan LIDE 25 +product CANON S10 0x3041 PowerShot S10 +product CANON S100 0x3045 PowerShot S100 +product CANON S200 0x3065 PowerShot S200 +product CANON REBELXT 0x30ef Digital Rebel XT + +/* CATC products */ +product CATC NETMATE 0x000a Netmate Ethernet +product CATC NETMATE2 0x000c Netmate2 Ethernet +product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer +product CATC ANDROMEDA 0x1237 Andromeda hub + +/* CASIO products */ +product CASIO QV_DIGICAM 0x1001 QV DigiCam +product CASIO EXS880 0x1105 Exilim EX-S880 +product CASIO BE300 0x2002 BE-300 PDA +product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB + +/* CCYU products */ +product CCYU ED1064 0x2136 EasyDisk ED1064 + +/* Century products */ +product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure + +/* Cherry products */ +product CHERRY MY3000KBD 0x0001 My3000 keyboard +product CHERRY MY3000HUB 0x0003 My3000 hub +product CHERRY CYBOARD 0x0004 CyBoard Keyboard + +/* Chic Technology products */ +product CHIC MOUSE1 0x0001 mouse +product CHIC CYPRESS 0x0003 Cypress USB Mouse + +/* Chicony products */ +product CHICONY KB8933 0x0001 KB-8933 keyboard +product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera + +/* CH Products */ +product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle +product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals +product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick +product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke + +/* Cisco-Linksys products */ +product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter +product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter +product CISCOLINKSYS USB200MV2 0x0018 USB200M v2 +product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter +product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC +product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR +product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G + +/* CMOTECH products */ +product CMOTECH CNU510 0x5141 CDMA Technologies USB modem +product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem +product CMOTECH CGU628 0x6006 CGU-628 +product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem +product CMOTECH DISK 0xf000 disk mode + +/* Compaq products */ +product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC +product COMPAQ PJB100 0x504a Personal Jukebox PJB100 +product COMPAQ IPAQLINUX 0x505a iPAQ Linux + +/* Composite Corp products looks the same as "TANGTOP" */ +product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor + +/* Conceptronic products */ +product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN +product CONCEPTRONIC C11U 0x7100 C11U +product CONCEPTRONIC WL210 0x7110 WL-210 +product CONCEPTRONIC AR5523_1 0x7801 AR5523 +product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware) +product CONCEPTRONIC AR5523_2 0x7811 AR5523 +product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware) +product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN +product CONCEPTRONIC2 C54RU2 0x3c22 C54RU + +/* Connectix products */ +product CONNECTIX QUICKCAM 0x0001 QuickCam + +/* Corega products */ +product COREGA ETHER_USB_T 0x0001 Ether USB-T +product COREGA FETHER_USB_TX 0x0004 FEther USB-TX +product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11 +product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS +product COREGA WLANUSB 0x0012 Wireless LAN Stick-11 +product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX +product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key +product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL +product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX +product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11 +product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC + +/* Creative products */ +product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player +product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG +product CREATIVE NOMAD 0x4106 Nomad +product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster +product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse + +/* Cambridge Silicon Radio Ltd. products */ +product CSR BT_DONGLE 0x0001 Bluetooth USB dongle +product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State + +/* CTX products */ +product CTX EX1300 0x9999 Ex1300 hub + +/* Curitel products */ +product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C) +product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) +product CURITEL PC5740 0x3701 Broadband Wireless modem + +/* CyberPower products */ +product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD + +/* CyberTAN Technology products */ +product CYBERTAN TG54USB 0x1666 TG54USB + +/* Cypress Semiconductor products */ +product CYPRESS MOUSE 0x0001 mouse +product CYPRESS THERMO 0x0002 thermometer +product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy +product CYPRESS KBDHUB 0x0101 Keyboard/Hub +product CYPRESS FMRADIO 0x1002 FM Radio +product CYPRESS USBRS232 0x5500 USB-RS232 Interface +product CYPRESS SLIM_HUB 0x6560 Slim Hub + +/* Daisy Technology products */ +product DAISY DMC 0x6901 USB MultiMedia Reader + +/* Dallas Semiconductor products */ +product DALLAS J6502 0x4201 J-6502 speakers + +/* Dell products */ +product DELL PORT 0x0058 Port Replicator +product DELL AIO926 0x5115 Photo AIO Printer 926 +product DELL BC02 0x8000 BC02 Bluetooth USB Adapter +product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN +product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter +product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN +product DELL U740 0x8135 Dell U740 CDMA + +/* Delorme Paublishing products */ +product DELORME EARTHMATE 0x0100 Earthmate GPS + +/* Desknote products */ +product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B + +/* Diamond products */ +product DIAMOND RIO500USB 0x0001 Rio 500 USB + +/* Dick Smith Electronics (really C-Net) products */ +product DICKSMITH RT2573 0x9022 RT2573 +product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F + +/* Digi International products */ +product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 +product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 +product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 + +/* D-Link products */ +/*product DLINK DSBS25 0x0100 DSB-S25 serial*/ +product DLINK DUBE100 0x1a00 10/100 Ethernet +product DLINK DSB650TX4 0x200c 10/100 Ethernet +product DLINK DWL120E 0x3200 DWL-120 rev E +product DLINK DWL122 0x3700 DWL-122 +product DLINK DWLG120 0x3701 DWL-G120 +product DLINK DWL120F 0x3702 DWL-120 rev F +product DLINK DWLAG132 0x3a00 DWL-AG132 +product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware) +product DLINK DWLG132 0x3a02 DWL-G132 +product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware) +product DLINK DWLAG122 0x3a04 DWL-AG122 +product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware) +product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter +product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 +product DLINK DSB650C 0x4000 10Mbps Ethernet +product DLINK DSB650TX1 0x4001 10/100 Ethernet +product DLINK DSB650TX 0x4002 10/100 Ethernet +product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet +product DLINK DSB650TX3 0x400b 10/100 Ethernet +product DLINK DSB650TX2 0x4102 10/100 Ethernet +product DLINK DSB650 0xabc1 10/100 Ethernet +product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1 +product DLINK2 WUA1340 0x3c04 WUA-1340 +product DLINK2 DWA111 0x3c06 DWA-111 +product DLINK2 DWA110 0x3c07 DWA-110 + +/* DMI products */ +product DMI CFSM_RW 0xa109 CF/SM Reader/Writer + +/* DrayTek products */ +product DRAYTEK VIGOR550 0x0550 Vigor550 + +/* dresden elektronik products */ +product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard + +/* Dynastream Innovations */ +product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board + +/* EIZO products */ +product EIZO HUB 0x0000 hub +product EIZO MONITOR 0x0001 monitor + +/* ELCON Systemtechnik products */ +product ELCON PLAN 0x0002 Goldpfeil P-LAN + +/* Elecom products */ +product ELECOM MOUSE29UO 0x0002 mouse 29UO +product ELECOM LDUSBTX0 0x200c LD-USB/TX +product ELECOM LDUSBTX1 0x4002 LD-USB/TX +product ELECOM LDUSBLTX 0x4005 LD-USBL/TX +product ELECOM LDUSBTX2 0x400b LD-USB/TX +product ELECOM LDUSB20 0x4010 LD-USB20 +product ELECOM UCSGT 0x5003 UC-SGT +product ELECOM UCSGT0 0x5004 UC-SGT +product ELECOM LDUSBTX3 0xabc1 LD-USB/TX + +/* Elsa products */ +product ELSA MODEM1 0x2265 ELSA Modem Board +product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet + +/* EMS products */ +product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter + +/* Entrega products */ +product ENTREGA 1S 0x0001 1S serial +product ENTREGA 2S 0x0002 2S serial +product ENTREGA 1S25 0x0003 1S25 serial +product ENTREGA 4S 0x0004 4S serial +product ENTREGA E45 0x0005 E45 Ethernet +product ENTREGA CENTRONICS 0x0006 Parallel Port +product ENTREGA XX1 0x0008 Ethernet +product ENTREGA 1S9 0x0093 1S9 serial +product ENTREGA EZUSB 0x8000 EZ-USB +/*product ENTREGA SERIAL 0x8001 DB25 Serial*/ +product ENTREGA 2U4S 0x8004 2U4S serial/usb hub +product ENTREGA XX2 0x8005 Ethernet +/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ + +/* Epson products */ +product EPSON PRINTER1 0x0001 USB Printer +product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac +product EPSON PRINTER3 0x0003 ISD USB Smart Cable +product EPSON PRINTER5 0x0005 USB Printer +product EPSON 636 0x0101 Perfection 636U / 636Photo scanner +product EPSON 610 0x0103 Perfection 610 scanner +product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner +product EPSON 1600 0x0107 Expression 1600 scanner +product EPSON 1640 0x010a Perfection 1640SU scanner +product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner +product EPSON 640U 0x010c Perfection 640U scanner +product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner +product EPSON 1650 0x0110 Perfection 1650 scanner +product EPSON GT9700F 0x0112 GT-9700F scanner +product EPSON GT9300UF 0x011b GT-9300UF scanner +product EPSON 3200 0x011c Perfection 3200 scanner +product EPSON 1260 0x011d Perfection 1260 scanner +product EPSON 1660 0x011e Perfection 1660 scanner +product EPSON 1670 0x011f Perfection 1670 scanner +product EPSON 1270 0x0120 Perfection 1270 scanner +product EPSON 2480 0x0121 Perfection 2480 scanner +product EPSON 3590 0x0122 Perfection 3590 scanner +product EPSON 4990 0x012a Perfection 4990 Photo scanner +product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader +product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader +product EPSON CX5400 0x0808 CX5400 scanner +product EPSON 3500 0x080e CX-3500/3600/3650 MFP +product EPSON RX425 0x080f Stylus Photo RX425 scanner +product EPSON DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner +product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner +product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner +product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner +product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner +product EPSON DX4000 0x082f DX4000 MFP scanner +product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner +product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner +product EPSON SX100 0x0841 SX100/NX100 MFP scanner +product EPSON NX300 0x0848 NX300 MFP scanner +product EPSON SX200 0x0849 SX200/SX205 MFP scanner +product EPSON SX400 0x084a SX400/NX400/TX400 MFP scanner + +/* e-TEK Labs products */ +product ETEK 1COM 0x8007 Serial + +/* Extended Systems products */ +product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA + +/* FEIYA products */ +product FEIYA 5IN1 0x1132 5-in-1 Card Reader + +/* Fiberline */ +product FIBERLINE WL430U 0x6003 WL-430U + +/* Fossil, Inc products */ +product FOSSIL WRISTPDA 0x0002 Wrist PDA + +/* Freecom products */ +product FREECOM DVD 0xfc01 DVD drive + +/* Fujitsu Siemens Computers products */ +product FSC E5400 0x1009 PrismGT USB 2.0 WLAN + +/* Future Technology Devices products */ +product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial +product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial +product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial +/* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ +product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi +product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru +product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal +product FTDI EISCOU 0xe888 Expert ISDN Control USB +product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge +product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II +product FTDI PCMSFU 0xe88b Precision Clock MSF USB +product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG +product FTDI MAXSTREAM 0xee18 Maxstream PKG-U +product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial +product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 +product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 +product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family +product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family +product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD +product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD +product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD +product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD +product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD +product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation + +/* Fuji photo products */ +product FUJIPHOTO MASS0100 0x0100 Mass Storage + +/* Fujitsu protducts */ +product FUJITSU AH_F401U 0x105b AH-F401U Air H device + +/* Garmin products */ +product GARMIN IQUE_3600 0x0004 iQue 3600 + +/* General Instruments (Motorola) products */ +product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem + +/* Genesys Logic products */ +product GENESYS GL620USB 0x0501 GL620USB Host-Host interface +product GENESYS GL650 0x0604 GL650 Hub +product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader +product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 +product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge +product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader + +/* GIGABYTE products */ +product GIGABYTE GN54G 0x8001 GN-54G +product GIGABYTE GNBR402W 0x8002 GN-BR402W +product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 +product GIGABYTE GNWBKG 0x8007 GN-WBKG +product GIGABYTE GNWB01GS 0x8008 GN-WB01GS +product GIGABYTE GNWI05GS 0x800a GN-WI05GS + +/* Gigaset products */ +product GIGASET WLAN 0x0701 WLAN +product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G +product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware) +product GIGASET AR5523 0x0712 AR5523 +product GIGASET AR5523_NF 0x0713 AR5523 (no firmware) +product GIGASET RT2573 0x0722 RT2573 + +/* Global Sun Technology product */ +product GLOBALSUN AR5523_1 0x7801 AR5523 +product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware) +product GLOBALSUN AR5523_2 0x7811 AR5523 +product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware) + +/* Globespan products */ +product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN +product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN + +/* G.Mate, Inc products */ +product GMATE YP3X00 0x1001 YP3X00 PDA + +/* GoHubs products */ +product GOHUBS GOCOM232 0x1001 GoCOM232 Serial + +/* Good Way Technology products */ +product GOODWAY GWUSB2E 0x6200 GWUSB2E +product GOODWAY RT2573 0xc019 RT2573 + +/* Gravis products */ +product GRAVIS GAMEPADPRO 0x4001 GamePad Pro + +/* GREENHOUSE products */ +product GREENHOUSE KANA21 0x0001 CF-writer with MP3 + +/* Griffin Technology */ +product GRIFFIN IMATE 0x0405 iMate, ADB Adapter + +/* Guillemot Corporation */ +product GUILLEMOT DALEADER 0xa300 DA Leader +product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN +product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB +product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP + +/* Hagiwara products */ +product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader +product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader +product HAGIWARA FG 0x0005 FlashGate + +/* HAL Corporation products */ +product HAL IMR001 0x0011 Crossam2+USB IR commander + +/* Handspring, Inc. */ +product HANDSPRING VISOR 0x0100 Handspring Visor +product HANDSPRING TREO 0x0200 Handspring Treo +product HANDSPRING TREO600 0x0300 Handspring Treo 600 + +/* Hauppauge Computer Works */ +product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM + +/* Hawking Technologies products */ +product HAWKING UF100 0x400c 10/100 USB Ethernet + +/* Hitachi, Ltd. products */ +product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder +product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface + +/* HP products */ +product HP 895C 0x0004 DeskJet 895C +product HP 4100C 0x0101 Scanjet 4100C +product HP S20 0x0102 Photosmart S20 +product HP 880C 0x0104 DeskJet 880C +product HP 4200C 0x0105 ScanJet 4200C +product HP CDWRITERPLUS 0x0107 CD-Writer Plus +product HP KBDHUB 0x010c Multimedia Keyboard Hub +product HP G55XI 0x0111 OfficeJet G55xi +product HP HN210W 0x011c HN210W 802.11b WLAN +product HP 49GPLUS 0x0121 49g+ graphing calculator +product HP 6200C 0x0201 ScanJet 6200C +product HP S20b 0x0202 PhotoSmart S20 +product HP 815C 0x0204 DeskJet 815C +product HP 3300C 0x0205 ScanJet 3300C +product HP CDW8200 0x0207 CD-Writer Plus 8200e +product HP MMKEYB 0x020c Multimedia keyboard +product HP 1220C 0x0212 DeskJet 1220C +product HP 810C 0x0304 DeskJet 810C/812C +product HP 4300C 0x0305 Scanjet 4300C +product HP CDW4E 0x0307 CD-Writer+ CD-4e +product HP G85XI 0x0311 OfficeJet G85xi +product HP 1200 0x0317 LaserJet 1200 +product HP 5200C 0x0401 Scanjet 5200C +product HP 830C 0x0404 DeskJet 830C +product HP 3400CSE 0x0405 ScanJet 3400cse +product HP 6300C 0x0601 Scanjet 6300C +product HP 840C 0x0604 DeskJet 840c +product HP 2200C 0x0605 ScanJet 2200C +product HP 5300C 0x0701 Scanjet 5300C +product HP 4400C 0x0705 Scanjet 4400C +product HP 4470C 0x0805 Scanjet 4470C +product HP 82x0C 0x0b01 Scanjet 82x0C +product HP 2300D 0x0b17 Laserjet 2300d +product HP 970CSE 0x1004 Deskjet 970Cse +product HP 5400C 0x1005 Scanjet 5400C +product HP 2215 0x1016 iPAQ 22xx/Jornada 548 +product HP 568J 0x1116 Jornada 568 +product HP 930C 0x1204 DeskJet 930c +product HP P2000U 0x1801 Inkjet P-2000U +product HP 640C 0x2004 DeskJet 640c +product HP 4670V 0x3005 ScanJet 4670v +product HP P1100 0x3102 Photosmart P1100 +product HP OJ4215 0x3d11 OfficeJet 4215 +product HP HN210E 0x811c Ethernet HN210E +product HP2 C500 0x6002 PhotoSmart C500 +product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775) + +/* HTC products */ +product HTC WINMOBILE 0x00ce HTC USB Sync +product HTC PPC6700MODEM 0x00cf PPC6700 Modem +product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync + +/* HUAWEI products */ +product HUAWEI MOBILE 0x1001 Huawei Mobile +product HUAWEI E220 0x1003 Huawei HSDPA modem + +/* HUAWEI 3com products */ +product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g + +/* IBM Corporation */ +product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive + +/* Imagination Technologies products */ +product IMAGINATION DBX1 0x2107 DBX1 DSP core + +/* Inside Out Networks products */ +product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports + +/* In-System products */ +product INSYSTEM F5U002 0x0002 Parallel printer +product INSYSTEM ATAPI 0x0031 ATAPI Adapter +product INSYSTEM ISD110 0x0200 IDE Adapter ISD110 +product INSYSTEM ISD105 0x0202 IDE Adapter ISD105 +product INSYSTEM USBCABLE 0x081a USB cable +product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2 + +/* Intel products */ +product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera +product INTEL TESTBOARD 0x9890 82930 test board + +/* Intersil products */ +product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN +product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN + +/* Interpid Control Systems products */ +product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface +product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface + +/* I/O DATA products */ +product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 +product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 +product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card +product IODATA USBSDRW 0x031e USB-SDRW SD-card +product IODATA USBETT 0x0901 USB ETT +product IODATA USBETTX 0x0904 USB ETTX +product IODATA USBETTXS 0x0913 USB ETTX +product IODATA USBWNB11A 0x0919 USB WN-B11 +product IODATA USBWNB11 0x0922 USB Airport WN-B11 +product IODATA ETGUS2 0x0930 ETG-US2 +product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 +product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC + +/* Iomega products */ +product IOMEGA ZIP100 0x0001 Zip 100 +product IOMEGA ZIP250 0x0030 Zip 250 + +/* Ituner networks products */ +product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20 +product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20 + +/* Jablotron products */ +product JABLOTRON PC60B 0x0001 PC-60B + +/* Jaton products */ +product JATON EDA 0x5704 Ethernet + +/* JVC products */ +product JVC GR_DX95 0x000a GR-DX95 +product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet + +/* JRC products */ +product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V + +/* Kawatsu products */ +product KAWATSU MH4000P 0x0003 MiniHub 4000P + +/* Keisokugiken Corp. products */ +product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ + +/* Kensington products */ +product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball +product KENSINGTON TURBOBALL 0x1005 TurboBall + +/* Keyspan products */ +product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware) +product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware) +product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware) +product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware) +product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware) +product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware) +product KEYSPAN USA19 0x0107 USA-19 serial Adapter +product KEYSPAN USA19W 0x0108 USA-19W serial Adapter +product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware) +product KEYSPAN USA49W 0x010a USA-49W serial Adapter +product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware) +product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter +product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware) +product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter +product KEYSPAN USA28 0x010f USA-28 serial Adapter +product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter +product KEYSPAN USA18 0x0111 USA-18 serial Adapter +product KEYSPAN USA18X 0x0112 USA-18X serial Adapter +product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware) +product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware) +product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter +product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware) +product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter +product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware) +product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter +product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter +product KEYSPAN UIA10 0x0201 UIA-10 remote control +product KEYSPAN UIA11 0x0202 UIA-11 remote control + +/* Kingston products */ +product KINGSTON XX1 0x0008 Ethernet +product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet + +/* Kawasaki products */ +product KLSI DUH3E10BT 0x0008 USB Ethernet +product KLSI DUH3E10BTN 0x0009 USB Ethernet + +/* Kodak products */ +product KODAK DC220 0x0100 Digital Science DC220 +product KODAK DC260 0x0110 Digital Science DC260 +product KODAK DC265 0x0111 Digital Science DC265 +product KODAK DC290 0x0112 Digital Science DC290 +product KODAK DC240 0x0120 Digital Science DC240 +product KODAK DC280 0x0130 Digital Science DC280 + +/* Konica Corp. Products */ +product KONICA CAMERA 0x0720 Digital Color Camera + +/* KYE products */ +product KYE NICHE 0x0001 Niche mouse +product KYE NETSCROLL 0x0003 Genius NetScroll mouse +product KYE FLIGHT2000 0x1004 Flight 2000 joystick +product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner + +/* Kyocera products */ +product KYOCERA FINECAM_S3X 0x0100 Finecam S3x +product KYOCERA FINECAM_S4 0x0101 Finecam S4 +product KYOCERA FINECAM_S5 0x0103 Finecam S5 +product KYOCERA FINECAM_L3 0x0105 Finecam L3 +product KYOCERA AHK3001V 0x0203 AH-K3001V +product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM + +/* LaCie products */ +product LACIE HD 0xa601 Hard Disk +product LACIE CDRW 0xa602 CD R/W + +/* Lexar products */ +product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader +product LEXAR CF_READER 0xb002 USB CF Reader + +/* Lexmark products */ +product LEXMARK S2450 0x0009 Optra S 2450 + +/* Linksys products */ +product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 +product LINKSYS USB10TX1 0x200c USB10TX +product LINKSYS USB10T 0x2202 USB10T Ethernet +product LINKSYS USB100TX 0x2203 USB100TX Ethernet +product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA +product LINKSYS USB10TA 0x2206 USB10TA Ethernet +product LINKSYS USB10TX2 0x400b USB10TX +product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter +product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet +product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter +product LINKSYS4 USB1000 0x0039 USB1000 + +/* Logitech products */ +product LOGITECH M2452 0x0203 M2452 keyboard +product LOGITECH M4848 0x0301 M4848 mouse +product LOGITECH PAGESCAN 0x040f PageScan +product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web +product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro +product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express +product LOGITECH QUICKCAM 0x0850 QuickCam +product LOGITECH N43 0xc000 N43 +product LOGITECH N48 0xc001 N48 mouse +product LOGITECH MBA47 0xc002 M-BA47 mouse +product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse +product LOGITECH BD58 0xc00c BD58 mouse +product LOGITECH UN58A 0xc030 iFeel Mouse +product LOGITECH UN53B 0xc032 iFeel MouseMan +product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme +product LOGITECH WMRPAD 0xc20a WingMan RumblePad +product LOGITECH WMJOY 0xc281 WingMan Force joystick +product LOGITECH BB13 0xc401 USB-PS/2 Trackball +product LOGITECH RK53 0xc501 Cordless mouse +product LOGITECH RB6 0xc503 Cordless keyboard +product LOGITECH MX700 0xc506 Cordless optical mouse +product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro + +/* Logitec Corp. products */ +product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 +product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 + +/* Lucent products */ +product LUCENT EVALKIT 0x1001 USS-720 evaluation kit + +/* Luwen products */ +product LUWEN EASYDISK 0x0005 EasyDisc + +/* Macally products */ +product MACALLY MOUSE1 0x0101 mouse + +/* MCT Corp. */ +product MCT HUB0100 0x0100 Hub +product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub +product MCT USB232 0x0210 USB-232 Interface +product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products + +/* Meizu Electronics */ +product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL) + +/* Melco, Inc products */ +product MELCO LUATX1 0x0001 LUA-TX Ethernet +product MELCO LUATX5 0x0005 LUA-TX Ethernet +product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet +product MELCO LUAKTX 0x0012 LUA-KTX Ethernet +product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG +product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet +product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN +product MELCO KG54 0x0066 WLI-U2-KG54 WLAN +product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN +product MELCO NINWIFI 0x008b Nintendo Wi-Fi +product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation +product MELCO SG54HP 0x00d8 WLI-U2-SG54HP +product MELCO G54HP 0x00d9 WLI-U2-G54HP +product MELCO KG54L 0x00da WLI-U2-KG54L +product MELCO SG54HG 0x00f4 WLI-U2-SG54HG + +/* Merlin products */ +product MERLIN V620 0x1110 Merlin V620 + +/* MetaGeek products */ +product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy +product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x + +/* Metricom products */ +product METRICOM RICOCHET_GS 0x0001 Ricochet GS + +/* MGE UPS Systems */ +product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 +product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 + +/* Micro Star International products */ +product MSI BT_DONGLE 0x1967 Bluetooth USB dongle +product MSI UB11B 0x6823 UB11B +product MSI RT2570 0x6861 RT2570 +product MSI RT2570_2 0x6865 RT2570 +product MSI RT2570_3 0x6869 RT2570 +product MSI RT2573_1 0x6874 RT2573 +product MSI RT2573_2 0x6877 RT2573 +product MSI RT2573_3 0xa861 RT2573 +product MSI RT2573_4 0xa874 RT2573 + +/* Microsoft products */ +product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro +product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse +product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite +product MICROSOFT DDS80 0x0014 Digital Sound System 80 +product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel +product MICROSOFT INETPRO 0x001c Internet Keyboard Pro +product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer +product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse +product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro +product MICROSOFT MN510 0x006e MN510 Wireless +product MICROSOFT MN110 0x007a 10/100 USB NIC +product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse +product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023) +product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043) +product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) +product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049) +product MICROSOFT WLUSBMOUSE 0x00b9 Wireless USB Mouse +product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN + +/* Microtech products */ +product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 +product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 +product MICROTECH DPCM 0x0006 USB CameraMate +product MICROTECH FREECOM 0xfc01 Freecom USB-IDE + +/* Microtek products */ +product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner +product MICROTEK X6U 0x0099 ScanMaker X6 - X6U +product MICROTEK C6 0x009a Phantom C6 scanner +product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner +product MICROTEK V6USL 0x00a3 ScanMaker V6USL +product MICROTEK V6USL2 0x80a3 ScanMaker V6USL +product MICROTEK V6UL 0x80ac ScanMaker V6UL + +/* Microtune, Inc. products */ +product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle + +/* Midiman products */ +product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2 + +/* MindsAtWork products */ +product MINDSATWORK WALLET 0x0001 Digital Wallet + +/* Minolta Co., Ltd. */ +product MINOLTA 2300 0x4001 Dimage 2300 +product MINOLTA S304 0x4007 Dimage S304 +product MINOLTA X 0x4009 Dimage X +product MINOLTA 5400 0x400e Dimage 5400 +product MINOLTA F300 0x4011 Dimage F300 +product MINOLTA E223 0x4017 Dimage E223 + +/* Mitsumi products */ +product MITSUMI CDRRW 0x0000 CD-R/RW Drive +product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle +product MITSUMI FDD 0x6901 USB FDD + +/* Mobility products */ +product MOBILITY EA 0x0204 Ethernet +product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet + +/* MosChip products */ +product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter +product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet + +/* Motorola products */ +product MOTOROLA MC141555 0x1555 MC141555 hub controller +product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem +product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones +product MOTOROLA2 E398 0x4810 E398 Mobile Phone +product MOTOROLA2 USBLAN 0x600c USBLAN +product MOTOROLA2 USBLAN2 0x6027 USBLAN + +/* MultiTech products */ +product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem + +/* Mustek products */ +product MUSTEK 1200CU 0x0001 1200 CU scanner +product MUSTEK 600CU 0x0002 600 CU scanner +product MUSTEK 1200USB 0x0003 1200 USB scanner +product MUSTEK 1200UB 0x0006 1200 UB scanner +product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner +product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner +product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner +product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner +product MUSTEK 600USB 0x0873 600 USB scanner +product MUSTEK MDC800 0xa800 MDC-800 digital camera + +/* M-Systems products */ +product MSYSTEMS DISKONKEY 0x0010 DiskOnKey +product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey + +/* Myson products */ +product MYSON HEDEN 0x8818 USB-IDE +product MYSON STARREADER 0x9920 USB flash card adapter + +/* National Semiconductor */ +product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 +product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 + +/* NEC products */ +product NEC HUB 0x55aa hub +product NEC HUB_B 0x55ab hub + +/* NEODIO products */ +product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller +product NEODIO ND5010 0x5010 Multi-format Flash Controller + +/* Netac products */ +product NETAC CF_CARD 0x1060 USB-CF-Card +product NETAC ONLYDISK 0x0003 OnlyDisk + +/* NetChip Technology Products */ +product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect +product NETCHIP CLIK_40 0xa140 USB Clik! 40 +product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x + +/* Netgear products */ +product NETGEAR EA101 0x1001 Ethernet +product NETGEAR EA101X 0x1002 Ethernet +product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1 +product NETGEAR FA120 0x1040 USB 2.0 Ethernet +product NETGEAR WG111V2_2 0x4240 PrismGT USB 2.0 WLAN +product NETGEAR WG111U 0x4300 WG111U +product NETGEAR WG111U_NF 0x4301 WG111U (no firmware) +product NETGEAR WG111V2 0x6a00 WG111V2 +product NETGEAR2 MA101 0x4100 MA101 +product NETGEAR2 MA101B 0x4102 MA101 Rev B +product NETGEAR3 WG111T 0x4250 WG111T +product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware) +product NETGEAR3 WPN111 0x5f00 WPN111 +product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware) + +/* Nikon products */ +product NIKON E990 0x0102 Digital Camera E990 +product NIKON LS40 0x4000 CoolScan LS40 ED +product NIKON D300 0x041a Digital Camera D300 + +/* NovaTech Products */ +product NOVATECH NV902 0x9020 NovaTech NV-902W +product NOVATECH RT2573 0x9021 RT2573 + +/* Novatel Wireless products */ +product NOVATEL V640 0x1100 Merlin V620 +product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA +product NOVATEL V620 0x1110 Merlin V620 +product NOVATEL V740 0x1120 Merlin V740 +product NOVATEL V720 0x1130 Merlin V720 +product NOVATEL U740 0x1400 Merlin U740 +product NOVATEL U740_2 0x1410 Merlin U740 +product NOVATEL U870 0x1420 Merlin U870 +product NOVATEL XU870 0x1430 Merlin XU870 +product NOVATEL X950D 0x1450 Merlin X950D +product NOVATEL ES620 0x2100 ES620 CDMA +product NOVATEL U720 0x2110 Merlin U720 +product NOVATEL U727 0x4100 Merlin U727 CDMA +product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA +product NOVATEL ZEROCD 0x5010 Novatel ZeroCD +product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD +product NOVATEL U760 0x6000 Novatel U760 +product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver + +/* Merlin products */ +product MERLIN V620 0x1110 Merlin V620 + +/* Olympus products */ +product OLYMPUS C1 0x0102 C-1 Digital Camera +product OLYMPUS C700 0x0105 C-700 Ultra Zoom + +/* OmniVision Technologies, Inc. products */ +product OMNIVISION OV511 0x0511 OV511 Camera +product OMNIVISION OV511PLUS 0xa511 OV511+ Camera + +/* OnSpec Electronic, Inc. */ +product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera +product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader +product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer +product ONSPEC READER 0xa003 Datafab-based Reader +product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader +product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader +product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader +product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1) +product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader +product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55 + +/* Option products */ +product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard +product OPTION GT3G 0x6000 GlobeTrotter 3G datacard +product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard +product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard +product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage +product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem +product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA + +/* OQO */ +product OQO WIFI01 0x0002 model 01 WiFi interface +product OQO BT01 0x0003 model 01 Bluetooth interface +product OQO ETHER01PLUS 0x7720 model 01+ Ethernet +product OQO ETHER01 0x8150 model 01 Ethernet interface + +/* Palm Computing, Inc. product */ +product PALM SERIAL 0x0080 USB Serial +product PALM M500 0x0001 Palm m500 +product PALM M505 0x0002 Palm m505 +product PALM M515 0x0003 Palm m515 +product PALM I705 0x0020 Palm i705 +product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z +product PALM M125 0x0040 Palm m125 +product PALM M130 0x0050 Palm m130 +product PALM TUNGSTEN_T 0x0060 Palm Tungsten T +product PALM ZIRE31 0x0061 Palm Zire 31 +product PALM ZIRE 0x0070 Palm Zire + +/* Panasonic products */ +product PANASONIC LS120CAM 0x0901 LS-120 Camera +product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN +product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN +product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN +product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW +product PANASONIC SDCAAE 0x1b00 MultiMediaCard + +/* Peracom products */ +product PERACOM SERIAL1 0x0001 Serial +product PERACOM ENET 0x0002 Ethernet +product PERACOM ENET3 0x0003 At Home Ethernet +product PERACOM ENET2 0x0005 Ethernet + +/* Philips products */ +product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System +product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System +product PHILIPS HUB 0x0201 hub +product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera +product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera +product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System +product PHILIPS SNU5600 0x1236 SNU5600 +product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit +product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player + +/* Philips Semiconductor products */ +product PHILIPSSEMI HUB1122 0x1122 hub + +/* P.I. Engineering products */ +product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter + +/* Planex Communications products */ +product PLANEX GW_US11H 0x14ea GW-US11H WLAN +product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN +product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN +product PLANEX2 GWUS54HP 0xab01 GW-US54HP +product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2 +product PLANEX2 GWUS54SG 0xc002 GW-US54SG +product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL +product PLANEX2 GWUS54GD 0xed01 GW-US54GD +product PLANEX2 GWUSMM 0xed02 GW-USMM +product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ +product PLANEX3 GU1000T 0xab11 GU-1000T +product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini + +/* Plextor Corp. */ +product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U + +/* PLX products */ +product PLX TESTBOARD 0x9060 test board + +/* PNY products */ +product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive + +/* PortGear products */ +product PORTGEAR EA8 0x0008 Ethernet +product PORTGEAR EA9 0x0009 Ethernet + +/* Portsmith products */ +product PORTSMITH EEA 0x3003 Express Ethernet + +/* Primax products */ +product PRIMAX G2X300 0x0300 G2-200 scanner +product PRIMAX G2E300 0x0301 G2E-300 scanner +product PRIMAX G2300 0x0302 G2-300 scanner +product PRIMAX G2E3002 0x0303 G2E-300 scanner +product PRIMAX 9600 0x0340 Colorado USB 9600 scanner +product PRIMAX 600U 0x0341 Colorado 600u scanner +product PRIMAX 6200 0x0345 Visioneer 6200 scanner +product PRIMAX 19200 0x0360 Colorado USB 19200 scanner +product PRIMAX 1200U 0x0361 Colorado 1200u scanner +product PRIMAX G600 0x0380 G2-600 scanner +product PRIMAX 636I 0x0381 ReadyScan 636i +product PRIMAX G2600 0x0382 G2-600 scanner +product PRIMAX G2E600 0x0383 G2E-600 scanner +product PRIMAX COMFORT 0x4d01 Comfort +product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box +product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 + +/* Prolific products */ +product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface +product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface +product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) +product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) +product PROLIFIC PL2305 0x2305 Parallel printer +product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller +product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface +product PROLIFIC PHAROS 0xaaa0 Prolific Pharos +product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3) +product PROLIFIC2 WSIM 0x2001 Willcom WSIM + +/* Putercom products */ +product PUTERCOM UPA100 0x047e USB-1284 BRIDGE + +/* Qcom products */ +product QCOM RT2573 0x6196 RT2573 +product QCOM RT2573_2 0x6229 RT2573 + +/* Qualcomm products */ +product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone +product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem +product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem +product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem +product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage +product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem + +/* Qtronix products */ +product QTRONIX 980N 0x2011 Scorpion-980N keyboard + +/* Quickshot products */ +product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad + +/* Radio Shack */ +product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable + +/* Rainbow Technologies products */ +product RAINBOW IKEY2000 0x1200 i-Key 2000 + +/* Ralink Technology products */ +product RALINK RT2570 0x1706 RT2500USB Wireless Adapter +product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter +product RALINK RT2573 0x2573 RT2501USB Wireless Adapter +product RALINK RT2671 0x2671 RT2601USB Wireless Adapter +product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter +product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter + +/* ReakTek products */ +/* Green House and CompUSA OEM this part */ +product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet +product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter + +/* Ricoh products */ +product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera +product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera +product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera +product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera +product RICOH VGPVCC7 0x183a VGP-VCC7 Camera +product RICOH VGPVCC8 0x183b VGP-VCC8 Camera + +/* Roland products */ +product ROLAND UM1 0x0009 UM-1 MIDI I/F +product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) +product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) + +/* Rockfire products */ +product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB + +/* RATOC Systems products */ +product RATOC REXUSB60 0xb000 REX-USB60 + +/* Sagem products */ +product SAGEM USBSERIAL 0x0027 USB-Serial Controller +product SAGEM XG760A 0x004a XG-760A +product SAGEM XG76NA 0x0062 XG-76NA + +/* Samsung products */ +product SAMSUNG ML6060 0x3008 ML-6060 laser printer +product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player +product SAMSUNG I500 0x6601 I500 Palm USB Phone + +/* Samsung Techwin products */ +product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410 + +/* SanDisk products */ +product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a +product SANDISK SDDR31 0x0002 ImageMate SDDR-31 +product SANDISK SDDR05 0x0005 ImageMate SDDR-05 +product SANDISK SDDR12 0x0100 ImageMate SDDR-12 +product SANDISK SDDR09 0x0200 ImageMate SDDR-09 +product SANDISK SDDR75 0x0810 ImageMate SDDR-75 +product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB +product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB +product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB + +/* Sanyo Electric products */ +product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone + +/* ScanLogic products */ +product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter +product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner + +/* Senao products */ +product SENAO NUB8301 0x2000 NUB-8301 + +/* ShanTou products */ +product SHANTOU ST268 0x0268 ST268 +product SHANTOU DM9601 0x9601 DM 9601 + +/* Shark products */ +product SHARK PA 0x0400 Pocket Adapter + +/* Sharp products */ +product SHARP SL5500 0x8004 Zaurus SL-5500 PDA +product SHARP SLA300 0x8005 Zaurus SL-A300 PDA +product SHARP SL5600 0x8006 Zaurus SL-5600 PDA +product SHARP SLC700 0x8007 Zaurus SL-C700 PDA +product SHARP SLC750 0x9031 Zaurus SL-C750 PDA +product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone + +/* Shuttle Technology products */ +product SHUTTLE EUSB 0x0001 E-USB Bridge +product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge +product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 +product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter +product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter +product SHUTTLE HIFD 0x0007 Sony Hifd +product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter +product SHUTTLE CF 0x000a eUSB CompactFlash Adapter +product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge +product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge +product SHUTTLE CDRW 0x0101 CD-RW Device +product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader + +/* Siemens products */ +product SIEMENS SPEEDSTREAM 0x1001 SpeedStream +product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022 +product SIEMENS2 WLL013 0x001b WLL013 +product SIEMENS2 ES75 0x0034 GSM module MC35 +product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter +product SIEMENS3 SX1 0x0001 SX1 +product SIEMENS3 X65 0x0003 X65 +product SIEMENS3 X75 0x0004 X75 + +/* Sierra Wireless products */ +product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 +product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595 +product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U +product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E +product SIERRA C597 0x0023 Sierra Wireless Compass 597 +product SIERRA AC875 0x6820 Sierra Wireless AirCard 875 +product SIERRA AC880 0x6850 Sierra Wireless AirCard 880 +product SIERRA AC881 0x6851 Sierra Wireless AirCard 881 +product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E +product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E +product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U +product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U +product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U +product SIERRA EM5625 0x0017 EM5625 +product SIERRA MC5720 0x0218 MC5720 Wireless Modem +product SIERRA MC5720_2 0x0018 MC5720 +product SIERRA MC5725 0x0020 MC5725 +product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275 +product SIERRA MC8755_2 0x6802 MC8755 +product SIERRA MC8765 0x6803 MC8765 +product SIERRA MC8755 0x6804 MC8755 +product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem +product SIERRA MC8755_3 0x6813 MC8755 HSDPA +product SIERRA MC8775_2 0x6815 MC8775 +product SIERRA AIRCARD875 0x6820 Aircard 875 HSDPA +product SIERRA MC8780 0x6832 MC8780 +product SIERRA MC8781 0x6833 MC8781 +product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer + +/* Sigmatel products */ +product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player + +/* SIIG products */ +/* Also: Omnidirectional Control Technology products */ +product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader +product SIIG WINTERREADER 0x0330 WINTERREADER Reader +product SIIG2 USBTOETHER 0x0109 USB TO Ethernet +product SIIG2 US2308 0x0421 Serial + +/* Silicom products */ +product SILICOM U2E 0x0001 U2E +product SILICOM GPE 0x0002 Psion Gold Port Ethernet + +/* SI Labs */ +product SILABS POLOLU 0x803b Pololu Serial +product SILABS ARGUSISP 0x8066 Argussoft ISP +product SILABS CRUMB128 0x807a Crumb128 board +product SILABS DEGREE 0x80ca Degree Controls Inc +product SILABS TRAQMATE 0x80ed Track Systems Traqmate +product SILABS SUUNTO 0x80f6 Suunto Sports Instrument +product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile +product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM +product SILABS CP2102 0xea60 SILABS USB UART +product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG +product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN +product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1 +product SILABS CP2102 0xea60 SILABS USB UARTa +product SILABS CP210X_2 0xea61 CP210x Serial +product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone + +/* Silicon Portals Inc. */ +product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) +product SILICONPORTALS YAPPHONE 0x0201 YAP Phone + +/* Sirius Technologies products */ +product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB + +/* Sitecom products */ +product SITECOM LN029 0x182d USB 2.0 Ethernet +product SITECOM SERIAL 0x2068 USB to serial cable (v2) +product SITECOM2 WL022 0x182d WL-022 + +/* Sitecom Europe products */ +product SITECOMEU LN028 0x061c LN-028 +product SITECOMEU WL113 0x9071 WL-113 +product SITECOMEU ZD1211B 0x9075 ZD1211B +product SITECOMEU WL172 0x90ac WL-172 +product SITECOMEU WL113R2 0x9712 WL-113 rev 2 + +/* Skanhex Technology products */ +product SKANHEX MD_7425 0x410a MD 7425 Camera +product SKANHEX SX_520Z 0x5200 SX 520z Camera + +/* SmartBridges products */ +product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet +product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet + +/* SMC products */ +product SMC 2102USB 0x0100 10Mbps Ethernet +product SMC 2202USB 0x0200 10/100 Ethernet +product SMC 2206USB 0x0201 EZ Connect USB Ethernet +product SMC 2862WG 0xee13 EZ Connect Wireless Adapter +product SMC2 2020HUB 0x2020 USB Hub +product SMC3 2662WUSB 0xa002 2662W-AR Wireless + +/* SOHOware products */ +product SOHOWARE NUB100 0x9100 10/100 USB Ethernet +product SOHOWARE NUB110 0x9110 10/100 USB Ethernet + +/* SOLID YEAR products */ +product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard + +/* SONY products */ +product SONY DSC 0x0010 DSC cameras +product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7 +product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2 +product SONY MSACUS1 0x002d Memorystick MSAC-US1 +product SONY HANDYCAM 0x002e Handycam +product SONY MSC 0x0032 MSC memory stick slot +product SONY CLIE_35 0x0038 Sony Clie v3.5 +product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick +product SONY CLIE_40 0x0066 Sony Clie v4.0 +product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03 +product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot +product SONY CLIE_S360 0x0095 Sony Clie s360 +product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot +product SONY CLIE_41 0x009a Sony Clie v4.1 +product SONY CLIE_NX60 0x00da Sony Clie nx60 +product SONY CLIE_TH55 0x0144 Sony Clie th55 +product SONY CLIE_TJ37 0x0169 Sony Clie tj37 +product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1 + +/* Sony Ericsson products */ +product SONYERICSSON DCU10 0x0528 USB Cable + +/* SOURCENEXT products */ +product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 +product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger + +/* SparkLAN products */ +product SPARKLAN RT2573 0x0004 RT2573 + +/* Sphairon Access Systems GmbH products */ +product SPHAIRON UB801R 0x0110 UB801R + +/* Stelera Wireless products */ +product STELERA ZEROCD 0x1000 Zerocd Installer +product STELERA C105 0x1002 Stelera/Bandrish C105 USB + +/* STMicroelectronics products */ +product STMICRO BIOCPU 0x2016 Biometric Coprocessor +product STMICRO COMMUNICATOR 0x7554 USB Communicator + +/* STSN products */ +product STSN STSN0001 0x0001 Internet Access Device + +/* SUN Corporation products */ +product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 +product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 +product SUNTAC VS10U 0x0009 SUNTAC Slipper U +product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity +product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 +product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 + +/* Sun Microsystems products */ +product SUN KEYBOARD 0x0005 Type 6 USB keyboard +/* XXX The above is a North American PC style keyboard possibly */ +product SUN MOUSE 0x0100 Type 6 USB mouse + +/* Supra products */ +product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem +product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem +product DIAMOND2 RIO600USB 0x5001 Rio 600 USB +product DIAMOND2 RIO800USB 0x5002 Rio 800 USB + +/* Surecom Technology products */ +product SURECOM RT2570 0x11f3 RT2570 +product SURECOM RT2573 0x31f3 RT2573 + +/* Sweex products */ +product SWEEX ZD1211 0x1809 ZD1211 + +/* System TALKS, Inc. */ +product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL + +/* Tapwave products */ +product TAPWAVE ZODIAC 0x0100 Zodiac + +/* Taugagreining products */ +product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) + +/* TDK products */ +product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 +product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 +product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 +product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 +product TDK BT_DONGLE 0x0309 Bluetooth USB dongle + +/* TEAC products */ +product TEAC FD05PUB 0x0000 FD-05PUB floppy + +/* Tekram Technology products */ +product TEKRAM QUICKWLAN 0x1630 QuickWLAN +product TEKRAM ZD1211_1 0x5630 ZD1211 +product TEKRAM ZD1211_2 0x6630 ZD1211 + +/* Telex Communications products */ +product TELEX MIC1 0x0001 Enhanced USB Microphone + +/* Ten X Technology, Inc. */ +product TENX UAUDIO0 0xf211 USB audio headset + +/* Texas Intel products */ +product TI UTUSB41 0x1446 UT-USB41 hub +product TI TUSB2046 0x2046 TUSB2046 hub + +/* Thrustmaster products */ +product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad + +/* Topre Corporation products */ +product TOPRE HHKB 0x0100 HHKB Professional + +/* Toshiba Corporation products */ +product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 + +/* Trek Technology products */ +product TREK THUMBDRIVE 0x1111 ThumbDrive +product TREK MEMKEY 0x8888 IBM USB Memory Key +product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB + +/* Tripp-Lite products */ +product TRIPPLITE U209 0x2008 Serial + +/* Trumpion products */ +product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller +product TRUMPION C3310 0x1100 Comotron C3310 MP3 player +product TRUMPION MP3 0x1200 MP3 player + +/* TwinMOS */ +product TWINMOS G240 0xa006 G240 +product TWINMOS MDIV 0x1325 Memory Disk IV + +/* Ubiquam products */ +product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520) + +/* Ultima products */ +product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner + +/* UMAX products */ +product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner +product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner +product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner +product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner +product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner +product UMAX ASTRA3400 0x0060 Astra 3400 Scanner + +/* U-MEDIA Communications products */ +product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU +product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware) +product UMEDIA TEW429UB_A 0x300a TEW-429UB_A +product UMEDIA TEW429UB 0x300b TEW-429UB +product UMEDIA TEW429UBC1 0x300d TEW-429UB C1 +product UMEDIA ALL0298V2 0x3204 ALL0298 v2 +product UMEDIA AR5523_2 0x3205 AR5523 +product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware) + +/* Universal Access products */ +product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter + +/* U.S. Robotics products */ +product USR USR5423 0x0121 USR5423 WLAN + +/* VIA Technologies products */ +product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge + +/* USI products */ +product USI MC60 0x10c5 MC60 Serial + +/* VidzMedia products */ +product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H + +/* Vision products */ +product VISION VC6452V002 0x0002 CPiA Camera + +/* Visioneer products */ +product VISIONEER 7600 0x0211 OneTouch 7600 +product VISIONEER 5300 0x0221 OneTouch 5300 +product VISIONEER 3000 0x0224 Scanport 3000 +product VISIONEER 6100 0x0231 OneTouch 6100 +product VISIONEER 6200 0x0311 OneTouch 6200 +product VISIONEER 8100 0x0321 OneTouch 8100 +product VISIONEER 8600 0x0331 OneTouch 8600 + +/* Vivitar products */ +product VIVITAR 35XX 0x0003 Vivicam 35Xx + +/* VTech products */ +product VTECH RT2570 0x3012 RT2570 +product VTECH ZD1211B 0x3014 ZD1211B + +/* Wacom products */ +product WACOM CT0405U 0x0000 CT-0405-U Tablet +product WACOM GRAPHIRE 0x0010 Graphire +product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 +product WACOM INTUOSA5 0x0021 Intuos A5 +product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet +/* WCH products*/ +product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge +/* Western Digital products */ +product WESTERN COMBO 0x0200 Firewire USB Combo +product WESTERN EXTHDD 0x0400 External HDD +product WESTERN HUB 0x0500 USB HUB +product WESTERN MYBOOK 0x0901 MyBook External HDD + +/* Windbond Electronics */ +product WINBOND UH104 0x5518 4-port USB Hub + +/* WinMaxGroup products */ +product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C + +/* Wistron NeWeb products */ +product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN +product WISTRONNEWEB UR055G 0x0711 UR055G +product WISTRONNEWEB AR5523_1 0x0826 AR5523 +product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware) +product WISTRONNEWEB AR5523_2 0x082a AR5523 +product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware) + +/* Xerox products */ +product XEROX WCM15 0xffef WorkCenter M15 + +/* Xirlink products */ +product XIRLINK PCCAM 0x8080 IBM PC Camera + +/* Xyratex products */ +product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN +product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN + +/* Y-E Data products */ +product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U + +/* Yamaha products */ +product YAMAHA UX256 0x1000 UX256 MIDI I/F +product YAMAHA UX96 0x1008 UX96 MIDI I/F +product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router +product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router +product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router +product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router + +/* Yano products */ +product YANO U640MO 0x0101 U640MO-03 +product YANO FW800HD 0x05fc METALWEAR-HDD + +/* Yiso Wireless Co. products */ +product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card + +/* Z-Com products */ +product ZCOM M4Y750 0x0001 M4Y-750 +product ZCOM XI725 0x0002 XI-725/726 +product ZCOM XI735 0x0005 XI-735 +product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN +product ZCOM ZD1211 0x0011 ZD1211 +product ZCOM AR5523 0x0012 AR5523 +product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware) +product ZCOM ZD1211B 0x001a ZD1211B + +/* Zinwell products */ +product ZINWELL RT2570 0x0260 RT2570 + +/* Zoom Telephonics, Inc. products */ +product ZOOM 2986L 0x9700 2986L Fax modem + +/* Zoran Microelectronics products */ +product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC + +/* Zydas Technology Corporation products */ +product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg +product ZYDAS ZD1211B 0x1215 ZD1211B + +/* ZyXEL Communication Co. products */ +product ZYXEL OMNI56K 0x1500 Omni 56K Plus +product ZYXEL 980N 0x2011 Scorpion-980N keyboard +product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 +product ZYXEL G200V2 0x3407 G-200 v2 +product ZYXEL AG225H 0x3409 AG-225H +product ZYXEL M202 0x340a M-202 +product ZYXEL G220V2 0x340f G-220 v2 +product ZYXEL G202 0x3410 G-202 diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h new file mode 100644 index 0000000..db6d370 --- /dev/null +++ b/sys/dev/usb/usbhid.h @@ -0,0 +1,175 @@ +/* $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_ + +#include + +#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/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c new file mode 100644 index 0000000..9b903f0 --- /dev/null +++ b/sys/dev/usb/wlan/if_rum.c @@ -0,0 +1,2435 @@ +/* $FreeBSD$ */ + +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2501USB/RT2601USB chipset driver + * http://www.ralinktech.com.tw/ + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR rum_debug + +#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 + +#define rum_do_request(sc,req,data) \ + usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) + +static const struct usb2_device_id rum_devs[] = { + { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM) }, + { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2) }, + { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3) }, + { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4) }, + { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700) }, + { USB_VP(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO) }, + { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1) }, + { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2) }, + { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A) }, + { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3) }, + { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC) }, + { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR) }, + { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2) }, + { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL) }, + { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX) }, + { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F) }, + { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573) }, + { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1) }, + { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340) }, + { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111) }, + { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110) }, + { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS) }, + { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS) }, + { USB_VP(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573) }, + { USB_VP(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573) }, + { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB) }, + { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP) }, + { USB_VP(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4) }, + { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573) }, + { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP) }, + { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2) }, + { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM) }, + { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573) }, + { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671) }, + { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2) }, + { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172) }, + { USB_VP(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573) }, + { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573) }, +}; + +MODULE_DEPEND(rum, wlan, 1, 1, 1); +MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); +MODULE_DEPEND(rum, usb, 1, 1, 1); + +static device_probe_t rum_match; +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_write_callback; + +static usb2_proc_callback_t rum_attach_post; +static usb2_proc_callback_t rum_task; +static usb2_proc_callback_t rum_scantask; +static usb2_proc_callback_t rum_promisctask; +static usb2_proc_callback_t rum_amrr_task; +static usb2_proc_callback_t rum_init_task; +static usb2_proc_callback_t rum_stop_task; + +static struct ieee80211vap *rum_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void rum_vap_delete(struct ieee80211vap *); +static void rum_tx_free(struct rum_tx_data *, int); +static void rum_setup_tx_list(struct rum_softc *); +static void rum_unsetup_tx_list(struct rum_softc *); +static int rum_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void rum_setup_tx_desc(struct rum_softc *, + struct rum_tx_desc *, uint32_t, uint16_t, int, + int); +static int rum_tx_mgt(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static int rum_tx_raw(struct rum_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int rum_tx_data(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static void rum_start(struct ifnet *); +static int rum_ioctl(struct ifnet *, u_long, caddr_t); +static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, + int); +static uint32_t rum_read(struct rum_softc *, uint16_t); +static void rum_read_multi(struct rum_softc *, uint16_t, void *, + int); +static void rum_write(struct rum_softc *, uint16_t, uint32_t); +static void rum_write_multi(struct rum_softc *, uint16_t, void *, + size_t); +static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); +static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); +static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); +static void rum_select_antenna(struct rum_softc *); +static void rum_enable_mrr(struct rum_softc *); +static void rum_set_txpreamble(struct rum_softc *); +static void rum_set_basicrates(struct rum_softc *); +static void rum_select_band(struct rum_softc *, + struct ieee80211_channel *); +static void rum_set_chan(struct rum_softc *, + struct ieee80211_channel *); +static void rum_enable_tsf_sync(struct rum_softc *); +static void rum_update_slot(struct ifnet *); +static void rum_set_bssid(struct rum_softc *, const uint8_t *); +static void rum_set_macaddr(struct rum_softc *, const uint8_t *); +static const char *rum_get_rf(int); +static void rum_read_eeprom(struct rum_softc *); +static int rum_bbp_init(struct rum_softc *); +static void rum_init(void *); +static int rum_load_microcode(struct rum_softc *, const u_char *, + size_t); +static int rum_prepare_beacon(struct rum_softc *, + struct ieee80211vap *); +static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void rum_newassoc(struct ieee80211_node *, int); +static void rum_scan_start(struct ieee80211com *); +static void rum_scan_end(struct ieee80211com *); +static void rum_set_channel(struct ieee80211com *); +static int rum_get_rssi(struct rum_softc *, uint8_t); +static void rum_amrr_start(struct rum_softc *, + struct ieee80211_node *); +static void rum_amrr_timeout(void *); +static int rum_pause(struct rum_softc *, int); +static void rum_queue_command(struct rum_softc *, + usb2_proc_callback_t *, struct usb2_proc_msg *, + struct usb2_proc_msg *); + +static const struct { + uint32_t reg; + uint32_t val; +} rum_def_mac[] = { + { RT2573_TXRX_CSR0, 0x025fb032 }, + { RT2573_TXRX_CSR1, 0x9eaa9eaf }, + { RT2573_TXRX_CSR2, 0x8a8b8c8d }, + { RT2573_TXRX_CSR3, 0x00858687 }, + { RT2573_TXRX_CSR7, 0x2e31353b }, + { RT2573_TXRX_CSR8, 0x2a2a2a2c }, + { RT2573_TXRX_CSR15, 0x0000000f }, + { RT2573_MAC_CSR6, 0x00000fff }, + { RT2573_MAC_CSR8, 0x016c030a }, + { RT2573_MAC_CSR10, 0x00000718 }, + { RT2573_MAC_CSR12, 0x00000004 }, + { RT2573_MAC_CSR13, 0x00007f00 }, + { RT2573_SEC_CSR0, 0x00000000 }, + { RT2573_SEC_CSR1, 0x00000000 }, + { RT2573_SEC_CSR5, 0x00000000 }, + { RT2573_PHY_CSR1, 0x000023b0 }, + { RT2573_PHY_CSR5, 0x00040a06 }, + { RT2573_PHY_CSR6, 0x00080606 }, + { RT2573_PHY_CSR7, 0x00000408 }, + { RT2573_AIFSN_CSR, 0x00002273 }, + { RT2573_CWMIN_CSR, 0x00002344 }, + { RT2573_CWMAX_CSR, 0x000034aa } +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rum_def_bbp[] = { + { 3, 0x80 }, + { 15, 0x30 }, + { 17, 0x20 }, + { 21, 0xc8 }, + { 22, 0x38 }, + { 23, 0x06 }, + { 24, 0xfe }, + { 25, 0x0a }, + { 26, 0x0d }, + { 32, 0x0b }, + { 34, 0x12 }, + { 37, 0x07 }, + { 39, 0xf8 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 60, 0x10 }, + { 61, 0x04 }, + { 62, 0x04 }, + { 75, 0xfe }, + { 86, 0xfe }, + { 88, 0xfe }, + { 90, 0x0f }, + { 99, 0x00 }, + { 102, 0x16 }, + { 107, 0x04 } +}; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rum_rf5226[] = { + { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, + { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, + { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, + { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, + { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, + { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, + { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, + { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, + { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, + { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, + { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, + { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, + { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, + { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, + + { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, + { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, + { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, + { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, + + { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, + { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, + { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, + { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, + { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, + { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, + { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, + { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, + + { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, + { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, + { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, + { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, + { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, + { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, + { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, + { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, + { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, + { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, + { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, + + { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, + { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, + { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, + { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, + { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } +}, rum_rf5225[] = { + { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, + { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, + { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, + { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, + { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, + { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, + { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, + { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, + { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, + { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, + { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, + { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, + { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, + { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, + + { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, + { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, + { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, + { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, + + { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, + { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, + { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, + { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, + { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, + { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, + { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, + { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, + + { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, + { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, + { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, + { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, + { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, + { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, + { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, + { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, + { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, + { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, + { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, + + { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, + { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, + { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, + { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, + { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } +}; + +static const struct usb2_config rum_config[RUM_N_TRANSFER] = { + [RUM_BULK_WR] = { + .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 */ + }, + [RUM_BULK_RD] = { + .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, + }, +}; + +static int +rum_match(device_t self) +{ + struct usb2_attach_arg *uaa = device_get_ivars(self); + + 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 self) +{ + struct usb2_attach_arg *uaa = device_get_ivars(self); + struct rum_softc *sc = device_get_softc(self); + uint8_t iface_index; + int error; + + device_set_usb2_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), + MTX_NETWORK_LOCK, MTX_DEF); + + 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(self, "could not allocate USB transfers, " + "err=%s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, + device_get_nameunit(self), USB_PRI_MED); + if (error) { + device_printf(self, "could not setup config thread!\n"); + goto detach; + } + + /* fork rest of the attach code */ + RUM_LOCK(sc); + rum_queue_command(sc, rum_attach_post, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + RUM_UNLOCK(sc); + return (0); + +detach: + rum_detach(self); + return (ENXIO); /* failure */ +} + +static void +rum_attach_post(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp; + struct ieee80211com *ic; + unsigned int ntries; + int error; + uint32_t tmp; + uint8_t bands; + + /* retrieve RT2573 rev. no */ + for (ntries = 0; ntries < 100; ntries++) { + if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for chip to settle\n"); + return; + } + + /* retrieve MAC address and various other things from EEPROM */ + rum_read_eeprom(sc); + + device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", + tmp, rum_get_rf(sc->rf_rev)); + + error = rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); + if (error != 0) { + RUM_UNLOCK(sc); + device_printf(sc->sc_dev, "could not load 8051 microcode\n"); + return; + } + RUM_UNLOCK(sc); + + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(sc->sc_dev, "can not if_alloc()\n"); + RUM_LOCK(sc); + return; + } + ic = ifp->if_l2com; + + ifp->if_softc = sc; + if_initname(ifp, "rum", device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = rum_init; + ifp->if_ioctl = rum_ioctl; + ifp->if_start = rum_start; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic); + ic->ic_newassoc = rum_newassoc; + ic->ic_raw_xmit = rum_raw_xmit; + ic->ic_node_alloc = rum_node_alloc; + ic->ic_scan_start = rum_scan_start; + ic->ic_scan_end = rum_scan_end; + ic->ic_set_channel = rum_set_channel; + + ic->ic_vap_create = rum_vap_create; + ic->ic_vap_delete = rum_vap_delete; + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); + + sc->sc_rxtap_len = sizeof sc->sc_rxtap; + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof sc->sc_txtap; + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + RUM_LOCK(sc); +} + +static int +rum_detach(device_t self) +{ + struct rum_softc *sc = device_get_softc(self); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic; + + /* wait for any post attach or other command to complete */ + usb2_proc_drain(&sc->sc_tq); + + /* stop all USB transfers */ + usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); + usb2_proc_free(&sc->sc_tq); + + /* free TX list, if any */ + RUM_LOCK(sc); + rum_unsetup_tx_list(sc); + RUM_UNLOCK(sc); + + if (ifp) { + ic = ifp->if_l2com; + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +rum_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + struct rum_vap *rvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (rvp == NULL) + return NULL; + vap = &rvp->vap; + /* enable s/w bmiss handling for sta mode */ + ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); + + /* override state transition machine */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = rum_newstate; + + rvp->sc = sc; + usb2_callout_init_mtx(&rvp->amrr_ch, &sc->sc_mtx, 0); + ieee80211_amrr_init(&rvp->amrr, vap, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, + 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); + ic->ic_opmode = opmode; + return vap; +} + +static void +rum_vap_delete(struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + + usb2_callout_drain(&rvp->amrr_ch); + ieee80211_amrr_cleanup(&rvp->amrr); + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); +} + +static void +rum_tx_free(struct rum_tx_data *data, int txerr) +{ + struct rum_softc *sc = data->sc; + + if (data->m != NULL) { + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, + txerr ? ETIMEDOUT : 0); + m_freem(data->m); + data->m = NULL; + + ieee80211_free_node(data->ni); + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +rum_setup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +rum_unsetup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static void +rum_task(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct rum_vap *rvp = RUM_VAP(vap); + const struct ieee80211_txparam *tp; + enum ieee80211_state ostate; + struct ieee80211_node *ni; + uint32_t tmp; + + ostate = vap->iv_state; + + switch (sc->sc_state) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + tmp = rum_read(sc, RT2573_TXRX_CSR9); + rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); + } + break; + + case IEEE80211_S_RUN: + ni = vap->iv_bss; + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + rum_update_slot(ic->ic_ifp); + rum_enable_mrr(sc); + rum_set_txpreamble(sc); + rum_set_basicrates(sc); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + rum_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) + rum_prepare_beacon(sc, vap); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + rum_enable_tsf_sync(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + rum_amrr_start(sc, ni); + break; + default: + break; + } + + RUM_UNLOCK(sc); + IEEE80211_LOCK(ic); + rvp->newstate(vap, sc->sc_state, sc->sc_arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); + IEEE80211_UNLOCK(ic); + RUM_LOCK(sc); +} + +static int +rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF("%s -> %s\n", + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + RUM_LOCK(sc); + usb2_callout_stop(&rvp->amrr_ch); + + /* do it in a process context */ + sc->sc_state = nstate; + sc->sc_arg = arg; + RUM_UNLOCK(sc); + + if (nstate == IEEE80211_S_INIT) { + rvp->newstate(vap, nstate, arg); + return 0; + } else { + RUM_LOCK(sc); + rum_queue_command(sc, rum_task, &sc->sc_task[0].hdr, + &sc->sc_task[1].hdr); + RUM_UNLOCK(sc); + return EINPROGRESS; + } +} + +static void +rum_bulk_write_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_channel *c = ic->ic_curchan; + struct rum_tx_data *data; + struct mbuf *m; + unsigned int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); + + /* free resources */ + data = xfer->priv_fifo; + rum_tx_free(data, 0); + xfer->priv_fifo = NULL; + + ifp->if_opackets++; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->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_copy_in(xfer->frbuffers, 0, &data->desc, + RT2573_TX_DESC_SIZE); + usb2_m_copy_in(xfer->frbuffers, RT2573_TX_DESC_SIZE, m, + 0, m->m_pkthdr.len); + + if (bpf_peers_present(ifp->if_bpf)) { + struct rum_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_chan_freq = htole16(c->ic_freq); + tap->wt_chan_flags = htole16(c->ic_flags); + tap->wt_antenna = sc->tx_ant; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); + } + + /* align end on a 4-bytes boundary */ + len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3; + if ((len % 64) == 0) + len += 4; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + xfer->frlengths[0] = len; + xfer->priv_fifo = data; + + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + data = xfer->priv_fifo; + if (data != NULL) { + rum_tx_free(data, xfer->error); + xfer->priv_fifo = NULL; + } + + if (xfer->error == USB_ERR_STALLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + if (xfer->error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + break; + } +} + +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; + uint8_t rssi = 0; + unsigned int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); + + len = xfer->actlen; + if (len < RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + ifp->if_ierrors++; + goto tr_setup; + } + + len -= RT2573_RX_DESC_SIZE; + usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc, + RT2573_RX_DESC_SIZE); + + rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); + 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 RUM_TXRX_CSR2: + */ + DPRINTFN(5, "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; + } + usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, + mtod(m, uint8_t *), len); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; + + 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, + (flags & RT2573_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_antenna = sc->rx_ant; + tap->wr_antsignal = rssi; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + 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) { + RUM_UNLOCK(sc); + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, + RT2573_NOISE_FLOOR, 0); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, + RT2573_NOISE_FLOOR, 0); + RUM_LOCK(sc); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static uint8_t +rum_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +static void +rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, + uint32_t flags, uint16_t xflags, int len, int rate) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint16_t plcp_length; + int remainder; + + desc->flags = htole32(flags); + desc->flags |= htole32(RT2573_TX_VALID); + desc->flags |= htole32(len << 16); + + desc->xflags = htole16(xflags); + + desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | + RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); + + /* setup PLCP fields */ + desc->plcp_signal = rum_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { + desc->flags |= htole32(RT2573_TX_OFDM); + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + plcp_length = (16 * len + rate - 1) / rate; + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RT2573_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } +} + +#define RUM_TX_TIMEOUT 5000 + +static int +rum_sendprot(struct rum_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct rum_tx_data *data; + struct mbuf *mprot; + int protrate, ackrate, pktlen, flags, isshort; + uint16_t dur; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, + ("protection %d", prot)); + + wh = mtod(m, const struct ieee80211_frame *); + pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + + protrate = ieee80211_ctl_rate(sc->sc_rates, rate); + ackrate = ieee80211_ack_rate(sc->sc_rates, rate); + + isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; + dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); + + ieee80211_ack_duration(sc->sc_rates, rate, isshort); + flags = RT2573_TX_MORE_FRAG; + if (prot == IEEE80211_PROT_RTSCTS) { + /* NB: CTS is the same size as an ACK */ + dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); + flags |= RT2573_TX_NEED_ACK; + mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); + } else { + mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); + } + if (mprot == NULL) { + /* XXX stat + msg */ + return ENOBUFS; + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + rum_setup_tx_desc(sc, &data->desc, flags, 0, mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + wh = mtod(m0, struct ieee80211_frame *); + } + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RT2573_TX_NEED_ACK; + + dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + flags |= RT2573_TX_TIMESTAMP; + } + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct rum_tx_data *data; + uint32_t flags; + int rate, error; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(params != NULL, ("no raw xmit params")); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + rate = params->ibp_rate0 & IEEE80211_RATE_VAL; + /* XXX validate */ + if (rate == 0) { + m_freem(m0); + return EINVAL; + } + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RT2573_TX_NEED_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = rum_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error) { + m_freem(m0); + return error; + } + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + int error, rate; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m0, struct ieee80211_frame *); + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else + rate = ni->ni_txrate; + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = rum_sendprot(sc, m0, ni, prot, rate); + if (error) { + m_freem(m0); + return error; + } + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RT2573_TX_NEED_ACK; + flags |= RT2573_TX_MORE_FRAG; + + dur = ieee80211_ack_duration(sc->sc_rates, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + } + + rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static void +rum_start(struct ifnet *ifp) +{ + struct rum_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + struct mbuf *m; + + RUM_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + RUM_UNLOCK(sc); + return; + } + for (;;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + if (sc->tx_nfree == 0) { + IFQ_DRV_PREPEND(&ifp->if_snd, m); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + continue; + } + if (rum_tx_data(sc, m, ni) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + break; + } + } + RUM_UNLOCK(sc); +} + +static int +rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct rum_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + RUM_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + rum_queue_command(sc, rum_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + startall = 1; + } else + rum_queue_command(sc, rum_promisctask, + &sc->sc_promisctask[0].hdr, + &sc->sc_promisctask[1].hdr); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + rum_queue_command(sc, rum_stop_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + } + } + RUM_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); + break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; + default: + error = EINVAL; + break; + } + return error; +} + +static void +rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usb2_errstr(error)); + } +} + +static uint32_t +rum_read(struct rum_softc *sc, uint16_t reg) +{ + uint32_t val; + + rum_read_multi(sc, reg, &val, sizeof val); + + return le32toh(val); +} + +static void +rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi read MAC register: %s\n", + usb2_errstr(error)); + } +} + +static void +rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) +{ + uint32_t tmp = htole32(val); + + rum_write_multi(sc, reg, &tmp, sizeof tmp); +} + +static void +rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi write MAC register: %s\n", + usb2_errstr(error)); + } +} + +static void +rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; + rum_write(sc, RT2573_PHY_CSR3, tmp); +} + +static uint8_t +rum_bbp_read(struct rum_softc *sc, uint8_t reg) +{ + uint32_t val; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; + rum_write(sc, RT2573_PHY_CSR3, val); + + for (ntries = 0; ntries < 100; ntries++) { + val = rum_read(sc, RT2573_PHY_CSR3); + if (!(val & RT2573_BBP_BUSY)) + return val & 0xff; + if (rum_pause(sc, hz / 100)) + break; + } + + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; +} + +static void +rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | + (reg & 3); + rum_write(sc, RT2573_PHY_CSR4, tmp); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff); +} + +static void +rum_select_antenna(struct rum_softc *sc) +{ + uint8_t bbp4, bbp77; + uint32_t tmp; + + bbp4 = rum_bbp_read(sc, 4); + bbp77 = rum_bbp_read(sc, 77); + + /* TBD */ + + /* make sure Rx is disabled before switching antenna */ + tmp = rum_read(sc, RT2573_TXRX_CSR0); + rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + rum_bbp_write(sc, 4, bbp4); + rum_bbp_write(sc, 77, bbp77); + + rum_write(sc, RT2573_TXRX_CSR0, tmp); +} + +/* + * Enable multi-rate retries for frames sent at OFDM rates. + * In 802.11b/g mode, allow fallback to CCK rates. + */ +static void +rum_enable_mrr(struct rum_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + + tmp = rum_read(sc, RT2573_TXRX_CSR4); + + tmp &= ~RT2573_MRR_CCK_FALLBACK; + if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) + tmp |= RT2573_MRR_CCK_FALLBACK; + tmp |= RT2573_MRR_ENABLED; + + rum_write(sc, RT2573_TXRX_CSR4, tmp); +} + +static void +rum_set_txpreamble(struct rum_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + + tmp = rum_read(sc, RT2573_TXRX_CSR4); + + tmp &= ~RT2573_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2573_SHORT_PREAMBLE; + + rum_write(sc, RT2573_TXRX_CSR4, tmp); +} + +static void +rum_set_basicrates(struct rum_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + /* update basic rate set */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + /* 11b basic rates: 1, 2Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x3); + } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { + /* 11a basic rates: 6, 12, 24Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x150); + } else { + /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0xf); + } +} + +/* + * Reprogram MAC/BBP to switch to a new band. Values taken from the reference + * driver. + */ +static void +rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) +{ + uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; + uint32_t tmp; + + /* update all BBP registers that depend on the band */ + bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; + bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; + bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; + } + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; + } + + sc->bbp17 = bbp17; + rum_bbp_write(sc, 17, bbp17); + rum_bbp_write(sc, 96, bbp96); + rum_bbp_write(sc, 104, bbp104); + + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + rum_bbp_write(sc, 75, 0x80); + rum_bbp_write(sc, 86, 0x80); + rum_bbp_write(sc, 88, 0x80); + } + + rum_bbp_write(sc, 35, bbp35); + rum_bbp_write(sc, 97, bbp97); + rum_bbp_write(sc, 98, bbp98); + + tmp = rum_read(sc, RT2573_PHY_CSR0); + tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); + if (IEEE80211_IS_CHAN_2GHZ(c)) + tmp |= RT2573_PA_PE_2GHZ; + else + tmp |= RT2573_PA_PE_5GHZ; + rum_write(sc, RT2573_PHY_CSR0, tmp); +} + +static void +rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + const struct rfprog *rfprog; + uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; + int8_t power; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + /* select the appropriate RF settings based on what EEPROM says */ + rfprog = (sc->rf_rev == RT2573_RF_5225 || + sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + power = sc->txpow[i]; + if (power < 0) { + bbp94 += power; + power = 0; + } else if (power > 31) { + bbp94 += power - 31; + power = 31; + } + + /* + * If we are switching from the 2GHz band to the 5GHz band or + * vice-versa, BBP registers need to be reprogrammed. + */ + if (c->ic_flags != ic->ic_curchan->ic_flags) { + rum_select_band(sc, c); + rum_select_antenna(sc); + } + ic->ic_curchan = c; + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_pause(sc, hz / 100); + + /* enable smart mode for MIMO-capable RFs */ + bbp3 = rum_bbp_read(sc, 3); + + bbp3 &= ~RT2573_SMART_MODE; + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) + bbp3 |= RT2573_SMART_MODE; + + rum_bbp_write(sc, 3, bbp3); + + if (bbp94 != RT2573_BBPR94_DEFAULT) + rum_bbp_write(sc, 94, bbp94); +} + +/* + * Enable TSF synchronization and tell h/w to start sending beacons for IBSS + * and HostAP operating modes. + */ +static void +rum_enable_tsf_sync(struct rum_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + + if (vap->iv_opmode != IEEE80211_M_STA) { + /* + * Change default 16ms TBTT adjustment to 8ms. + * Must be done before enabling beacon generation. + */ + rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8); + } + + tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; + + /* set beacon interval (in 1/16ms unit) */ + tmp |= vap->iv_bss->ni_intval * 16; + + tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; + if (vap->iv_opmode == IEEE80211_M_STA) + tmp |= RT2573_TSF_MODE(1); + else + tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; + + rum_write(sc, RT2573_TXRX_CSR9, tmp); +} + +static void +rum_update_slot(struct ifnet *ifp) +{ + struct rum_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + uint8_t slottime; + uint32_t tmp; + + slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + + tmp = rum_read(sc, RT2573_MAC_CSR9); + tmp = (tmp & ~0xff) | slottime; + rum_write(sc, RT2573_MAC_CSR9, tmp); + + DPRINTF("setting slot time to %uus\n", slottime); +} + +static void +rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) +{ + uint32_t tmp; + + tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24; + rum_write(sc, RT2573_MAC_CSR4, tmp); + + tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16; + rum_write(sc, RT2573_MAC_CSR5, tmp); +} + +static void +rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) +{ + uint32_t tmp; + + tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24; + rum_write(sc, RT2573_MAC_CSR2, tmp); + + tmp = addr[4] | addr[5] << 8 | 0xff << 16; + rum_write(sc, RT2573_MAC_CSR3, tmp); +} + +static void +rum_promisctask(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + uint32_t tmp; + + tmp = rum_read(sc, RT2573_TXRX_CSR0); + + tmp &= ~RT2573_DROP_NOT_TO_ME; + if (!(ifp->if_flags & IFF_PROMISC)) + tmp |= RT2573_DROP_NOT_TO_ME; + + rum_write(sc, RT2573_TXRX_CSR0, tmp); + + DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? + "entering" : "leaving"); +} + +static const char * +rum_get_rf(int rev) +{ + switch (rev) { + case RT2573_RF_2527: return "RT2527 (MIMO XR)"; + case RT2573_RF_2528: return "RT2528"; + case RT2573_RF_5225: return "RT5225 (MIMO XR)"; + case RT2573_RF_5226: return "RT5226"; + default: return "unknown"; + } +} + +static void +rum_read_eeprom(struct rum_softc *sc) +{ + uint16_t val; +#ifdef RUM_DEBUG + int i; +#endif + + /* read MAC address */ + rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_bssid, 6); + + rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x1f; + sc->hw_radio = (val >> 10) & 0x1; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + DPRINTF("RF revision=%d\n", sc->rf_rev); + + rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); + val = le16toh(val); + sc->ext_5ghz_lna = (val >> 6) & 0x1; + sc->ext_2ghz_lna = (val >> 4) & 0x1; + + DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", + sc->ext_2ghz_lna, sc->ext_5ghz_lna); + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) + sc->rssi_2ghz_corr = 0; + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) + sc->rssi_5ghz_corr = 0; + + if (sc->ext_2ghz_lna) + sc->rssi_2ghz_corr -= 14; + if (sc->ext_5ghz_lna) + sc->rssi_5ghz_corr -= 14; + + DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", + sc->rssi_2ghz_corr, sc->rssi_5ghz_corr); + + rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rffreq = val & 0xff; + + DPRINTF("RF freq=%d\n", sc->rffreq); + + /* read Tx power for all a/b/g channels */ + rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); + /* XXX default Tx power for 802.11a channels */ + memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) + DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]); +#endif + + /* read default values for BBP registers */ + rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg, + sc->bbp_prom[i].val); + } +#endif +} + +static int +rum_bbp_init(struct rum_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + const uint8_t val = rum_bbp_read(sc, 0); + if (val != 0 && val != 0xff) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < N(rum_def_bbp); i++) + rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } + + return 0; +#undef N +} + +static void +rum_init_task(struct usb2_proc_msg *pm) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + usb2_error_t error; + int i, ntries; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + rum_stop_task(pm); + + /* initialize MAC registers to default values */ + for (i = 0; i < N(rum_def_mac); i++) + rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); + + /* set host ready */ + rum_write(sc, RT2573_MAC_CSR1, 3); + rum_write(sc, RT2573_MAC_CSR1, 0); + + /* wait for BBP/RF to wakeup */ + for (ntries = 0; ntries < 100; ntries++) { + if (rum_read(sc, RT2573_MAC_CSR12) & 8) + break; + rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + goto fail; + } + + if ((error = rum_bbp_init(sc)) != 0) + goto fail; + + /* select default channel */ + rum_select_band(sc, ic->ic_curchan); + rum_select_antenna(sc); + rum_set_chan(sc, ic->ic_curchan); + + /* clear STA registers */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + rum_set_macaddr(sc, ic->ic_myaddr); + + /* initialize ASIC */ + rum_write(sc, RT2573_MAC_CSR1, 4); + + /* + * Allocate Tx and Rx xfer queues. + */ + rum_setup_tx_list(sc); + + /* update Rx filter */ + tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; + + tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | + RT2573_DROP_ACKCTS; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RT2573_DROP_TODS; + if (!(ifp->if_flags & IFF_PROMISC)) + tmp |= RT2573_DROP_NOT_TO_ME; + } + rum_write(sc, RT2573_TXRX_CSR0, tmp); + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + usb2_transfer_set_stall(sc->sc_xfer[RUM_BULK_WR]); + usb2_transfer_start(sc->sc_xfer[RUM_BULK_RD]); + return; + +fail: rum_stop_task(pm); +#undef N +} + +static void +rum_init(void *priv) +{ + struct rum_softc *sc = priv; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + RUM_LOCK(sc); + rum_queue_command(sc, rum_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + RUM_UNLOCK(sc); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); /* start all vap's */ +} + +static void +rum_stop_task(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + uint32_t tmp; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + RUM_UNLOCK(sc); + + /* + * Drain the USB transfers, if not already drained: + */ + usb2_transfer_drain(sc->sc_xfer[RUM_BULK_WR]); + usb2_transfer_drain(sc->sc_xfer[RUM_BULK_RD]); + + RUM_LOCK(sc); + + rum_unsetup_tx_list(sc); + + /* disable Rx */ + tmp = rum_read(sc, RT2573_TXRX_CSR0); + rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + /* reset ASIC */ + rum_write(sc, RT2573_MAC_CSR1, 3); + rum_write(sc, RT2573_MAC_CSR1, 0); +} + +static int +rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size) +{ + struct usb2_device_request req; + uint16_t reg = RT2573_MCU_CODE_BASE; + usb2_error_t error; + + /* copy firmware image into NIC */ + for (; size >= 4; reg += 4, ucode += 4, size -= 4) + rum_write(sc, reg, UGETDW(ucode)); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_MCU_CNTL; + USETW(req.wValue, RT2573_MCU_RUN); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + error = rum_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not run firmware: %s\n", + usb2_errstr(error)); + } + return error; +} + +static int +rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_txparam *tp; + struct rum_tx_desc desc; + struct mbuf *m0; + + m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo); + if (m0 == NULL) { + return ENOBUFS; + } + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ, + m0->m_pkthdr.len, tp->mgmtrate); + + /* copy the first 24 bytes of Tx descriptor into NIC memory */ + rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24); + + /* copy beacon header and payload into NIC memory */ + rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *), + m0->m_pkthdr.len); + + m_freem(m0); + + return 0; +} + +static int +rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ifnet *ifp = ni->ni_ic->ic_ifp; + struct rum_softc *sc = ifp->if_softc; + + RUM_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + RUM_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return ENETDOWN; + } + if (sc->tx_nfree == 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + RUM_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return EIO; + } + + ifp->if_opackets++; + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if (rum_tx_mgt(sc, m, ni) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if (rum_tx_raw(sc, m, ni, params) != 0) + goto bad; + } + RUM_UNLOCK(sc); + + return 0; +bad: + ifp->if_oerrors++; + RUM_UNLOCK(sc); + ieee80211_free_node(ni); + return EIO; +} + +static void +rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct rum_vap *rvp = RUM_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni); + + usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); +} + +static void +rum_amrr_timeout(void *arg) +{ + struct rum_vap *rvp = arg; + struct rum_softc *sc = rvp->sc; + + rum_queue_command(sc, rum_amrr_task, + &rvp->amrr_task[0].hdr, &rvp->amrr_task[1].hdr); +} + +static void +rum_amrr_task(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211_node *ni = vap->iv_bss; + int ok, fail; + + /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta)); + + ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */ + (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */ + fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ + + ieee80211_amrr_tx_update(&RUM_NODE(ni)->amn, + ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail); + (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn); + + ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ + + usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); +} + +/* ARGUSED */ +static struct ieee80211_node * +rum_node_alloc(struct ieee80211vap *vap __unused, + const uint8_t mac[IEEE80211_ADDR_LEN] __unused) +{ + struct rum_node *rn; + + rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); + return rn != NULL ? &rn->ni : NULL; +} + +static void +rum_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); +} + +static void +rum_scan_start(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + RUM_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = RUM_SCAN_START; + rum_queue_command(sc, rum_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + RUM_UNLOCK(sc); + +} + +static void +rum_scan_end(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + RUM_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = RUM_SCAN_END; + rum_queue_command(sc, rum_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + RUM_UNLOCK(sc); + +} + +static void +rum_set_channel(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_ifp->if_softc; + + RUM_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = RUM_SET_CHANNEL; + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + rum_queue_command(sc, rum_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + RUM_UNLOCK(sc); +} + +static void +rum_scantask(struct usb2_proc_msg *pm) +{ + struct rum_task *task = (struct rum_task *)pm; + struct rum_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + switch (sc->sc_scan_action) { + case RUM_SCAN_START: + /* abort TSF synchronization */ + tmp = rum_read(sc, RT2573_TXRX_CSR9); + rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); + rum_set_bssid(sc, ifp->if_broadcastaddr); + break; + + case RUM_SET_CHANNEL: + rum_set_chan(sc, ic->ic_curchan); + break; + + default: /* RUM_SCAN_END */ + rum_enable_tsf_sync(sc); + rum_set_bssid(sc, sc->sc_bssid); + break; + } +} + +static int +rum_get_rssi(struct rum_softc *sc, uint8_t raw) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + int lna, agc, rssi; + + lna = (raw >> 5) & 0x3; + agc = raw & 0x1f; + + if (lna == 0) { + /* + * No RSSI mapping + * + * NB: Since RSSI is relative to noise floor, -1 is + * adequate for caller to know error happened. + */ + return -1; + } + + rssi = (2 * agc) - RT2573_NOISE_FLOOR; + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + rssi += sc->rssi_2ghz_corr; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 74; + else if (lna == 3) + rssi -= 90; + } else { + rssi += sc->rssi_5ghz_corr; + + if (!sc->ext_5ghz_lna && lna != 1) + rssi += 4; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 86; + else if (lna == 3) + rssi -= 100; + } + return rssi; +} + +static int +rum_pause(struct rum_softc *sc, int timeout) +{ + if (usb2_proc_is_gone(&sc->sc_tq)) + return (1); + + usb2_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} + +static void +rum_queue_command(struct rum_softc *sc, usb2_proc_callback_t *fn, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) +{ + struct rum_task *task; + + RUM_LOCK_ASSERT(sc, MA_OWNED); + + if (usb2_proc_is_gone(&sc->sc_tq)) { + DPRINTF("proc is gone\n"); + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct rum_task *) + usb2_proc_msignal(&sc->sc_tq, t0, t1); + + /* Setup callback and softc pointers */ + task->hdr.pm_callback = fn; + task->sc = sc; + + /* + * Init and stop must be synchronous! + */ + if ((fn == rum_init_task) || (fn == rum_stop_task)) + usb2_proc_mwait(&sc->sc_tq, t0, t1); +} + +static device_method_t rum_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rum_match), + DEVMETHOD(device_attach, rum_attach), + DEVMETHOD(device_detach, rum_detach), + + { 0, 0 } +}; + +static driver_t rum_driver = { + .name = "rum", + .methods = rum_methods, + .size = sizeof(struct rum_softc), +}; + +static devclass_t rum_devclass; + +DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0); diff --git a/sys/dev/usb/wlan/if_rumfw.h b/sys/dev/usb/wlan/if_rumfw.h new file mode 100644 index 0000000..0f08674 --- /dev/null +++ b/sys/dev/usb/wlan/if_rumfw.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/usb/wlan/if_rumreg.h b/sys/dev/usb/wlan/if_rumreg.h new file mode 100644 index 0000000..75a51bc --- /dev/null +++ b/sys/dev/usb/wlan/if_rumreg.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) + + 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/usb/wlan/if_rumvar.h b/sys/dev/usb/wlan/if_rumvar.h new file mode 100644 index 0000000..1b58dc4 --- /dev/null +++ b/sys/dev/usb/wlan/if_rumvar.h @@ -0,0 +1,156 @@ +/* $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_TX_LIST_COUNT 8 + +struct rum_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antenna; + uint8_t wr_antsignal; +}; + +#define RT2573_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) + +struct rum_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_antenna; +}; + +#define RT2573_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct rum_softc; + +struct rum_task { + struct usb2_proc_msg hdr; + struct rum_softc *sc; +}; + +struct rum_tx_data { + STAILQ_ENTRY(rum_tx_data) next; + struct rum_softc *sc; + struct rum_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead; + +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 rum_softc *sc; + struct ieee80211_beacon_offsets bo; + struct ieee80211_amrr amrr; + struct usb2_callout amrr_ch; + struct rum_task amrr_task[2]; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define RUM_VAP(vap) ((struct rum_vap *)(vap)) + +enum { + RUM_BULK_WR, + RUM_BULK_RD, + RUM_N_TRANSFER = 2, +}; + +struct rum_softc { + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_process sc_tq; + + const struct ieee80211_rate_table *sc_rates; + struct usb2_xfer *sc_xfer[RUM_N_TRANSFER]; + + uint8_t rf_rev; + uint8_t rffreq; + + enum ieee80211_state sc_state; + int sc_arg; + struct rum_task sc_synctask[2]; + struct rum_task sc_task[2]; + struct rum_task sc_promisctask[2]; + struct rum_task sc_scantask[2]; + int sc_scan_action; +#define RUM_SCAN_START 0 +#define RUM_SCAN_END 1 +#define RUM_SET_CHANNEL 2 + + struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; + rum_txdhead tx_q; + rum_txdhead tx_free; + int tx_nfree; + struct rum_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + uint32_t sta[6]; + uint32_t rf_regs[4]; + uint8_t txpow[44]; + uint8_t sc_bssid[6]; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + int ext_2ghz_lna; + int ext_5ghz_lna; + int rssi_2ghz_corr; + int rssi_5ghz_corr; + uint8_t bbp17; + + struct rum_rx_radiotap_header sc_rxtap; + int sc_rxtap_len; + + struct rum_tx_radiotap_header sc_txtap; + int sc_txtap_len; +}; + +#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RUM_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/sys/dev/usb/wlan/if_ural.c b/sys/dev/usb/wlan/if_ural.c new file mode 100644 index 0000000..aebffaa --- /dev/null +++ b/sys/dev/usb/wlan/if_ural.c @@ -0,0 +1,2364 @@ +/* $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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2500USB chipset driver + * http://www.ralinktech.com/ + */ + +#include "usbdevs.h" +#include +#include +#include + +#define USB_DEBUG_VAR ural_debug + +#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_do_request(sc,req,data) \ + usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) + +#define URAL_RSSI(rssi) \ + ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ + ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) + +/* various supported device vendors/products */ +static const struct usb2_device_id ural_devs[] = { + { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G) }, + { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570) }, + { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050) }, + { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051) }, + { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS) }, + { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G) }, + { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP) }, + { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU) }, + { USB_VP(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122) }, + { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G) }, + { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG) }, + { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB) }, + { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2) }, + { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3) }, + { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2) }, + { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3) }, + { USB_VP(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G) }, + { USB_VP(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG) }, + { USB_VP(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R) }, + { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570) }, + { USB_VP(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570) }, + { USB_VP(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570) }, +}; + +static usb2_callback_t ural_bulk_read_callback; +static usb2_callback_t ural_bulk_write_callback; + +static usb2_proc_callback_t ural_attach_post; +static usb2_proc_callback_t ural_task; +static usb2_proc_callback_t ural_scantask; +static usb2_proc_callback_t ural_promisctask; +static usb2_proc_callback_t ural_amrr_task; +static usb2_proc_callback_t ural_init_task; +static usb2_proc_callback_t ural_stop_task; + +static struct ieee80211vap *ural_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void ural_vap_delete(struct ieee80211vap *); +static void ural_tx_free(struct ural_tx_data *, int); +static void ural_setup_tx_list(struct ural_softc *); +static void ural_unsetup_tx_list(struct ural_softc *); +static int ural_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void ural_setup_tx_desc(struct ural_softc *, + struct ural_tx_desc *, uint32_t, int, int); +static int ural_tx_bcn(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_mgt(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_data(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static void ural_start(struct ifnet *); +static int ural_ioctl(struct ifnet *, u_long, caddr_t); +static void ural_set_testmode(struct ural_softc *); +static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, + int); +static uint16_t ural_read(struct ural_softc *, uint16_t); +static void ural_read_multi(struct ural_softc *, uint16_t, void *, + int); +static void ural_write(struct ural_softc *, uint16_t, uint16_t); +static void ural_write_multi(struct ural_softc *, uint16_t, void *, + int) __unused; +static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); +static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); +static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); +static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void ural_newassoc(struct ieee80211_node *, int); +static void ural_scan_start(struct ieee80211com *); +static void ural_scan_end(struct ieee80211com *); +static void ural_set_channel(struct ieee80211com *); +static void ural_set_chan(struct ural_softc *, + struct ieee80211_channel *); +static void ural_disable_rf_tune(struct ural_softc *); +static void ural_enable_tsf_sync(struct ural_softc *); +static void ural_update_slot(struct ifnet *); +static void ural_set_txpreamble(struct ural_softc *); +static void ural_set_basicrates(struct ural_softc *, + const struct ieee80211_channel *); +static void ural_set_bssid(struct ural_softc *, const uint8_t *); +static void ural_set_macaddr(struct ural_softc *, uint8_t *); +static const char *ural_get_rf(int); +static void ural_read_eeprom(struct ural_softc *); +static int ural_bbp_init(struct ural_softc *); +static void ural_set_txantenna(struct ural_softc *, int); +static void ural_set_rxantenna(struct ural_softc *, int); +static void ural_init(void *); +static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void ural_amrr_start(struct ural_softc *, + struct ieee80211_node *); +static void ural_amrr_timeout(void *); +static int ural_pause(struct ural_softc *sc, int timeout); +static void ural_queue_command(struct ural_softc *, + usb2_proc_callback_t *, struct usb2_proc_msg *, + struct usb2_proc_msg *); + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +static const struct { + uint16_t reg; + uint16_t val; +} ural_def_mac[] = { + { RAL_TXRX_CSR5, 0x8c8d }, + { RAL_TXRX_CSR6, 0x8b8a }, + { RAL_TXRX_CSR7, 0x8687 }, + { RAL_TXRX_CSR8, 0x0085 }, + { RAL_MAC_CSR13, 0x1111 }, + { RAL_MAC_CSR14, 0x1e11 }, + { RAL_TXRX_CSR21, 0xe78f }, + { RAL_MAC_CSR9, 0xff1d }, + { RAL_MAC_CSR11, 0x0002 }, + { RAL_MAC_CSR22, 0x0053 }, + { RAL_MAC_CSR15, 0x0000 }, + { RAL_MAC_CSR8, 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. + */ +static const struct { + uint8_t reg; + uint8_t val; +} ural_def_bbp[] = { + { 3, 0x02 }, + { 4, 0x19 }, + { 14, 0x1c }, + { 15, 0x30 }, + { 16, 0xac }, + { 17, 0x48 }, + { 18, 0x18 }, + { 19, 0xff }, + { 20, 0x1e }, + { 21, 0x08 }, + { 22, 0x08 }, + { 23, 0x08 }, + { 24, 0x80 }, + { 25, 0x50 }, + { 26, 0x08 }, + { 27, 0x23 }, + { 30, 0x10 }, + { 31, 0x2b }, + { 32, 0xb9 }, + { 34, 0x12 }, + { 35, 0x50 }, + { 39, 0xc4 }, + { 40, 0x02 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 56, 0x08 }, + { 57, 0x10 }, + { 58, 0x08 }, + { 61, 0x60 }, + { 62, 0x10 }, + { 75, 0xff } +}; + +/* + * Default values for RF register R2 indexed by channel numbers. + */ +static const uint32_t ural_rf2522_r2[] = { + 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, + 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e +}; + +static const uint32_t ural_rf2523_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2524_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2525_r2[] = { + 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, + 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 +}; + +static const uint32_t ural_rf2525_hi_r2[] = { + 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, + 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e +}; + +static const uint32_t ural_rf2525e_r2[] = { + 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, + 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b +}; + +static const uint32_t ural_rf2526_hi_r2[] = { + 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, + 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 +}; + +static const uint32_t ural_rf2526_r2[] = { + 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, + 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d +}; + +/* + * For dual-band RF, RF registers R1 and R4 also depend on channel number; + * values taken from the reference driver. + */ +static const struct { + uint8_t chan; + uint32_t r1; + uint32_t r2; + uint32_t r4; +} ural_rf5222[] = { + { 1, 0x08808, 0x0044d, 0x00282 }, + { 2, 0x08808, 0x0044e, 0x00282 }, + { 3, 0x08808, 0x0044f, 0x00282 }, + { 4, 0x08808, 0x00460, 0x00282 }, + { 5, 0x08808, 0x00461, 0x00282 }, + { 6, 0x08808, 0x00462, 0x00282 }, + { 7, 0x08808, 0x00463, 0x00282 }, + { 8, 0x08808, 0x00464, 0x00282 }, + { 9, 0x08808, 0x00465, 0x00282 }, + { 10, 0x08808, 0x00466, 0x00282 }, + { 11, 0x08808, 0x00467, 0x00282 }, + { 12, 0x08808, 0x00468, 0x00282 }, + { 13, 0x08808, 0x00469, 0x00282 }, + { 14, 0x08808, 0x0046b, 0x00286 }, + + { 36, 0x08804, 0x06225, 0x00287 }, + { 40, 0x08804, 0x06226, 0x00287 }, + { 44, 0x08804, 0x06227, 0x00287 }, + { 48, 0x08804, 0x06228, 0x00287 }, + { 52, 0x08804, 0x06229, 0x00287 }, + { 56, 0x08804, 0x0622a, 0x00287 }, + { 60, 0x08804, 0x0622b, 0x00287 }, + { 64, 0x08804, 0x0622c, 0x00287 }, + + { 100, 0x08804, 0x02200, 0x00283 }, + { 104, 0x08804, 0x02201, 0x00283 }, + { 108, 0x08804, 0x02202, 0x00283 }, + { 112, 0x08804, 0x02203, 0x00283 }, + { 116, 0x08804, 0x02204, 0x00283 }, + { 120, 0x08804, 0x02205, 0x00283 }, + { 124, 0x08804, 0x02206, 0x00283 }, + { 128, 0x08804, 0x02207, 0x00283 }, + { 132, 0x08804, 0x02208, 0x00283 }, + { 136, 0x08804, 0x02209, 0x00283 }, + { 140, 0x08804, 0x0220a, 0x00283 }, + + { 149, 0x08808, 0x02429, 0x00281 }, + { 153, 0x08808, 0x0242b, 0x00281 }, + { 157, 0x08808, 0x0242d, 0x00281 }, + { 161, 0x08808, 0x0242f, 0x00281 } +}; + +static const struct usb2_config ural_config[URAL_N_TRANSFER] = { + [URAL_BULK_WR] = { + .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 */ + }, + [URAL_BULK_RD] = { + .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, + }, +}; + +static device_probe_t ural_match; +static device_attach_t ural_attach; +static device_detach_t ural_detach; + +static device_method_t ural_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ural_match), + DEVMETHOD(device_attach, ural_attach), + DEVMETHOD(device_detach, ural_detach), + + { 0, 0 } +}; + +static driver_t ural_driver = { + .name = "ural", + .methods = ural_methods, + .size = sizeof(struct ural_softc), +}; + +static devclass_t ural_devclass; + +DRIVER_MODULE(ural, ushub, ural_driver, ural_devclass, NULL, 0); +MODULE_DEPEND(ural, usb, 1, 1, 1); +MODULE_DEPEND(ural, wlan, 1, 1, 1); +MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); + +static int +ural_match(device_t self) +{ + struct usb2_attach_arg *uaa = device_get_ivars(self); + + 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 self) +{ + struct usb2_attach_arg *uaa = device_get_ivars(self); + struct ural_softc *sc = device_get_softc(self); + int error; + uint8_t iface_index; + + device_set_usb2_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), + MTX_NETWORK_LOCK, MTX_DEF); + + 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(self, "could not allocate USB transfers, " + "err=%s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, + device_get_nameunit(self), USB_PRI_MED); + if (error) { + device_printf(self, "could not setup config thread!\n"); + goto detach; + } + + /* fork rest of the attach code */ + RAL_LOCK(sc); + ural_queue_command(sc, ural_attach_post, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + RAL_UNLOCK(sc); + return (0); + +detach: + ural_detach(self); + return (ENXIO); /* failure */ +} + +static void +ural_attach_post(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp; + struct ieee80211com *ic; + uint8_t bands; + + /* retrieve RT2570 rev. no */ + sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); + + /* retrieve MAC address and various other things from EEPROM */ + ural_read_eeprom(sc); + RAL_UNLOCK(sc); + + device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", + sc->asic_rev, ural_get_rf(sc->rf_rev)); + + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(sc->sc_dev, "can not if_alloc()\n"); + RAL_LOCK(sc); + return; + } + ic = ifp->if_l2com; + + ifp->if_softc = sc; + if_initname(ifp, "ural", device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = ural_init; + ifp->if_ioctl = ural_ioctl; + ifp->if_start = ural_start; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RAL_RF_5222) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic); + ic->ic_newassoc = ural_newassoc; + ic->ic_raw_xmit = ural_raw_xmit; + ic->ic_node_alloc = ural_node_alloc; + ic->ic_scan_start = ural_scan_start; + ic->ic_scan_end = ural_scan_end; + ic->ic_set_channel = ural_set_channel; + + ic->ic_vap_create = ural_vap_create; + ic->ic_vap_delete = ural_vap_delete; + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); + + sc->sc_rxtap_len = sizeof sc->sc_rxtap; + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof sc->sc_txtap; + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + RAL_LOCK(sc); +} + +static int +ural_detach(device_t self) +{ + struct ural_softc *sc = device_get_softc(self); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic; + + /* wait for any post attach or other command to complete */ + usb2_proc_drain(&sc->sc_tq); + + /* stop all USB transfers */ + usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); + usb2_proc_free(&sc->sc_tq); + + /* free TX list, if any */ + RAL_LOCK(sc); + ural_unsetup_tx_list(sc); + RAL_UNLOCK(sc); + + if (ifp) { + ic = ifp->if_l2com; + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +ural_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + struct ural_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (uvp == NULL) + return NULL; + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = ural_newstate; + + uvp->sc = sc; + usb2_callout_init_mtx(&uvp->amrr_ch, &sc->sc_mtx, 0); + ieee80211_amrr_init(&uvp->amrr, vap, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, + 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); + ic->ic_opmode = opmode; + return vap; +} + +static void +ural_vap_delete(struct ieee80211vap *vap) +{ + struct ural_vap *uvp = URAL_VAP(vap); + + usb2_callout_drain(&uvp->amrr_ch); + ieee80211_amrr_cleanup(&uvp->amrr); + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +ural_tx_free(struct ural_tx_data *data, int txerr) +{ + struct ural_softc *sc = data->sc; + + if (data->m != NULL) { + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, + txerr ? ETIMEDOUT : 0); + m_freem(data->m); + data->m = NULL; + + ieee80211_free_node(data->ni); + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +ural_setup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +ural_unsetup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static void +ural_task(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ural_vap *uvp = URAL_VAP(vap); + const struct ieee80211_txparam *tp; + enum ieee80211_state ostate; + struct ieee80211_node *ni; + struct mbuf *m; + + ostate = vap->iv_state; + + switch (sc->sc_state) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + /* force tx led to stop blinking */ + ural_write(sc, RAL_MAC_CSR20, 0); + } + break; + + case IEEE80211_S_RUN: + ni = vap->iv_bss; + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + ural_update_slot(ic->ic_ifp); + ural_set_txpreamble(sc); + ural_set_basicrates(sc, ic->ic_bsschan); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + ural_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + m = ieee80211_beacon_alloc(ni, &uvp->bo); + if (m == NULL) { + device_printf(sc->sc_dev, + "could not allocate beacon\n"); + return; + } + + if (ural_tx_bcn(sc, m, ni) != 0) { + device_printf(sc->sc_dev, + "could not send beacon\n"); + return; + } + } + + /* make tx led blink on tx (controlled by ASIC) */ + ural_write(sc, RAL_MAC_CSR20, 1); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + ural_enable_tsf_sync(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + ural_amrr_start(sc, ni); + + break; + + default: + break; + } + + RAL_UNLOCK(sc); + IEEE80211_LOCK(ic); + uvp->newstate(vap, sc->sc_state, sc->sc_arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); + IEEE80211_UNLOCK(ic); + RAL_LOCK(sc); +} + +static void +ural_scantask(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + switch (sc->sc_scan_action) { + case URAL_SCAN_START: + /* abort TSF synchronization */ + DPRINTF("starting scan\n"); + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_set_bssid(sc, ifp->if_broadcastaddr); + break; + + case URAL_SET_CHANNEL: + ural_set_chan(sc, ic->ic_curchan); + break; + + default: /* URAL_SCAN_END */ + DPRINTF("stopping scan\n"); + ural_enable_tsf_sync(sc); + ural_set_bssid(sc, sc->sc_bssid); + break; + } +} + +static int +ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ural_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF("%s -> %s\n", + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + RAL_LOCK(sc); + usb2_callout_stop(&uvp->amrr_ch); + + /* do it in a process context */ + sc->sc_state = nstate; + sc->sc_arg = arg; + RAL_UNLOCK(sc); + + if (nstate == IEEE80211_S_INIT) { + uvp->newstate(vap, nstate, arg); + return 0; + } else { + RAL_LOCK(sc); + ural_queue_command(sc, ural_task, &sc->sc_task[0].hdr, + &sc->sc_task[1].hdr); + RAL_UNLOCK(sc); + return EINPROGRESS; + } +} + + +static void +ural_bulk_write_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_channel *c = ic->ic_curchan; + struct ural_tx_data *data; + struct mbuf *m; + unsigned int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); + + /* free resources */ + data = xfer->priv_fifo; + ural_tx_free(data, 0); + xfer->priv_fifo = NULL; + + ifp->if_opackets++; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->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_copy_in(xfer->frbuffers, 0, &data->desc, + RAL_TX_DESC_SIZE); + usb2_m_copy_in(xfer->frbuffers, RAL_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + if (bpf_peers_present(ifp->if_bpf)) { + struct ural_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_chan_freq = htole16(c->ic_freq); + tap->wt_chan_flags = htole16(c->ic_flags); + tap->wt_antenna = sc->tx_ant; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); + } + + /* xfer length needs to be a multiple of two! */ + len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; + if ((len % 64) == 0) + len += 2; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + xfer->frlengths[0] = len; + xfer->priv_fifo = data; + + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + data = xfer->priv_fifo; + if (data != NULL) { + ural_tx_free(data, xfer->error); + xfer->priv_fifo = NULL; + } + + if (xfer->error == USB_ERR_STALLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + if (xfer->error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + break; + } +} + +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; + uint8_t rssi = 0; + unsigned int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); + + len = xfer->actlen; + if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + ifp->if_ierrors++; + goto tr_setup; + } + + len -= RAL_RX_DESC_SIZE; + /* rx descriptor is located at the end */ + usb2_copy_out(xfer->frbuffers, len, &sc->sc_rx_desc, + RAL_RX_DESC_SIZE); + + rssi = URAL_RSSI(sc->sc_rx_desc.rssi); + 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(5, "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; + } + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; + + if (bpf_peers_present(ifp->if_bpf)) { + struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; + tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, + (flags & RAL_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_antenna = sc->rx_ant; + tap->wr_antsignal = rssi; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + /* Strip trailing 802.11 MAC FCS. */ + m_adj(m, -IEEE80211_CRC_LEN); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + 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) { + RAL_UNLOCK(sc); + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, + RAL_NOISE_FLOOR, 0); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, + RAL_NOISE_FLOOR, 0); + RAL_LOCK(sc); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static uint8_t +ural_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +static void +ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, + uint32_t flags, int len, int rate) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint16_t plcp_length; + int remainder; + + desc->flags = htole32(flags); + desc->flags |= htole32(RAL_TX_NEWSEQ); + desc->flags |= htole32(len << 16); + + desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); + desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); + + /* setup PLCP fields */ + desc->plcp_signal = ural_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { + desc->flags |= htole32(RAL_TX_OFDM); + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + plcp_length = (16 * len + rate - 1) / rate; + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RAL_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } + + desc->iv = 0; + desc->eiv = 0; +} + +#define RAL_TX_TIMEOUT 5000 + +static int +ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = sc->sc_ifp; + const struct ieee80211_txparam *tp; + struct ural_tx_data *data; + + if (sc->tx_nfree == 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + m_freem(m0); + ieee80211_free_node(ni); + return EIO; + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + ural_setup_tx_desc(sc, &data->desc, + RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, + tp->mgmtrate); + + DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return (0); +} + +static int +ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags; + uint16_t dur; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + wh = mtod(m0, struct ieee80211_frame *); + } + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + flags = 0; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + + dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= RAL_TX_TIMESTAMP; + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_sendprot(struct ural_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct ural_tx_data *data; + struct mbuf *mprot; + int protrate, ackrate, pktlen, flags, isshort; + uint16_t dur; + + KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, + ("protection %d", prot)); + + wh = mtod(m, const struct ieee80211_frame *); + pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + + protrate = ieee80211_ctl_rate(sc->sc_rates, rate); + ackrate = ieee80211_ack_rate(sc->sc_rates, rate); + + isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; + dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); + + ieee80211_ack_duration(sc->sc_rates, rate, isshort); + flags = RAL_TX_RETRY(7); + if (prot == IEEE80211_PROT_RTSCTS) { + /* NB: CTS is the same size as an ACK */ + dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); + flags |= RAL_TX_ACK; + mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); + } else { + mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); + } + if (mprot == NULL) { + /* XXX stat + msg */ + return ENOBUFS; + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ural_tx_data *data; + uint32_t flags; + int error; + int rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(params != NULL, ("no raw xmit params")); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + rate = params->ibp_rate0 & IEEE80211_RATE_VAL; + /* XXX validate */ + if (rate == 0) { + m_freem(m0); + return EINVAL; + } + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RAL_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = ural_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error) { + m_freem(m0); + return error; + } + flags |= RAL_TX_IFS_SIFS; + } + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + int error, rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m0, struct ieee80211_frame *); + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else + rate = ni->ni_txrate; + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = ural_sendprot(sc, m0, ni, prot, rate); + if (error) { + m_freem(m0); + return error; + } + flags |= RAL_TX_IFS_SIFS; + } + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + flags |= RAL_TX_RETRY(7); + + dur = ieee80211_ack_duration(sc->sc_rates, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending data frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static void +ural_start(struct ifnet *ifp) +{ + struct ural_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + struct mbuf *m; + + RAL_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + RAL_UNLOCK(sc); + return; + } + for (;;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + if (sc->tx_nfree == 0) { + IFQ_DRV_PREPEND(&ifp->if_snd, m); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + continue; + } + if (ural_tx_data(sc, m, ni) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + break; + } + } + RAL_UNLOCK(sc); +} + +static int +ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ural_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + RAL_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ural_queue_command(sc, ural_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + startall = 1; + } else + ural_queue_command(sc, ural_promisctask, + &sc->sc_promisctask[0].hdr, + &sc->sc_promisctask[1].hdr); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ural_queue_command(sc, ural_stop_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + } + } + RAL_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return error; +} + +static void +ural_set_testmode(struct ural_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_VENDOR_REQUEST; + USETW(req.wValue, 4); + USETW(req.wIndex, 1); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not set test mode: %s\n", + usb2_errstr(error)); + } +} + +static void +ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usb2_errstr(error)); + } +} + +static uint16_t +ural_read(struct ural_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + usb2_error_t error; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, sizeof (uint16_t)); + + error = ural_do_request(sc, &req, &val); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usb2_errstr(error)); + return 0; + } + + return le16toh(val); +} + +static void +ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usb2_errstr(error)); + } +} + +static void +ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MAC; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usb2_errstr(error)); + } +} + +static void +ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usb2_errstr(error)); + } +} + +static void +ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) +{ + uint16_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = reg << 8 | val; + ural_write(sc, RAL_PHY_CSR7, tmp); +} + +static uint8_t +ural_bbp_read(struct ural_softc *sc, uint8_t reg) +{ + uint16_t val; + int ntries; + + val = RAL_BBP_WRITE | reg << 8; + ural_write(sc, RAL_PHY_CSR7, val); + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + return ural_read(sc, RAL_PHY_CSR7) & 0xff; +} + +static void +ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); + ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); + ural_write(sc, RAL_PHY_CSR10, tmp >> 16); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); +} + +/* ARGUSED */ +static struct ieee80211_node * +ural_node_alloc(struct ieee80211vap *vap __unused, + const uint8_t mac[IEEE80211_ADDR_LEN] __unused) +{ + struct ural_node *un; + + un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); + return un != NULL ? &un->ni : NULL; +} + +static void +ural_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); +} + +static void +ural_scan_start(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + RAL_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = URAL_SCAN_START; + ural_queue_command(sc, ural_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + RAL_UNLOCK(sc); + +} + +static void +ural_scan_end(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + RAL_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = URAL_SCAN_END; + ural_queue_command(sc, ural_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + RAL_UNLOCK(sc); + +} + +static void +ural_set_channel(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_ifp->if_softc; + + RAL_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = URAL_SET_CHANNEL; + ural_queue_command(sc, ural_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + + sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); + RAL_UNLOCK(sc); +} + +static void +ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint8_t power, tmp; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + if (IEEE80211_IS_CHAN_2GHZ(c)) + power = min(sc->txpow[chan - 1], 31); + else + power = 31; + + /* adjust txpower using ifconfig settings */ + power -= (100 - ic->ic_txpowlimit) / 8; + + DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); + + switch (sc->rf_rev) { + case RAL_RF_2522: + ural_rf_write(sc, RAL_RF1, 0x00814); + ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + break; + + case RAL_RF_2523: + ural_rf_write(sc, RAL_RF1, 0x08804); + ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2524: + ural_rf_write(sc, RAL_RF1, 0x0c808); + ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525E: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); + break; + + case RAL_RF_2526: + ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + ural_rf_write(sc, RAL_RF1, 0x08804); + + ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + break; + + /* dual-band RF */ + case RAL_RF_5222: + for (i = 0; ural_rf5222[i].chan != chan; i++); + + ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); + ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); + break; + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* set Japan filter bit for channel 14 */ + tmp = ural_bbp_read(sc, 70); + + tmp &= ~RAL_JAPAN_FILTER; + if (chan == 14) + tmp |= RAL_JAPAN_FILTER; + + ural_bbp_write(sc, 70, tmp); + + /* clear CRC errors */ + ural_read(sc, RAL_STA_CSR0); + + ural_pause(sc, hz / 100); + ural_disable_rf_tune(sc); + } + + /* XXX doesn't belong here */ + /* update basic rate set */ + ural_set_basicrates(sc, c); +} + +/* + * Disable RF auto-tuning. + */ +static void +ural_disable_rf_tune(struct ural_softc *sc) +{ + uint32_t tmp; + + if (sc->rf_rev != RAL_RF_2523) { + tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; + ural_rf_write(sc, RAL_RF1, tmp); + } + + tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; + ural_rf_write(sc, RAL_RF3, tmp); + + DPRINTFN(2, "disabling RF autotune\n"); +} + +/* + * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF + * synchronization. + */ +static void +ural_enable_tsf_sync(struct ural_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t logcwmin, preload, tmp; + + /* first, disable TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + tmp = (16 * vap->iv_bss->ni_intval) << 4; + ural_write(sc, RAL_TXRX_CSR18, tmp); + + logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; + preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; + tmp = logcwmin << 12 | preload; + ural_write(sc, RAL_TXRX_CSR20, tmp); + + /* finally, enable TSF synchronization */ + tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RAL_ENABLE_TSF_SYNC(1); + else + tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; + ural_write(sc, RAL_TXRX_CSR19, tmp); + + DPRINTF("enabling TSF synchronization\n"); +} + +#define RAL_RXTX_TURNAROUND 5 /* us */ +static void +ural_update_slot(struct ifnet *ifp) +{ + struct ural_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + uint16_t slottime, sifs, eifs; + + slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + + /* + * These settings may sound a bit inconsistent but this is what the + * reference driver does. + */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + sifs = 16 - RAL_RXTX_TURNAROUND; + eifs = 364; + } else { + sifs = 10 - RAL_RXTX_TURNAROUND; + eifs = 64; + } + + ural_write(sc, RAL_MAC_CSR10, slottime); + ural_write(sc, RAL_MAC_CSR11, sifs); + ural_write(sc, RAL_MAC_CSR12, eifs); +} + +static void +ural_set_txpreamble(struct ural_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint16_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR10); + + tmp &= ~RAL_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RAL_SHORT_PREAMBLE; + + ural_write(sc, RAL_TXRX_CSR10, tmp); +} + +static void +ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) +{ + /* XXX wrong, take from rate set */ + /* update basic rate set */ + if (IEEE80211_IS_CHAN_5GHZ(c)) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x150); + } else if (IEEE80211_IS_CHAN_ANYG(c)) { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + } else { + /* 11b basic rates: 1, 2Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x3); + } +} + +static void +ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) +{ + uint16_t tmp; + + tmp = bssid[0] | bssid[1] << 8; + ural_write(sc, RAL_MAC_CSR5, tmp); + + tmp = bssid[2] | bssid[3] << 8; + ural_write(sc, RAL_MAC_CSR6, tmp); + + tmp = bssid[4] | bssid[5] << 8; + ural_write(sc, RAL_MAC_CSR7, tmp); + + DPRINTF("setting BSSID to %6D\n", bssid, ":"); +} + +static void +ural_set_macaddr(struct ural_softc *sc, uint8_t *addr) +{ + uint16_t tmp; + + tmp = addr[0] | addr[1] << 8; + ural_write(sc, RAL_MAC_CSR2, tmp); + + tmp = addr[2] | addr[3] << 8; + ural_write(sc, RAL_MAC_CSR3, tmp); + + tmp = addr[4] | addr[5] << 8; + ural_write(sc, RAL_MAC_CSR4, tmp); + + DPRINTF("setting MAC address to %6D\n", addr, ":"); +} + +static void +ural_promisctask(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + uint32_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR2); + + tmp &= ~RAL_DROP_NOT_TO_ME; + if (!(ifp->if_flags & IFF_PROMISC)) + tmp |= RAL_DROP_NOT_TO_ME; + + ural_write(sc, RAL_TXRX_CSR2, tmp); + + DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? + "entering" : "leaving"); +} + +static const char * +ural_get_rf(int rev) +{ + switch (rev) { + case RAL_RF_2522: return "RT2522"; + case RAL_RF_2523: return "RT2523"; + case RAL_RF_2524: return "RT2524"; + case RAL_RF_2525: return "RT2525"; + case RAL_RF_2525E: return "RT2525e"; + case RAL_RF_2526: return "RT2526"; + case RAL_RF_5222: return "RT5222"; + default: return "unknown"; + } +} + +static void +ural_read_eeprom(struct ural_softc *sc) +{ + uint16_t val; + + ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x7; + sc->hw_radio = (val >> 10) & 0x1; + sc->led_mode = (val >> 6) & 0x7; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + /* read MAC address */ + ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_bssid, 6); + + /* read default values for BBP registers */ + ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); + + /* read Tx power for all b/g channels */ + ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); +} + +static int +ural_bbp_init(struct ural_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < N(ural_def_bbp); i++) + ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); + +#if 0 + /* initialize BBP registers to values stored in EEPROM */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0xff) + continue; + ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } +#endif + + return 0; +#undef N +} + +static void +ural_set_txantenna(struct ural_softc *sc, int antenna) +{ + uint16_t tmp; + uint8_t tx; + + tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + tx |= RAL_BBP_ANTA; + else if (antenna == 2) + tx |= RAL_BBP_ANTB; + else + tx |= RAL_BBP_DIVERSITY; + + /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || + sc->rf_rev == RAL_RF_5222) + tx |= RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_TX, tx); + + /* update values in PHY_CSR5 and PHY_CSR6 */ + tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; + ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); + + tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; + ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); +} + +static void +ural_set_rxantenna(struct ural_softc *sc, int antenna) +{ + uint8_t rx; + + rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + rx |= RAL_BBP_ANTA; + else if (antenna == 2) + rx |= RAL_BBP_ANTB; + else + rx |= RAL_BBP_DIVERSITY; + + /* need to force no I/Q flip for RF 2525e and 2526 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) + rx &= ~RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_RX, rx); +} + +static void +ural_init_task(struct usb2_proc_msg *pm) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint16_t tmp; + int i, ntries; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + ural_set_testmode(sc); + ural_write(sc, 0x308, 0x00f0); /* XXX magic */ + + ural_stop_task(pm); + + /* initialize MAC registers to default values */ + for (i = 0; i < N(ural_def_mac); i++) + ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); + + /* wait for BBP and RF to wake up (this can take a long time!) */ + for (ntries = 0; ntries < 100; ntries++) { + tmp = ural_read(sc, RAL_MAC_CSR17); + if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == + (RAL_BBP_AWAKE | RAL_RF_AWAKE)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + goto fail; + } + + /* we're ready! */ + ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); + + /* set basic rate set (will be updated later) */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + + if (ural_bbp_init(sc) != 0) + goto fail; + + ural_set_chan(sc, ic->ic_curchan); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + ural_set_txantenna(sc, sc->tx_ant); + ural_set_rxantenna(sc, sc->rx_ant); + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + ural_set_macaddr(sc, ic->ic_myaddr); + + /* + * Allocate Tx and Rx xfer queues. + */ + ural_setup_tx_list(sc); + + /* kick Rx */ + tmp = RAL_DROP_PHY | RAL_DROP_CRC; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RAL_DROP_TODS; + if (!(ifp->if_flags & IFF_PROMISC)) + tmp |= RAL_DROP_NOT_TO_ME; + } + ural_write(sc, RAL_TXRX_CSR2, tmp); + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + usb2_transfer_start(sc->sc_xfer[URAL_BULK_RD]); + return; + +fail: ural_stop_task(pm); +#undef N +} + +static void +ural_init(void *priv) +{ + struct ural_softc *sc = priv; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + RAL_LOCK(sc); + ural_queue_command(sc, ural_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + RAL_UNLOCK(sc); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); /* start all vap's */ +} + +static void +ural_stop_task(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + /* + * Drain all the transfers, if not already drained: + */ + RAL_UNLOCK(sc); + usb2_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); + usb2_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); + RAL_LOCK(sc); + + ural_unsetup_tx_list(sc); + + /* disable Rx */ + ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); + /* reset ASIC and BBP (but won't reset MAC registers!) */ + ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); + /* wait a little */ + ural_pause(sc, hz / 10); + ural_write(sc, RAL_MAC_CSR1, 0); +} + +static int +ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct ural_softc *sc = ifp->if_softc; + + RAL_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + RAL_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return ENETDOWN; + } + if (sc->tx_nfree == 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + RAL_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return EIO; + } + + ifp->if_opackets++; + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if (ural_tx_mgt(sc, m, ni) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if (ural_tx_raw(sc, m, ni, params) != 0) + goto bad; + } + RAL_UNLOCK(sc); + return 0; +bad: + ifp->if_oerrors++; + RAL_UNLOCK(sc); + ieee80211_free_node(ni); + return EIO; /* XXX */ +} + +static void +ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ural_vap *uvp = URAL_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni); + + usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); +} + +static void +ural_amrr_timeout(void *arg) +{ + struct ural_vap *uvp = arg; + struct ural_softc *sc = uvp->sc; + + ural_queue_command(sc, ural_amrr_task, + &uvp->amrr_task[0].hdr, &uvp->amrr_task[1].hdr); +} + +static void +ural_amrr_task(struct usb2_proc_msg *pm) +{ + struct ural_task *task = (struct ural_task *)pm; + struct ural_softc *sc = task->sc; + 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); + struct ieee80211_node *ni = vap->iv_bss; + int ok, fail; + + /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); + + ok = sc->sta[7] + /* TX ok w/o retry */ + sc->sta[8]; /* TX ok w/ retry */ + fail = sc->sta[9]; /* TX retry-fail count */ + + ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, + ok+fail, ok, sc->sta[8] + fail); + (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn); + + ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ + + usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); +} + +static int +ural_pause(struct ural_softc *sc, int timeout) +{ + if (usb2_proc_is_gone(&sc->sc_tq)) + return (1); + + usb2_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} + +static void +ural_queue_command(struct ural_softc *sc, usb2_proc_callback_t *fn, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) +{ + struct ural_task *task; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + if (usb2_proc_is_gone(&sc->sc_tq)) { + DPRINTF("proc is gone\n"); + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct ural_task *) + usb2_proc_msignal(&sc->sc_tq, t0, t1); + + /* Setup callback and softc pointers */ + task->hdr.pm_callback = fn; + task->sc = sc; + + /* + * Init and stop must be synchronous! + */ + if ((fn == ural_init_task) || (fn == ural_stop_task)) + usb2_proc_mwait(&sc->sc_tq, t0, t1); +} + diff --git a/sys/dev/usb/wlan/if_uralreg.h b/sys/dev/usb/wlan/if_uralreg.h new file mode 100644 index 0000000..042cf5a --- /dev/null +++ b/sys/dev/usb/wlan/if_uralreg.h @@ -0,0 +1,211 @@ +/* $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 */ + +#define RAL_CONFIG_NO 1 +#define RAL_IFACE_INDEX 0 + +#define RAL_VENDOR_REQUEST 0x01 +#define RAL_WRITE_MAC 0x02 +#define RAL_READ_MAC 0x03 +#define RAL_WRITE_MULTI_MAC 0x06 +#define RAL_READ_MULTI_MAC 0x07 +#define RAL_READ_EEPROM 0x09 + +/* + * MAC registers. + */ +#define RAL_MAC_CSR0 0x0400 /* ASIC Version */ +#define RAL_MAC_CSR1 0x0402 /* System control */ +#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */ +#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */ +#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */ +#define RAL_MAC_CSR5 0x040a /* BSSID0 */ +#define RAL_MAC_CSR6 0x040c /* BSSID1 */ +#define RAL_MAC_CSR7 0x040e /* BSSID2 */ +#define RAL_MAC_CSR8 0x0410 /* Max frame length */ +#define RAL_MAC_CSR9 0x0412 /* Timer control */ +#define RAL_MAC_CSR10 0x0414 /* Slot time */ +#define RAL_MAC_CSR11 0x0416 /* IFS */ +#define RAL_MAC_CSR12 0x0418 /* EIFS */ +#define RAL_MAC_CSR13 0x041a /* Power mode0 */ +#define RAL_MAC_CSR14 0x041c /* Power mode1 */ +#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */ +#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */ +#define RAL_MAC_CSR17 0x0422 /* Power state control */ +#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */ +#define RAL_MAC_CSR19 0x0426 /* GPIO control */ +#define RAL_MAC_CSR20 0x0428 /* LED control0 */ +#define RAL_MAC_CSR22 0x042c /* XXX not documented */ + +/* + * Tx/Rx Registers. + */ +#define RAL_TXRX_CSR0 0x0440 /* Security control */ +#define RAL_TXRX_CSR2 0x0444 /* Rx control */ +#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */ +#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */ +#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */ +#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */ +#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */ +#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */ +#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */ +#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */ +#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */ +#define RAL_TXRX_CSR21 0x046a /* XXX not documented */ + +/* + * Security registers. + */ +#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */ + +/* + * PHY registers. + */ +#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */ +#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */ +#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */ +#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */ +#define RAL_PHY_CSR7 0x04ce /* BBP serial control */ +#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */ +#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */ +#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */ + +/* + * Statistics registers. + */ +#define RAL_STA_CSR0 0x04e0 /* FCS error */ + + +#define RAL_DISABLE_RX (1 << 0) +#define RAL_DROP_CRC (1 << 1) +#define RAL_DROP_PHY (1 << 2) +#define RAL_DROP_CTL (1 << 3) +#define RAL_DROP_NOT_TO_ME (1 << 4) +#define RAL_DROP_TODS (1 << 5) +#define RAL_DROP_BAD_VERSION (1 << 6) +#define RAL_DROP_MULTICAST (1 << 9) +#define RAL_DROP_BROADCAST (1 << 10) + +#define RAL_SHORT_PREAMBLE (1 << 2) + +#define RAL_RESET_ASIC (1 << 0) +#define RAL_RESET_BBP (1 << 1) +#define RAL_HOST_READY (1 << 2) + +#define RAL_ENABLE_TSF (1 << 0) +#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1) +#define RAL_ENABLE_TBCN (1 << 3) +#define RAL_ENABLE_BEACON_GENERATOR (1 << 4) + +#define RAL_RF_AWAKE (3 << 7) +#define RAL_BBP_AWAKE (3 << 5) + +#define RAL_BBP_WRITE (1 << 15) +#define RAL_BBP_BUSY (1 << 0) + +#define RAL_RF1_AUTOTUNE 0x08000 +#define RAL_RF3_AUTOTUNE 0x00040 + +#define RAL_RF_2522 0x00 +#define RAL_RF_2523 0x01 +#define RAL_RF_2524 0x02 +#define RAL_RF_2525 0x03 +#define RAL_RF_2525E 0x04 +#define RAL_RF_2526 0x05 +/* dual-band RF */ +#define RAL_RF_5222 0x10 + +#define RAL_BBP_VERSION 0 +#define RAL_BBP_TX 2 +#define RAL_BBP_RX 14 + +#define RAL_BBP_ANTA 0x00 +#define RAL_BBP_DIVERSITY 0x01 +#define RAL_BBP_ANTB 0x02 +#define RAL_BBP_ANTMASK 0x03 +#define RAL_BBP_FLIPIQ 0x04 + +#define RAL_JAPAN_FILTER 0x08 + +struct ural_tx_desc { + uint32_t flags; +#define RAL_TX_RETRY(x) ((x) << 4) +#define RAL_TX_MORE_FRAG (1 << 8) +#define RAL_TX_ACK (1 << 9) +#define RAL_TX_TIMESTAMP (1 << 10) +#define RAL_TX_OFDM (1 << 11) +#define RAL_TX_NEWSEQ (1 << 12) + +#define RAL_TX_IFS_MASK 0x00006000 +#define RAL_TX_IFS_BACKOFF (0 << 13) +#define RAL_TX_IFS_SIFS (1 << 13) +#define RAL_TX_IFS_NEWBACKOFF (2 << 13) +#define RAL_TX_IFS_NONE (3 << 13) + + uint16_t wme; +#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12) +#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8) +#define RAL_AIFSN(x) (((x) & 0x3) << 6) +#define RAL_IVOFFSET(x) (((x) & 0x3f)) + + uint16_t reserved1; + uint8_t plcp_signal; + uint8_t plcp_service; +#define RAL_PLCP_LENGEXT 0x80 + + uint8_t plcp_length_lo; + uint8_t plcp_length_hi; + uint32_t iv; + uint32_t eiv; +} __packed; + +struct ural_rx_desc { + uint32_t flags; +#define RAL_RX_CRC_ERROR (1 << 5) +#define RAL_RX_OFDM (1 << 6) +#define RAL_RX_PHY_ERROR (1 << 7) + + uint8_t rssi; + uint8_t rate; + uint16_t reserved; + + uint32_t iv; + uint32_t eiv; +} __packed; + +#define RAL_RF_LOBUSY (1 << 15) +#define RAL_RF_BUSY (1 << 31) +#define RAL_RF_20BIT (20 << 24) + +#define RAL_RF1 0 +#define RAL_RF2 2 +#define RAL_RF3 1 +#define RAL_RF4 3 + +#define RAL_EEPROM_ADDRESS 0x0004 +#define RAL_EEPROM_TXPOWER 0x003c +#define RAL_EEPROM_CONFIG0 0x0016 +#define RAL_EEPROM_BBP_BASE 0x001c diff --git a/sys/dev/usb/wlan/if_uralvar.h b/sys/dev/usb/wlan/if_uralvar.h new file mode 100644 index 0000000..c7e5469 --- /dev/null +++ b/sys/dev/usb/wlan/if_uralvar.h @@ -0,0 +1,155 @@ +/* $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 RAL_TX_LIST_COUNT 8 + +#define URAL_SCAN_START 1 +#define URAL_SCAN_END 2 +#define URAL_SET_CHANNEL 3 + + +struct ural_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antenna; + uint8_t wr_antsignal; +}; + +#define RAL_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) + +struct ural_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_antenna; +}; + +#define RAL_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct ural_softc; + +struct ural_task { + struct usb2_proc_msg hdr; + struct ural_softc *sc; +}; + +struct ural_tx_data { + STAILQ_ENTRY(ural_tx_data) next; + struct ural_softc *sc; + struct ural_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead; + +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 ural_softc *sc; + struct ieee80211_beacon_offsets bo; + struct ieee80211_amrr amrr; + struct usb2_callout amrr_ch; + struct ural_task amrr_task[2]; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define URAL_VAP(vap) ((struct ural_vap *)(vap)) + +enum { + URAL_BULK_WR, + URAL_BULK_RD, + URAL_N_TRANSFER = 2, +}; + +struct ural_softc { + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_process sc_tq; + + const struct ieee80211_rate_table *sc_rates; + + uint32_t asic_rev; + uint8_t rf_rev; + + struct usb2_xfer *sc_xfer[URAL_N_TRANSFER]; + + enum ieee80211_state sc_state; + int sc_arg; + int sc_scan_action; /* should be an enum */ + struct ural_task sc_synctask[2]; + struct ural_task sc_task[2]; + struct ural_task sc_promisctask[2]; + struct ural_task sc_scantask[2]; + + struct ural_tx_data tx_data[RAL_TX_LIST_COUNT]; + ural_txdhead tx_q; + ural_txdhead tx_free; + int tx_nfree; + struct ural_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + uint16_t sta[11]; + uint32_t rf_regs[4]; + uint8_t txpow[14]; + uint8_t sc_bssid[6]; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int led_mode; + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + + struct ural_rx_radiotap_header sc_rxtap; + int sc_rxtap_len; + + struct ural_tx_radiotap_header sc_txtap; + int sc_txtap_len; +}; + +#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/sys/dev/usb/wlan/if_zyd.c b/sys/dev/usb/wlan/if_zyd.c new file mode 100644 index 0000000..1d8b38f --- /dev/null +++ b/sys/dev/usb/wlan/if_zyd.c @@ -0,0 +1,3121 @@ +/* $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. + */ + +#include "usbdevs.h" +#include +#include +#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"); + +enum { + ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */ + ZYD_DEBUG_RESET = 0x00000004, /* reset processing */ + ZYD_DEBUG_INIT = 0x00000008, /* device init */ + ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ + ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ + ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ + ZYD_DEBUG_STAT = 0x00000080, /* statistic */ + ZYD_DEBUG_FW = 0x00000100, /* firmware */ + ZYD_DEBUG_CMD = 0x00000200, /* fw commands */ + ZYD_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (zyd_debug & (m)) \ + printf("%s: " fmt, __func__, ## __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif + +#define zyd_do_request(sc,req,data) \ + usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) + +static device_probe_t zyd_match; +static device_attach_t zyd_attach; +static device_detach_t zyd_detach; + +static usb2_callback_t zyd_intr_read_callback; +static usb2_callback_t zyd_intr_write_callback; +static usb2_callback_t zyd_bulk_read_callback; +static usb2_callback_t zyd_bulk_write_callback; + +static usb2_proc_callback_t zyd_attach_post; +static usb2_proc_callback_t zyd_task; +static usb2_proc_callback_t zyd_scantask; +static usb2_proc_callback_t zyd_multitask; +static usb2_proc_callback_t zyd_init_task; +static usb2_proc_callback_t zyd_stop_task; + +static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void zyd_vap_delete(struct ieee80211vap *); +static void zyd_tx_free(struct zyd_tx_data *, int); +static void zyd_setup_tx_list(struct zyd_softc *); +static void zyd_unsetup_tx_list(struct zyd_softc *); +static struct ieee80211_node *zyd_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int, + void *, int, int); +static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *); +static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *); +static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t); +static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t); +static int zyd_rfwrite(struct zyd_softc *, uint32_t); +static int zyd_lock_phy(struct zyd_softc *); +static int zyd_unlock_phy(struct zyd_softc *); +static int zyd_rf_attach(struct zyd_softc *, uint8_t); +static const char *zyd_rf_name(uint8_t); +static int zyd_hw_init(struct zyd_softc *); +static int zyd_read_pod(struct zyd_softc *); +static int zyd_read_eeprom(struct zyd_softc *); +static int zyd_get_macaddr(struct zyd_softc *); +static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *); +static int zyd_set_bssid(struct zyd_softc *, const uint8_t *); +static int zyd_switch_radio(struct zyd_softc *, int); +static int zyd_set_led(struct zyd_softc *, int, int); +static void zyd_set_multi(struct zyd_softc *); +static void zyd_update_mcast(struct ifnet *); +static int zyd_set_rxfilter(struct zyd_softc *); +static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *); +static int zyd_set_beacon_interval(struct zyd_softc *, int); +static void zyd_rx_data(struct usb2_xfer *, int, uint16_t); +static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *, + struct ieee80211_node *); +static int zyd_tx_data(struct zyd_softc *, struct mbuf *, + struct ieee80211_node *); +static void zyd_start(struct ifnet *); +static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int zyd_ioctl(struct ifnet *, u_long, caddr_t); +static void zyd_init(void *); +static int zyd_loadfirmware(struct zyd_softc *); +static void zyd_newassoc(struct ieee80211_node *, int); +static void zyd_scan_start(struct ieee80211com *); +static void zyd_scan_end(struct ieee80211com *); +static void zyd_set_channel(struct ieee80211com *); +static int zyd_rfmd_init(struct zyd_rf *); +static int zyd_rfmd_switch_radio(struct zyd_rf *, int); +static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2230_init(struct zyd_rf *); +static int zyd_al2230_switch_radio(struct zyd_rf *, int); +static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t); +static int zyd_al2230_init_b(struct zyd_rf *); +static int zyd_al7230B_init(struct zyd_rf *); +static int zyd_al7230B_switch_radio(struct zyd_rf *, int); +static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2210_init(struct zyd_rf *); +static int zyd_al2210_switch_radio(struct zyd_rf *, int); +static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t); +static int zyd_gct_init(struct zyd_rf *); +static int zyd_gct_switch_radio(struct zyd_rf *, int); +static int zyd_gct_set_channel(struct zyd_rf *, uint8_t); +static int zyd_maxim_init(struct zyd_rf *); +static int zyd_maxim_switch_radio(struct zyd_rf *, int); +static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t); +static int zyd_maxim2_init(struct zyd_rf *); +static int zyd_maxim2_switch_radio(struct zyd_rf *, int); +static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t); +static void zyd_queue_command(struct zyd_softc *, usb2_proc_callback_t *, + struct usb2_proc_msg *, struct usb2_proc_msg *); + +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_BULK_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_BULK_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_INTR_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_INTR_RD] = { + .type = UE_INTERRUPT, + .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, + }, +}; +#define zyd_read16_m(sc, val, data) do { \ + error = zyd_read16(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_write16_m(sc, val, data) do { \ + error = zyd_write16(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_read32_m(sc, val, data) do { \ + error = zyd_read32(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_write32_m(sc, val, data) do { \ + error = zyd_write32(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) + +static int +zyd_match(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 != ZYD_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) + return (ENXIO); + + return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); +} + +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 (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); + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + sc->sc_macrev = USB_GET_DRIVER_INFO(uaa); + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), + MTX_NETWORK_LOCK, MTX_DEF); + + STAILQ_INIT(&sc->sc_rqh); + + 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, " + "err=%s\n", usb2_errstr(error)); + goto detach; + } + error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, + device_get_nameunit(dev), USB_PRI_MED); + if (error) { + device_printf(dev, "could not setup config thread!\n"); + goto detach; + } + + /* fork rest of the attach code */ + ZYD_LOCK(sc); + zyd_queue_command(sc, zyd_attach_post, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + ZYD_UNLOCK(sc); + return (0); + +detach: + zyd_detach(dev); + return (ENXIO); /* failure */ +} + +static void +zyd_attach_post(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + struct ifnet *ifp; + struct ieee80211com *ic; + int error; + uint8_t bands; + + if ((error = zyd_get_macaddr(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + return; + } + + ZYD_UNLOCK(sc); + + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(sc->sc_dev, "can not if_alloc()\n"); + ZYD_LOCK(sc); + return; + } + ifp->if_softc = sc; + if_initname(ifp, "zyd", device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = zyd_init; + ifp->if_ioctl = zyd_ioctl; + ifp->if_start = zyd_start; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + IFQ_SET_READY(&ifp->if_snd); + + ic = ifp->if_l2com; + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; + IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic); + ic->ic_newassoc = zyd_newassoc; + ic->ic_raw_xmit = zyd_raw_xmit; + ic->ic_node_alloc = zyd_node_alloc; + ic->ic_scan_start = zyd_scan_start; + ic->ic_scan_end = zyd_scan_end; + ic->ic_set_channel = zyd_set_channel; + + ic->ic_vap_create = zyd_vap_create; + ic->ic_vap_delete = zyd_vap_delete; + ic->ic_update_mcast = zyd_update_mcast; + ic->ic_update_promisc = zyd_update_mcast; + + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); + sc->sc_rxtap_len = sizeof(sc->sc_rxtap); + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); + sc->sc_txtap_len = sizeof(sc->sc_txtap); + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + ZYD_LOCK(sc); +} + +static int +zyd_detach(device_t dev) +{ + struct zyd_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic; + + /* wait for any post attach or other command to complete */ + usb2_proc_drain(&sc->sc_tq); + + /* stop all USB transfers */ + usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); + usb2_proc_free(&sc->sc_tq); + + /* free TX list, if any */ + zyd_unsetup_tx_list(sc); + + if (ifp) { + ic = ifp->if_l2com; + bpfdetach(ifp); + ieee80211_ifdetach(ic); + if_free(ifp); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +zyd_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct zyd_vap *zvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (zvp == NULL) + return (NULL); + vap = &zvp->vap; + /* enable s/w bmiss handling for sta mode */ + ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); + + /* override state transition machine */ + zvp->newstate = vap->iv_newstate; + vap->iv_newstate = zyd_newstate; + + ieee80211_amrr_init(&zvp->amrr, vap, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, + 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status); + ic->ic_opmode = opmode; + return (vap); +} + +static void +zyd_vap_delete(struct ieee80211vap *vap) +{ + struct zyd_vap *zvp = ZYD_VAP(vap); + + ieee80211_amrr_cleanup(&zvp->amrr); + ieee80211_vap_detach(vap); + free(zvp, M_80211_VAP); +} + +static void +zyd_tx_free(struct zyd_tx_data *data, int txerr) +{ + struct zyd_softc *sc = data->sc; + + if (data->m != NULL) { + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, + txerr ? ETIMEDOUT : 0); + m_freem(data->m); + data->m = NULL; + + ieee80211_free_node(data->ni); + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +zyd_setup_tx_list(struct zyd_softc *sc) +{ + struct zyd_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < ZYD_TX_LIST_CNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +zyd_unsetup_tx_list(struct zyd_softc *sc) +{ + struct zyd_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < ZYD_TX_LIST_CNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +/* ARGUSED */ +static struct ieee80211_node * +zyd_node_alloc(struct ieee80211vap *vap __unused, + const uint8_t mac[IEEE80211_ADDR_LEN] __unused) +{ + struct zyd_node *zn; + + zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO); + return (zn != NULL) ? (&zn->ni) : (NULL); +} + +static void +zyd_task(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni = vap->iv_bss; + struct zyd_vap *zvp = ZYD_VAP(vap); + int error; + + switch (sc->sc_state) { + case IEEE80211_S_AUTH: + zyd_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_RUN: + if (vap->iv_opmode == IEEE80211_M_MONITOR) + break; + + /* turn link LED on */ + error = zyd_set_led(sc, ZYD_LED1, 1); + if (error != 0) + goto fail; + + /* make data LED blink upon Tx */ + zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1); + + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + zyd_set_bssid(sc, sc->sc_bssid); + break; + default: + break; + } +fail: + ZYD_UNLOCK(sc); + IEEE80211_LOCK(ic); + zvp->newstate(vap, sc->sc_state, sc->sc_arg); + if (vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); + IEEE80211_UNLOCK(ic); + ZYD_LOCK(sc); +} + +static int +zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct zyd_vap *zvp = ZYD_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + ZYD_LOCK(sc); + /* do it in a process context */ + sc->sc_state = nstate; + sc->sc_arg = arg; + ZYD_UNLOCK(sc); + + if (nstate == IEEE80211_S_INIT) { + zvp->newstate(vap, nstate, arg); + return (0); + } else { + ZYD_LOCK(sc); + zyd_queue_command(sc, zyd_task, &sc->sc_task[0].hdr, + &sc->sc_task[1].hdr); + ZYD_UNLOCK(sc); + return (EINPROGRESS); + } +} + +/* + * Callback handler for interrupt transfer + */ +static void +zyd_intr_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 ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni; + struct zyd_cmd *cmd = &sc->sc_ibuf; + int datalen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_copy_out(xfer->frbuffers, 0, cmd, sizeof(*cmd)); + + switch (le16toh(cmd->code)) { + case ZYD_NOTIF_RETRYSTATUS: + { + struct zyd_notif_retry *retry = + (struct zyd_notif_retry *)cmd->data; + + DPRINTF(sc, ZYD_DEBUG_TX_PROC, + "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", + le16toh(retry->rate), ether_sprintf(retry->macaddr), + le16toh(retry->count)&0xff, le16toh(retry->count)); + + /* + * Find the node to which the packet was sent and + * update its retry statistics. In BSS mode, this node + * is the AP we're associated to so no lookup is + * actually needed. + */ + ni = ieee80211_find_txnode(vap, retry->macaddr); + if (ni != NULL) { + ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, + IEEE80211_AMRR_FAILURE, 1); + ieee80211_free_node(ni); + } + if (le16toh(retry->count) & 0x100) + ifp->if_oerrors++; /* too many retries */ + break; + } + case ZYD_NOTIF_IORD: + { + struct zyd_rq *rqp; + + if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT) + break; /* HMAC interrupt */ + + datalen = xfer->actlen - sizeof(cmd->code); + datalen -= 2; /* XXX: padding? */ + + STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { + int i, cnt; + + if (rqp->olen != datalen) + continue; + cnt = rqp->olen / sizeof(struct zyd_pair); + for (i = 0; i < cnt; i++) { + if (*(((const uint16_t *)rqp->idata) + i) != + (((struct zyd_pair *)cmd->data) + i)->reg) + break; + } + if (i != cnt) + continue; + /* copy answer into caller-supplied buffer */ + bcopy(cmd->data, rqp->odata, rqp->olen); + DPRINTF(sc, ZYD_DEBUG_CMD, + "command %p complete, data = %*D \n", + rqp, rqp->olen, rqp->odata, ":"); + wakeup(rqp); /* wakeup caller */ + break; + } + if (rqp == NULL) { + device_printf(sc->sc_dev, + "unexpected IORD notification %*D\n", + datalen, cmd->data, ":"); + } + break; + } + default: + device_printf(sc->sc_dev, "unknown notification %x\n", + le16toh(cmd->code)); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_CMD, "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; + } + break; + } +} + +static void +zyd_intr_write_callback(struct usb2_xfer *xfer) +{ + struct zyd_softc *sc = xfer->priv_sc; + struct zyd_rq *rqp; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + rqp = xfer->priv_fifo; + DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", rqp); + if ((rqp->flags & ZYD_CMD_FLAG_READ) == 0) + wakeup(rqp); /* wakeup caller */ + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { + if (rqp->flags & ZYD_CMD_FLAG_SENT) + continue; + + usb2_copy_in(xfer->frbuffers, 0, rqp->cmd, rqp->ilen); + + xfer->frlengths[0] = rqp->ilen; + xfer->priv_fifo = rqp; + rqp->flags |= ZYD_CMD_FLAG_SENT; + usb2_start_hardware(xfer); + break; + } + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "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; + } + break; + } +} + +static int +zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen, + void *odata, int olen, int flags) +{ + struct zyd_cmd cmd; + struct zyd_rq rq; + int error; + + if (ilen > sizeof(cmd.data)) + return (EINVAL); + + if (usb2_proc_is_gone(&sc->sc_tq)) + return (ENXIO); + + cmd.code = htole16(code); + bcopy(idata, cmd.data, ilen); + DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n", + &rq, ilen, idata, ":"); + + rq.cmd = &cmd; + rq.idata = idata; + rq.odata = odata; + rq.ilen = sizeof(uint16_t) + ilen; + rq.olen = olen; + rq.flags = flags; + STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq); + usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); + usb2_transfer_start(sc->sc_xfer[ZYD_INTR_WR]); + + /* wait at most one second for command reply */ + error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz); + if (error) + device_printf(sc->sc_dev, "command timeout\n"); + STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq); + DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n", + &rq, error); + + return (error); +} + +static int +zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) +{ + struct zyd_pair tmp; + int error; + + reg = htole16(reg); + error = zyd_cmd(sc, ZYD_CMD_IORD, ®, sizeof(reg), &tmp, sizeof(tmp), + ZYD_CMD_FLAG_READ); + if (error == 0) + *val = le16toh(tmp.val); + return (error); +} + +static int +zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) +{ + struct zyd_pair tmp[2]; + uint16_t regs[2]; + int error; + + regs[0] = htole16(ZYD_REG32_HI(reg)); + regs[1] = htole16(ZYD_REG32_LO(reg)); + error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), + ZYD_CMD_FLAG_READ); + if (error == 0) + *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val); + return (error); +} + +static int +zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) +{ + struct zyd_pair pair; + + pair.reg = htole16(reg); + pair.val = htole16(val); + + return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0); +} + +static int +zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) +{ + struct zyd_pair pair[2]; + + pair[0].reg = htole16(ZYD_REG32_HI(reg)); + pair[0].val = htole16(val >> 16); + pair[1].reg = htole16(ZYD_REG32_LO(reg)); + pair[1].val = htole16(val & 0xffff); + + return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); +} + +static int +zyd_rfwrite(struct zyd_softc *sc, uint32_t val) +{ + struct zyd_rf *rf = &sc->sc_rf; + struct zyd_rfwrite_cmd req; + uint16_t cr203; + int error, i; + + zyd_read16_m(sc, ZYD_CR203, &cr203); + cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); + + req.code = htole16(2); + req.width = htole16(rf->width); + for (i = 0; i < rf->width; i++) { + req.bit[i] = htole16(cr203); + if (val & (1 << (rf->width - 1 - i))) + req.bit[i] |= htole16(ZYD_RF_DATA); + } + error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0); +fail: + return (error); +} + +static int +zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val) +{ + int error; + + zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff); + zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff); + zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff); +fail: + return (error); +} + +static int +zyd_lock_phy(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); + tmp &= ~ZYD_UNLOCK_PHY_REGS; + zyd_write32_m(sc, ZYD_MAC_MISC, tmp); +fail: + return (error); +} + +static int +zyd_unlock_phy(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); + tmp |= ZYD_UNLOCK_PHY_REGS; + zyd_write32_m(sc, ZYD_MAC_MISC, tmp); +fail: + return (error); +} + +/* + * RFMD RF methods. + */ +static int +zyd_rfmd_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; + static const uint32_t rfini[] = ZYD_RFMD_RF; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) { + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + } + + /* init RFMD radio */ + for (i = 0; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } +fail: + return (error); +#undef N +} + +static int +zyd_rfmd_switch_radio(struct zyd_rf *rf, int on) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + + zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15); + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81); +fail: + return (error); +} + +static int +zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_RFMD_CHANTABLE; + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + +fail: + return (error); +} + +/* + * AL2230 RF methods. + */ +static int +zyd_al2230_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const struct zyd_phy_pair phypll[] = { + { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } + }; + static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; + static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; + static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { + for (i = 0; i < N(phy2230s); i++) + zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); + } + + /* init AL2230 radio */ + for (i = 0; i < N(rfini1); i++) { + error = zyd_rfwrite(sc, rfini1[i]); + if (error != 0) + goto fail; + } + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) + error = zyd_rfwrite(sc, 0x000824); + else + error = zyd_rfwrite(sc, 0x0005a4); + if (error != 0) + goto fail; + + for (i = 0; i < N(rfini2); i++) { + error = zyd_rfwrite(sc, rfini2[i]); + if (error != 0) + goto fail; + } + + for (i = 0; i < N(phypll); i++) + zyd_write16_m(sc, phypll[i].reg, phypll[i].val); + + for (i = 0; i < N(rfini3); i++) { + error = zyd_rfwrite(sc, rfini3[i]); + if (error != 0) + goto fail; + } +fail: + return (error); +#undef N +} + +static int +zyd_al2230_fini(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; + + for (i = 0; i < N(phy); i++) + zyd_write16_m(sc, phy[i].reg, phy[i].val); + + if (sc->sc_newphy != 0) + zyd_write16_m(sc, ZYD_CR9, 0xe1); + + zyd_write16_m(sc, ZYD_CR203, 0x6); +fail: + return (error); +#undef N +} + +static int +zyd_al2230_init_b(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; + static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; + static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; + static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; + static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; + static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; + int i, error; + + for (i = 0; i < N(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { + for (i = 0; i < N(phy2230s); i++) + zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); + } + + for (i = 0; i < 3; i++) { + error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); + if (error != 0) + return (error); + } + + for (i = 0; i < N(rfini_part1); i++) { + error = zyd_rfwrite_cr(sc, rfini_part1[i]); + if (error != 0) + return (error); + } + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) + error = zyd_rfwrite(sc, 0x241000); + else + error = zyd_rfwrite(sc, 0x25a000); + if (error != 0) + goto fail; + + for (i = 0; i < N(rfini_part2); i++) { + error = zyd_rfwrite_cr(sc, rfini_part2[i]); + if (error != 0) + return (error); + } + + for (i = 0; i < N(phy2); i++) + zyd_write16_m(sc, phy2[i].reg, phy2[i].val); + + for (i = 0; i < N(rfini_part3); i++) { + error = zyd_rfwrite_cr(sc, rfini_part3[i]); + if (error != 0) + return (error); + } + + for (i = 0; i < N(phy3); i++) + zyd_write16_m(sc, phy3[i].reg, phy3[i].val); + + error = zyd_al2230_fini(rf); +fail: + return (error); +#undef N +} + +static int +zyd_al2230_switch_radio(struct zyd_rf *rf, int on) +{ + struct zyd_softc *sc = rf->rf_sc; + int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f; + + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f); +fail: + return (error); +} + +static int +zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = { + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }, + }; + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE; + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r3); + if (error != 0) + goto fail; + + for (i = 0; i < N(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); +fail: + return (error); +#undef N +} + +static int +zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE_B; + + for (i = 0; i < N(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); + + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3); + if (error != 0) + goto fail; + error = zyd_al2230_fini(rf); +fail: + return (error); +#undef N +} + +#define ZYD_AL2230_PHY_BANDEDGE6 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ + { ZYD_CR47, 0x1e } \ +} + +static int +zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int error = 0, i; + struct zyd_softc *sc = rf->rf_sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; + int chan = ieee80211_chan2ieee(ic, c); + + if (chan == 1 || chan == 11) + r[0].val = 0x12; + + for (i = 0; i < N(r); i++) + zyd_write16_m(sc, r[i].reg, r[i].val); +fail: + return (error); +#undef N +} + +/* + * AL7230B RF methods. + */ +static int +zyd_al7230B_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; + static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; + static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; + static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; + static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; + int i, error; + + /* for AL7230B, PHY and RF need to be initialized in "phases" */ + + /* init RF-dependent PHY registers, part one */ + for (i = 0; i < N(phyini_1); i++) + zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val); + + /* init AL7230B radio, part one */ + for (i = 0; i < N(rfini_1); i++) { + if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0) + return (error); + } + /* init RF-dependent PHY registers, part two */ + for (i = 0; i < N(phyini_2); i++) + zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val); + + /* init AL7230B radio, part two */ + for (i = 0; i < N(rfini_2); i++) { + if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0) + return (error); + } + /* init RF-dependent PHY registers, part three */ + for (i = 0; i < N(phyini_3); i++) + zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val); +fail: + return (error); +#undef N +} + +static int +zyd_al7230B_switch_radio(struct zyd_rf *rf, int on) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f); +fail: + return (error); +} + +static int +zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_AL7230B_CHANTABLE; + static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; + int i, error; + + zyd_write16_m(sc, ZYD_CR240, 0x57); + zyd_write16_m(sc, ZYD_CR251, 0x2f); + + for (i = 0; i < N(rfsc); i++) { + if ((error = zyd_rfwrite(sc, rfsc[i])) != 0) + return (error); + } + + zyd_write16_m(sc, ZYD_CR128, 0x14); + zyd_write16_m(sc, ZYD_CR129, 0x12); + zyd_write16_m(sc, ZYD_CR130, 0x10); + zyd_write16_m(sc, ZYD_CR38, 0x38); + zyd_write16_m(sc, ZYD_CR136, 0xdf); + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, 0x3c9000); + if (error != 0) + goto fail; + + zyd_write16_m(sc, ZYD_CR251, 0x3f); + zyd_write16_m(sc, ZYD_CR203, 0x06); + zyd_write16_m(sc, ZYD_CR240, 0x08); +fail: + return (error); +#undef N +} + +/* + * AL2210 RF methods. + */ +static int +zyd_al2210_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; + static const uint32_t rfini[] = ZYD_AL2210_RF; + uint32_t tmp; + int i, error; + + zyd_write32_m(sc, ZYD_CR18, 2); + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + /* init AL2210 radio */ + for (i = 0; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_write32_m(sc, ZYD_CR18, 3); +fail: + return (error); +#undef N +} + +static int +zyd_al2210_switch_radio(struct zyd_rf *rf, int on) +{ + /* vendor driver does nothing for this RF chip */ + + return (0); +} + +static int +zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; + uint32_t tmp; + + zyd_write32_m(sc, ZYD_CR18, 2); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + + /* actually set the channel */ + error = zyd_rfwrite(sc, rfprog[chan - 1]); + if (error != 0) + goto fail; + + zyd_write32_m(sc, ZYD_CR18, 3); +fail: + return (error); +} + +/* + * GCT RF methods. + */ +static int +zyd_gct_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; + static const uint32_t rfini[] = ZYD_GCT_RF; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + /* init cgt radio */ + for (i = 0; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } +fail: + return (error); +#undef N +} + +static int +zyd_gct_switch_radio(struct zyd_rf *rf, int on) +{ + /* vendor driver does nothing for this RF chip */ + + return (0); +} + +static int +zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; + + error = zyd_rfwrite(sc, 0x1c0000); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1]); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, 0x1c0008); +fail: + return (error); +} + +/* + * Maxim RF methods. + */ +static int +zyd_maxim_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; + static const uint32_t rfini[] = ZYD_MAXIM_RF; + uint16_t tmp; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* init maxim radio */ + for (i = 0; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +#undef N +} + +static int +zyd_maxim_switch_radio(struct zyd_rf *rf, int on) +{ + + /* vendor driver does nothing for this RF chip */ + return (0); +} + +static int +zyd_maxim_set_channel(struct zyd_rf *rf, uint8_t chan) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; + static const uint32_t rfini[] = ZYD_MAXIM_RF; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_MAXIM_CHANTABLE; + uint16_t tmp; + int i, error; + + /* + * Do the same as we do when initializing it, except for the channel + * values coming from the two channel tables. + */ + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* first two values taken from the chantables */ + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + + /* init maxim radio - skipping the two first values */ + for (i = 2; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +#undef N +} + +/* + * Maxim2 RF methods. + */ +static int +zyd_maxim2_init(struct zyd_rf *rf) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; + static const uint32_t rfini[] = ZYD_MAXIM2_RF; + uint16_t tmp; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* init maxim2 radio */ + for (i = 0; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +#undef N +} + +static int +zyd_maxim2_switch_radio(struct zyd_rf *rf, int on) +{ + + /* vendor driver does nothing for this RF chip */ + return (0); +} + +static int +zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; + static const uint32_t rfini[] = ZYD_MAXIM2_RF; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_MAXIM2_CHANTABLE; + uint16_t tmp; + int i, error; + + /* + * Do the same as we do when initializing it, except for the channel + * values coming from the two channel tables. + */ + + /* init RF-dependent PHY registers */ + for (i = 0; i < N(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* first two values taken from the chantables */ + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + + /* init maxim2 radio - skipping the two first values */ + for (i = 2; i < N(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +#undef N +} + +static int +zyd_rf_attach(struct zyd_softc *sc, uint8_t type) +{ + struct zyd_rf *rf = &sc->sc_rf; + + rf->rf_sc = sc; + + switch (type) { + case ZYD_RF_RFMD: + rf->init = zyd_rfmd_init; + rf->switch_radio = zyd_rfmd_switch_radio; + rf->set_channel = zyd_rfmd_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2230: + case ZYD_RF_AL2230S: + if (sc->sc_macrev == ZYD_ZD1211B) { + rf->init = zyd_al2230_init_b; + rf->set_channel = zyd_al2230_set_channel_b; + } else { + rf->init = zyd_al2230_init; + rf->set_channel = zyd_al2230_set_channel; + } + rf->switch_radio = zyd_al2230_switch_radio; + rf->bandedge6 = zyd_al2230_bandedge6; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL7230B: + rf->init = zyd_al7230B_init; + rf->switch_radio = zyd_al7230B_switch_radio; + rf->set_channel = zyd_al7230B_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2210: + rf->init = zyd_al2210_init; + rf->switch_radio = zyd_al2210_switch_radio; + rf->set_channel = zyd_al2210_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_GCT: + rf->init = zyd_gct_init; + rf->switch_radio = zyd_gct_switch_radio; + rf->set_channel = zyd_gct_set_channel; + rf->width = 21; /* 21-bit RF values */ + break; + case ZYD_RF_MAXIM_NEW: + rf->init = zyd_maxim_init; + rf->switch_radio = zyd_maxim_switch_radio; + rf->set_channel = zyd_maxim_set_channel; + rf->width = 18; /* 18-bit RF values */ + break; + case ZYD_RF_MAXIM_NEW2: + rf->init = zyd_maxim2_init; + rf->switch_radio = zyd_maxim2_switch_radio; + rf->set_channel = zyd_maxim2_set_channel; + rf->width = 18; /* 18-bit RF values */ + break; + default: + device_printf(sc->sc_dev, + "sorry, radio \"%s\" is not supported yet\n", + zyd_rf_name(type)); + return (EINVAL); + } + return (0); +} + +static const char * +zyd_rf_name(uint8_t type) +{ + static const char * const zyd_rfs[] = { + "unknown", "unknown", "UW2451", "UCHIP", "AL2230", + "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", + "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", + "PHILIPS" + }; + + return zyd_rfs[(type > 15) ? 0 : type]; +} + +static int +zyd_hw_init(struct zyd_softc *sc) +{ + int error; + const struct zyd_phy_pair *phyp; + struct zyd_rf *rf = &sc->sc_rf; + uint16_t val; + + /* specify that the plug and play is finished */ + zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); + zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase); + DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n", + sc->sc_fwbase); + + /* retrieve firmware revision number */ + zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev); + zyd_write32_m(sc, ZYD_CR_GPI_EN, 0); + zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); + /* set mandatory rates - XXX assumes 802.11b/g */ + zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f); + + /* disable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); + + if ((error = zyd_read_pod(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + goto fail; + } + + /* PHY init (resetting) */ + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; + for (; phyp->reg != 0; phyp++) + zyd_write16_m(sc, phyp->reg, phyp->val); + if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) { + zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val); + zyd_write32_m(sc, ZYD_CR157, val >> 8); + } + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + /* HMAC init */ + zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020); + zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); + zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000); + zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4); + zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); + zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401); + zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080); + zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); + zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); + zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000); + zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); + zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); + zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); + zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); + zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3); + + if (sc->sc_macrev == ZYD_ZD1211) { + zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002); + zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); + } else { + zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); + zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); + zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); + zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824); + zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); + } + + /* init beacon interval to 100ms */ + if ((error = zyd_set_beacon_interval(sc, 100)) != 0) + goto fail; + + if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) { + device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n", + sc->sc_rfrev); + goto fail; + } + + /* RF chip init */ + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + error = (*rf->init)(rf); + if (error != 0) { + device_printf(sc->sc_dev, + "radio initialization failed, error %d\n", error); + goto fail; + } + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + if ((error = zyd_read_eeprom(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + goto fail; + } + +fail: return (error); +} + +static int +zyd_read_pod(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp); + sc->sc_rfrev = tmp & 0x0f; + sc->sc_ledtype = (tmp >> 4) & 0x01; + sc->sc_al2230s = (tmp >> 7) & 0x01; + sc->sc_cckgain = (tmp >> 8) & 0x01; + sc->sc_fix_cr157 = (tmp >> 13) & 0x01; + sc->sc_parev = (tmp >> 16) & 0x0f; + sc->sc_bandedge6 = (tmp >> 21) & 0x01; + sc->sc_newphy = (tmp >> 31) & 0x01; + sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; +fail: + return (error); +} + +static int +zyd_read_eeprom(struct zyd_softc *sc) +{ + uint16_t val; + int error, i; + + /* read Tx power calibration tables */ + for (i = 0; i < 7; i++) { + zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val); + sc->sc_pwrcal[i * 2] = val >> 8; + sc->sc_pwrcal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val); + sc->sc_pwrint[i * 2] = val >> 8; + sc->sc_pwrint[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val); + sc->sc_ofdm36_cal[i * 2] = val >> 8; + sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val); + sc->sc_ofdm48_cal[i * 2] = val >> 8; + sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val); + sc->sc_ofdm54_cal[i * 2] = val >> 8; + sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff; + } +fail: + return (error); +} + +static int +zyd_get_macaddr(struct zyd_softc *sc) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ZYD_READFWDATAREQ; + USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); + USETW(req.wIndex, 0); + USETW(req.wLength, IEEE80211_ADDR_LEN); + + error = zyd_do_request(sc, &req, sc->sc_bssid); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usb2_errstr(error)); + } + + return (error); +} + +static int +zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) +{ + int error; + uint32_t tmp; + + tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; + zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp); + tmp = addr[5] << 8 | addr[4]; + zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp); +fail: + return (error); +} + +static int +zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr) +{ + int error; + uint32_t tmp; + + tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; + zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp); + tmp = addr[5] << 8 | addr[4]; + zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp); +fail: + return (error); +} + +static int +zyd_switch_radio(struct zyd_softc *sc, int on) +{ + struct zyd_rf *rf = &sc->sc_rf; + int error; + + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + error = (*rf->switch_radio)(rf, on); + if (error != 0) + goto fail; + error = zyd_unlock_phy(sc); +fail: + return (error); +} + +static int +zyd_set_led(struct zyd_softc *sc, int which, int on) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); + tmp &= ~which; + if (on) + tmp |= which; + zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp); +fail: + return (error); +} + +static void +zyd_multitask(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + + zyd_set_multi(sc); +} + +static void +zyd_set_multi(struct zyd_softc *sc) +{ + int error; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ifmultiaddr *ifma; + uint32_t low, high; + uint8_t v; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + low = 0x00000000; + high = 0x80000000; + + if (ic->ic_opmode == IEEE80211_M_MONITOR || + (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { + low = 0xffffffff; + high = 0xffffffff; + } else { + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + v = ((uint8_t *)LLADDR((struct sockaddr_dl *) + ifma->ifma_addr))[5] >> 2; + if (v < 32) + low |= 1 << v; + else + high |= 1 << (v - 32); + } + IF_ADDR_UNLOCK(ifp); + } + + /* reprogram multicast global hash table */ + zyd_write32_m(sc, ZYD_MAC_GHTBL, low); + zyd_write32_m(sc, ZYD_MAC_GHTBH, high); +fail: + if (error != 0) + device_printf(sc->sc_dev, + "could not set multicast hash table\n"); +} + +static void +zyd_update_mcast(struct ifnet *ifp) +{ + struct zyd_softc *sc = ifp->if_softc; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + ZYD_LOCK(sc); + zyd_queue_command(sc, zyd_multitask, + &sc->sc_mcasttask[0].hdr, &sc->sc_mcasttask[1].hdr); + ZYD_UNLOCK(sc); +} + +static int +zyd_set_rxfilter(struct zyd_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t rxfilter; + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + rxfilter = ZYD_FILTER_BSS; + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_HOSTAP: + rxfilter = ZYD_FILTER_HOSTAP; + break; + case IEEE80211_M_MONITOR: + rxfilter = ZYD_FILTER_MONITOR; + break; + default: + /* should not get there */ + return (EINVAL); + } + return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter); +} + +static void +zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c) +{ + int error; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct zyd_rf *rf = &sc->sc_rf; + uint32_t tmp; + int chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) { + /* XXX should NEVER happen */ + device_printf(sc->sc_dev, + "%s: invalid channel %x\n", __func__, chan); + return; + } + + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + + error = (*rf->set_channel)(rf, chan); + if (error != 0) + goto fail; + + /* update Tx power */ + zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]); + + if (sc->sc_macrev == ZYD_ZD1211B) { + zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]); + zyd_write16_m(sc, ZYD_CR69, 0x28); + zyd_write16_m(sc, ZYD_CR69, 0x2a); + } + if (sc->sc_cckgain) { + /* set CCK baseband gain from EEPROM */ + if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) + zyd_write16_m(sc, ZYD_CR47, tmp & 0xff); + } + if (sc->sc_bandedge6 && rf->bandedge6 != NULL) { + error = (*rf->bandedge6)(rf, c); + if (error != 0) + goto fail; + } + zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0); + + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = + htole16(c->ic_freq); + sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = + htole16(c->ic_flags); +fail: + return; +} + +static int +zyd_set_beacon_interval(struct zyd_softc *sc, int bintval) +{ + int error; + uint32_t val; + + zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val); + sc->sc_atim_wnd = val; + zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val); + sc->sc_pre_tbtt = val; + sc->sc_bcn_int = bintval; + + if (sc->sc_bcn_int <= 5) + sc->sc_bcn_int = 5; + if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) + sc->sc_pre_tbtt = sc->sc_bcn_int - 1; + if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) + sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; + + zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); + zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); + zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); +fail: + return (error); +} + +static void +zyd_rx_data(struct usb2_xfer *xfer, int offset, uint16_t 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; + int rlen, rssi; + + if (len < ZYD_MIN_FRAGSZ) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", + device_get_nameunit(sc->sc_dev), len); + ifp->if_ierrors++; + return; + } + 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(sc, ZYD_DEBUG_RECV, + "%s: RX status indicated error (%x)\n", + device_get_nameunit(sc->sc_dev), stat.flags); + ifp->if_ierrors++; + return; + } + + /* compute actual frame length */ + rlen = len - sizeof(struct zyd_plcphdr) - + sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN; + + /* allocate a mbuf to store the frame */ + if (rlen > MCLBYTES) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n", + device_get_nameunit(sc->sc_dev), rlen); + ifp->if_ierrors++; + return; + } else if (rlen > MHLEN) + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", + device_get_nameunit(sc->sc_dev)); + ifp->if_ierrors++; + return; + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = rlen; + usb2_copy_out(xfer->frbuffers, offset + sizeof(plcp), + mtod(m, uint8_t *), rlen); + + if (bpf_peers_present(ifp->if_bpf)) { + struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) + tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + /* XXX toss, no way to express errors */ + if (stat.flags & ZYD_RX_DECRYPTERR) + tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + tap->wr_rate = ieee80211_plcp2rate(plcp.signal, + (stat.flags & ZYD_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_antsignal = stat.rssi + -95; + tap->wr_antnoise = -95; /* XXX */ + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); + } + rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi; + + sc->sc_rx_data[sc->sc_rx_count].rssi = rssi; + sc->sc_rx_data[sc->sc_rx_count].m = m; + sc->sc_rx_count++; +} + +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 desc; + struct mbuf *m; + uint32_t offset; + uint8_t rssi; + int8_t nf; + int i; + + sc->sc_rx_count = 0; + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(desc), + &desc, sizeof(desc)); + + offset = 0; + if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) { + DPRINTF(sc, ZYD_DEBUG_RECV, + "%s: received multi-frame transfer\n", __func__); + + for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) { + uint16_t len16 = UGETW(desc.len[i]); + + if (len16 == 0 || len16 > xfer->actlen) + break; + + zyd_rx_data(xfer, 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 { + DPRINTF(sc, ZYD_DEBUG_RECV, + "%s: received single-frame transfer\n", __func__); + + zyd_rx_data(xfer, 0, xfer->actlen); + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + 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! + */ + ZYD_UNLOCK(sc); + for (i = 0; i < sc->sc_rx_count; i++) { + rssi = sc->sc_rx_data[i].rssi; + m = sc->sc_rx_data[i].m; + sc->sc_rx_data[i].m = NULL; + + nf = -95; /* XXX */ + + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi, nf, 0); + ieee80211_free_node(ni); + } else + (void)ieee80211_input_all(ic, m, rssi, nf, 0); + } + ZYD_LOCK(sc); + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "frame 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; + } + break; + } +} + +static uint8_t +zyd_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: + return (0xb); + case 18: + return (0xf); + case 24: + return (0xa); + case 36: + return (0xe); + case 48: + return (0x9); + case 72: + return (0xd); + case 96: + return (0x8); + case 108: + return (0xc); + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + } + return (0xff); /* XXX unsupported/unknown rate */ +} + +static int +zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = sc->sc_ifp; + struct zyd_tx_desc *desc; + struct zyd_tx_data *data; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + int rate, totlen; + uint16_t pktlen; + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + desc = &data->desc; + + rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2; + + wh = mtod(m0, struct ieee80211_frame *); + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return (ENOBUFS); + } + } + + data->ni = ni; + data->m = m0; + data->rate = rate; + + wh = mtod(m0, struct ieee80211_frame *); + + totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; + + /* fill Tx descriptor */ + desc->len = htole16(totlen); + + desc->flags = ZYD_TX_FLAG_BACKOFF; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* multicast frames are not sent at OFDM rates in 802.11b/g */ + if (totlen > vap->iv_rtsthreshold) { + desc->flags |= ZYD_TX_FLAG_RTS; + } else if (ZYD_RATE_IS_OFDM(rate) && + (ic->ic_flags & IEEE80211_F_USEPROT)) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + desc->flags |= ZYD_TX_FLAG_RTS; + } + } else + desc->flags |= ZYD_TX_FLAG_MULTICAST; + + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) + desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); + + desc->phy = zyd_plcp_signal(rate); + if (ZYD_RATE_IS_OFDM(rate)) { + desc->phy |= ZYD_TX_PHY_OFDM; + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + desc->phy |= ZYD_TX_PHY_5GHZ; + } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->phy |= ZYD_TX_PHY_SHPREAMBLE; + + /* actual transmit length (XXX why +10?) */ + pktlen = ZYD_TX_DESC_SIZE + 10; + if (sc->sc_macrev == ZYD_ZD1211) + pktlen += totlen; + desc->pktlen = htole16(pktlen); + + desc->plcp_length = (16 * totlen + rate - 1) / rate; + desc->plcp_service = 0; + if (rate == 22) { + const int remainder = (16 * totlen) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= ZYD_PLCP_LENGEXT; + } + + if (bpf_peers_present(ifp->if_bpf)) { + struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); + } + + DPRINTF(sc, ZYD_DEBUG_XMIT, + "%s: sending mgt frame len=%zu rate=%u\n", + device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, + rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); + + return (0); +} + +static void +zyd_bulk_write_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_channel *c = ic->ic_curchan; + struct zyd_tx_data *data; + struct mbuf *m; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n", + xfer->actlen); + + /* free resources */ + data = xfer->priv_fifo; + zyd_tx_free(data, 0); + xfer->priv_fifo = NULL; + + ifp->if_opackets++; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) { + DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; + } + usb2_copy_in(xfer->frbuffers, 0, &data->desc, + ZYD_TX_DESC_SIZE); + usb2_m_copy_in(xfer->frbuffers, ZYD_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + if (bpf_peers_present(ifp->if_bpf)) { + struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_chan_freq = htole16(c->ic_freq); + tap->wt_chan_flags = htole16(c->ic_flags); + + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); + } + + xfer->frlengths[0] = ZYD_TX_DESC_SIZE + m->m_pkthdr.len; + xfer->priv_fifo = data; + usb2_start_hardware(xfer); + } + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n", + usb2_errstr(xfer->error)); + + ifp->if_oerrors++; + data = xfer->priv_fifo; + xfer->priv_fifo = NULL; + if (data != NULL) + zyd_tx_free(data, xfer->error); + + if (xfer->error == USB_ERR_STALLED) { + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + if (xfer->error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + break; + } +} + +static int +zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct zyd_tx_desc *desc; + struct zyd_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + int rate, totlen; + uint16_t pktlen; + + wh = mtod(m0, struct ieee80211_frame *); + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + desc = &data->desc; + + desc->flags = ZYD_TX_FLAG_BACKOFF; + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + rate = tp->mcastrate; + desc->flags |= ZYD_TX_FLAG_MULTICAST; + } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + rate = tp->ucastrate; + } else { + (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return (ENOBUFS); + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + data->ni = ni; + data->m = m0; + + totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; + + /* fill Tx descriptor */ + desc->len = htole16(totlen); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* multicast frames are not sent at OFDM rates in 802.11b/g */ + if (totlen > vap->iv_rtsthreshold) { + desc->flags |= ZYD_TX_FLAG_RTS; + } else if (ZYD_RATE_IS_OFDM(rate) && + (ic->ic_flags & IEEE80211_F_USEPROT)) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + desc->flags |= ZYD_TX_FLAG_RTS; + } + } + + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) + desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); + + desc->phy = zyd_plcp_signal(rate); + if (ZYD_RATE_IS_OFDM(rate)) { + desc->phy |= ZYD_TX_PHY_OFDM; + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + desc->phy |= ZYD_TX_PHY_5GHZ; + } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->phy |= ZYD_TX_PHY_SHPREAMBLE; + + /* actual transmit length (XXX why +10?) */ + pktlen = sizeof(struct zyd_tx_desc) + 10; + if (sc->sc_macrev == ZYD_ZD1211) + pktlen += totlen; + desc->pktlen = htole16(pktlen); + + desc->plcp_length = (16 * totlen + rate - 1) / rate; + desc->plcp_service = 0; + if (rate == 22) { + const int remainder = (16 * totlen) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= ZYD_PLCP_LENGEXT; + } + + DPRINTF(sc, ZYD_DEBUG_XMIT, + "%s: sending data frame len=%zu rate=%u\n", + device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, + rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); + + return (0); +} + +static void +zyd_start(struct ifnet *ifp) +{ + struct zyd_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + struct mbuf *m; + + ZYD_LOCK(sc); + for (;;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + if (sc->tx_nfree == 0) { + IFQ_DRV_PREPEND(&ifp->if_snd, m); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m = ieee80211_encap(ni, m); + if (m == NULL) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + continue; + } + if (zyd_tx_data(sc, m, ni) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + break; + } + } + ZYD_UNLOCK(sc); +} + +static int +zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct zyd_softc *sc = ifp->if_softc; + + ZYD_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + ZYD_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return (ENETDOWN); + } + if (sc->tx_nfree == 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + ZYD_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return (ENOBUFS); /* XXX */ + } + + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + * XXX raw path + */ + if (zyd_tx_mgt(sc, m, ni) != 0) { + ZYD_UNLOCK(sc); + ifp->if_oerrors++; + ieee80211_free_node(ni); + return (EIO); + } + ZYD_UNLOCK(sc); + return (0); +} + +static int +zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct zyd_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + ZYD_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->sc_if_flags) & + (IFF_ALLMULTI | IFF_PROMISC)) + zyd_set_multi(sc); + } else { + zyd_queue_command(sc, zyd_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + startall = 1; + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + zyd_queue_command(sc, zyd_stop_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + } + } + sc->sc_if_flags = ifp->if_flags; + ZYD_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); + break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static void +zyd_init_task(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct usb2_config_descriptor *cd; + int error; + uint32_t val; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) { + error = zyd_loadfirmware(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load firmware (error=%d)\n", error); + goto fail; + } + + /* reset device */ + cd = usb2_get_config_descriptor(sc->sc_udev); + error = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (error) + device_printf(sc->sc_dev, "reset failed, continuing\n"); + + error = zyd_hw_init(sc); + if (error) { + device_printf(sc->sc_dev, + "hardware initialization failed\n"); + goto fail; + } + + device_printf(sc->sc_dev, + "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x " + "BE%x NP%x Gain%x F%x\n", + (sc->sc_macrev == ZYD_ZD1211) ? "": "B", + sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff, + zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev, + sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy, + sc->sc_cckgain, sc->sc_fix_cr157); + + /* read regulatory domain (currently unused) */ + zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val); + sc->sc_regdomain = val >> 16; + DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n", + sc->sc_regdomain); + + /* we'll do software WEP decryption for now */ + DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n", + __func__); + zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); + + sc->sc_flags |= ZYD_FLAG_INITONCE; + } + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + zyd_stop_task(pm); + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %s\n", + ether_sprintf(ic->ic_myaddr)); + error = zyd_set_macaddr(sc, ic->ic_myaddr); + if (error != 0) + return; + + /* set basic rates */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500); + else /* assumes 802.11b/g */ + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f); + + /* promiscuous mode */ + zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0); + /* multicast setup */ + zyd_set_multi(sc); + /* set RX filter */ + error = zyd_set_rxfilter(sc); + if (error != 0) + goto fail; + + /* switch radio transmitter ON */ + error = zyd_switch_radio(sc, 1); + if (error != 0) + goto fail; + /* set default BSS channel */ + zyd_set_chan(sc, ic->ic_curchan); + + /* + * Allocate Tx and Rx xfer queues. + */ + zyd_setup_tx_list(sc); + + /* enable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + usb2_transfer_start(sc->sc_xfer[ZYD_BULK_RD]); + usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); + + return; + +fail: zyd_stop_task(pm); + return; +} + +static void +zyd_init(void *priv) +{ + struct zyd_softc *sc = priv; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + ZYD_LOCK(sc); + zyd_queue_command(sc, zyd_init_task, + &sc->sc_synctask[0].hdr, + &sc->sc_synctask[1].hdr); + ZYD_UNLOCK(sc); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); /* start all vap's */ +} + +static void +zyd_stop_task(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + int error; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + /* + * Drain all the transfers, if not already drained: + */ + ZYD_UNLOCK(sc); + usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]); + usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]); + ZYD_LOCK(sc); + + zyd_unsetup_tx_list(sc); + + /* Stop now if the device was never set up */ + if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) + return; + + /* switch radio transmitter OFF */ + error = zyd_switch_radio(sc, 0); + if (error != 0) + goto fail; + /* disable Rx */ + zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0); + /* disable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); + +fail: + return; +} + +static int +zyd_loadfirmware(struct zyd_softc *sc) +{ + struct usb2_device_request req; + size_t size; + u_char *fw; + uint8_t stat; + uint16_t addr; + + if (sc->sc_flags & ZYD_FLAG_FWLOADED) + return (0); + + if (sc->sc_macrev == ZYD_ZD1211) { + fw = (u_char *)zd1211_firmware; + size = sizeof(zd1211_firmware); + } else { + fw = (u_char *)zd1211b_firmware; + size = sizeof(zd1211b_firmware); + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = ZYD_DOWNLOADREQ; + USETW(req.wIndex, 0); + + addr = ZYD_FIRMWARE_START_ADDR; + while (size > 0) { + /* + * When the transfer size is 4096 bytes, it is not + * likely to be able to transfer it. + * The cause is port or machine or chip? + */ + const int mlen = min(size, 64); + + DPRINTF(sc, ZYD_DEBUG_FW, + "loading firmware block: len=%d, addr=0x%x\n", mlen, addr); + + USETW(req.wValue, addr); + USETW(req.wLength, mlen); + if (zyd_do_request(sc, &req, fw) != 0) + return (EIO); + + addr += mlen / 2; + fw += mlen; + size -= mlen; + } + + /* check whether the upload succeeded */ + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ZYD_DOWNLOADSTS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(stat)); + if (zyd_do_request(sc, &req, &stat) != 0) + return (EIO); + + sc->sc_flags |= ZYD_FLAG_FWLOADED; + + return (stat & 0x80) ? (EIO) : (0); +} + +static void +zyd_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); +} + +static void +zyd_scan_start(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + ZYD_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = ZYD_SCAN_START; + zyd_queue_command(sc, zyd_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + ZYD_UNLOCK(sc); +} + +static void +zyd_scan_end(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + ZYD_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = ZYD_SCAN_END; + zyd_queue_command(sc, zyd_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + ZYD_UNLOCK(sc); +} + +static void +zyd_set_channel(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_ifp->if_softc; + + ZYD_LOCK(sc); + /* do it in a process context */ + sc->sc_scan_action = ZYD_SET_CHANNEL; + zyd_queue_command(sc, zyd_scantask, + &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); + ZYD_UNLOCK(sc); +} + +static void +zyd_scantask(struct usb2_proc_msg *pm) +{ + struct zyd_task *task = (struct zyd_task *)pm; + struct zyd_softc *sc = task->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + switch (sc->sc_scan_action) { + case ZYD_SCAN_START: + /* want broadcast address while scanning */ + zyd_set_bssid(sc, ifp->if_broadcastaddr); + break; + + case ZYD_SET_CHANNEL: + zyd_set_chan(sc, ic->ic_curchan); + break; + + default: /* ZYD_SCAN_END */ + /* restore previous bssid */ + zyd_set_bssid(sc, sc->sc_bssid); + break; + } +} + +static void +zyd_queue_command(struct zyd_softc *sc, usb2_proc_callback_t *fn, + struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) +{ + struct zyd_task *task; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + if (usb2_proc_is_gone(&sc->sc_tq)) { + DPRINTF(sc, ZYD_DEBUG_STATE, "proc is gone\n"); + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct zyd_task *) + usb2_proc_msignal(&sc->sc_tq, t0, t1); + + /* Setup callback and softc pointers */ + task->hdr.pm_callback = fn; + task->sc = sc; + + /* + * Init and stop must be synchronous! + */ + if ((fn == zyd_init_task) || (fn == zyd_stop_task)) + usb2_proc_mwait(&sc->sc_tq, t0, t1); +} + +static device_method_t zyd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, zyd_match), + DEVMETHOD(device_attach, zyd_attach), + DEVMETHOD(device_detach, zyd_detach), + + { 0, 0 } +}; + +static driver_t zyd_driver = { + "zyd", + zyd_methods, + sizeof(struct zyd_softc) +}; + +static devclass_t zyd_devclass; + +DRIVER_MODULE(zyd, ushub, zyd_driver, zyd_devclass, NULL, 0); +MODULE_DEPEND(zyd, usb, 1, 1, 1); +MODULE_DEPEND(zyd, wlan, 1, 1, 1); +MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1); diff --git a/sys/dev/usb/wlan/if_zydfw.h b/sys/dev/usb/wlan/if_zydfw.h new file mode 100644 index 0000000..46f5c2a --- /dev/null +++ b/sys/dev/usb/wlan/if_zydfw.h @@ -0,0 +1,1144 @@ +/* + * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that the following conditions are met: + * 1. Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +uint8_t zd1211_firmware[] = { + 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE, + 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96, + 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99, + 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE, + 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A, + 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44, + 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE, + 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC, + 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC, + 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8, + 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, + 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A, + 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90, + 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0, + 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, + 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, + 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A, + 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90, + 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, + 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, + 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02, + 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86, + 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80, + 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65, + 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4, + 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4, + 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A, + 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2, + 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98, + 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86, + 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2, + 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1, + 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92, + 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92, + 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8, + 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, + 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F, + 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, + 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95, + 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82, + 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC, + 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3, + 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3, + 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD, + 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7, + 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0, + 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, + 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, + 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65, + 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, + 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F, + 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96, + 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96, + 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04, + 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97, + 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93, + 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97, + 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65, + 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2, + 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, + 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0, + 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3, + 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65, + 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0, + 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92, + 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC, + 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC, + 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC, + 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC, + 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94, + 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF, + 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF, + 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07, + 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC, + 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95, + 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02, + 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48, + 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93, + 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2, + 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF, + 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93, + 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10, + 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9, + 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44, + 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3, + 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05, + 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03, + 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC, + 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5, + 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05, + 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92, + 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96, + 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93, + 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93, + 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4, + 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95, + 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93, + 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93, + 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3, + 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93, + 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4, + 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2, + 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95, + 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2, + 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, + 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0, + 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99, + 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93, + 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44, + 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93, + 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF, + 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02, + 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95, + 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95, + 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96, + 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2, + 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC, + 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80, + 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F, + 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04, + 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3, + 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0, + 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93, + 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93, + 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9, + 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22, + 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC, + 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE, + 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC, + 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95, + 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF, + 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0, + 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, + 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E, + 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82, + 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82, + 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2, + 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, + 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96, + 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E, + 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1, + 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9, + 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4, + 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D, + 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03, + 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9, + 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82, + 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0, + 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94, + 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5, + 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0, + 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1, + 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3, + 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94, + 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC, + 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0, + 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1, + 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3, + 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92, + 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC, + 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F, + 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7, + 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2, + 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3, + 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94, + 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB, + 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7, + 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3, + 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC, + 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97, + 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1, + 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3, + 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0, + 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96, + 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2, + 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44, + 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4, + 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95, + 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80, + 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, + 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44, + 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, + 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, + 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, + 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, + 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, + 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09, + 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2, + 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2, + 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, + 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, + 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F, + 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2, + 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, + 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93, + 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6, + 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC, + 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80, + 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, + 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46, + 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95, + 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92, + 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05, + 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2, + 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94, + 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC, + 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06, + 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95, + 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96, + 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E, + 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC, + 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92, + 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92, + 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E, + 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E, + 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF, + 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93, + 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93, + 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3, + 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5, + 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E, + 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7, + 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, + 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3, + 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95, + 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96, + 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3, + 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC, + 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3, + 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00, + 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, + 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, + 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3, + 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0, + 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92, + 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4, + 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93, + 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC, + 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F, + 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, + 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC, + 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08, + 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC, + 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC, + 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, + 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3, + 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC, + 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65, + 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83, + 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80, + 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7, + 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, + 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3, + 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3, + 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5, + 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94, + 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3, + 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, + 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4, + 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95, + 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97, + 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92, + 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC, + 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95, + 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92, + 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3, + 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00, + 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42, + 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44, + 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93, + 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E, + 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82, + 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, + 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94, + 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4, + 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E, + 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03, + 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F, + 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92, + 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46, + 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07, + 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9, + 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC, + 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4, + 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2, + 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC, + 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F, + 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, + 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94, + 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4, + 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4, + 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96, + 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99, + 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2, + 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, + 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E, + 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3, + 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, + 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82, + 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6, + 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00, + 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44, + 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98, + 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98, + 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2, + 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44, + 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65, + 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2, + 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93, + 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03, + 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4, + 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC, + 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D, + 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2, + 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44, + 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93, + 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, + 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, + 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93, + 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3, + 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, + 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F, + 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC, + 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F, + 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, + 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80, + 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5, + 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3, + 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45, + 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92, + 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, + 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00, + 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63, + 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99, + 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96, + 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6, + 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96, + 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, + 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F, + 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95, + 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95, + 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, + 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC, + 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94, + 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2, + 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F, + 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC, + 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93, + 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4, + 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC, + 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6, + 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F, + 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, + 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3, + 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, + 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65, + 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99, + 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03, + 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96, + 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5, + 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC, + 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, + 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00, + 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F, + 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, + 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, + 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95, + 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6, + 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6, + 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99, + 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC, + 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8, + 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F, + 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F, + 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F, + 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99, + 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96, + 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6, + 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47, + 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5, + 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99, + 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, + 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, + 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95, + 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E, + 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, + 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, + 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06, + 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0, + 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, + 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E, + 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, + 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92, + 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0, + 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94, + 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, + 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2, + 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF, + 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF, + 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94, + 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4, + 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4, + 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF, + 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6, + 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96, + 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92, + 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6, + 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93, + 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7, + 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7, + 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC, + 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65, + 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, + 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65, + 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, + 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC, + 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94, + 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7, + 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43, + 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98, + 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC, + 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7, + 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC, + 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2, + 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, + 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, + 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93, + 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65, + 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2, + 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96, + 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92, + 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93, + 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93, + 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94, + 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90, + 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00, + 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, + 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2, + 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7, + 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7, + 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * current zd1211b firmware version. + */ +#define ZD1211B_FIRMWARE_VER 4705 + +uint8_t zd1211b_firmware[] = { + 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11, + 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99, + 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4, + 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92, + 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41, + 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00, + 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8, + 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a, + 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef, + 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0, + 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90, + 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98, + 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90, + 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40, + 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93, + 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40, + 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92, + 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f, + 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19, + 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82, + 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07, + 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42, + 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04, + 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, + 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0, + 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b, + 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4, + 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1, + 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee, + 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e, + 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95, + 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3, + 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f, + 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, + 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41, + 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94, + 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec, + 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c, + 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65, + 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11, + 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, + 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41, + 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02, + 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f, + 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec, + 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95, + 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65, + 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19, + 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94, + 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f, + 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09, + 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97, + 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46, + 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e, + 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5, + 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef, + 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09, + 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef, + 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, + 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7, + 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40, + 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07, + 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03, + 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a, + 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef, + 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01, + 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff, + 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11, + 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96, + 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59, + 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93, + 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04, + 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96, + 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1, + 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3, + 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02, + 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95, + 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99, + 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f, + 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0, + 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1, + 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96, + 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3, + 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec, + 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02, + 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec, + 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57, + 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5, + 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00, + 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f, + 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, + 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec, + 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48, + 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22, + 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47, + 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, + 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, + 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf, + 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82, + 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a, + 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0, + 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, + 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec, + 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09, + 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7, + 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0, + 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, + 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02, + 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00, + 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0, + 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98, + 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40, + 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92, + 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c, + 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69, + 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3, + 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42, + 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, + 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94, + 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff, + 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa, + 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b, + 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1, + 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, + 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb, + 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3, + 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02, + 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7, + 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, + 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1, + 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c, + 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10, + 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92, + 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d, + 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec, + 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3, + 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40, + 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40, + 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7, + 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a, + 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1, + 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3, + 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3, + 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, + 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, + 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba, + 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, + 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, + 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, + 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43, + 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, + 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, + 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, + 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02, + 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, + 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe, + 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93, + 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, + 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a, + 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, + 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, + 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd, + 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00, + 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3, + 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41, + 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40, + 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec, + 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96, + 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2, + 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5, + 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98, + 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02, + 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, + 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96, + 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2, + 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43, + 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e, + 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab, + 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4, + 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a, + 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06, + 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a, + 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec, + 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11, + 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7, + 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, + 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b, + 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80, + 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6, + 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41, + 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0, + 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec, + 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, + 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45, + 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a, + 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, + 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92, + 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3, + 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40, + 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03, + 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, + 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3, + 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12, + 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a, + 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a, + 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, + 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, + 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4, + 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb, + 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99, + 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03, + 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3, + 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01, + 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82, + 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5, + 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44, + 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40, + 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08, + 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11, + 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93, + 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03, + 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93, + 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5, + 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f, + 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9, + 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3, + 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8, + 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff, + 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9, + 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05, + 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a, + 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f, + 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f, + 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f, + 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11, + 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, + 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02, + 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65, + 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, + 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e, + 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40, + 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93, + 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11, + 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4, + 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8, + 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96, + 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68, + 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f, + 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, + 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f, + 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80, + 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2, + 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c, + 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec, + 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b, + 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92, + 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40, + 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec, + 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8, + 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c, + 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, + 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02, + 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, + 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f, + 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94, + 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2, + 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40, + 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec, + 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01, + 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f, + 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41, + 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e, + 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5, + 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4, + 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc, + 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2, + 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f, + 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b, + 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95, + 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2, + 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8, + 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11, + 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94, + 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11, + 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5, + 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19, + 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, + 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40, + 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec, + 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3, + 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99, + 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00, + 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f, + 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03, + 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01, + 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99, + 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, + 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0, + 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42, + 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65, + 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, + 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, + 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63, + 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2, + 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01, + 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04, + 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1, + 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc, + 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03, + 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4, + 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f, + 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f, + 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec, + 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1, + 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47, + 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a, + 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00, + 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, + 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03, + 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02, + 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2, + 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3, + 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0, + 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22, + 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f, + 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8, + 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00, + 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08, + 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4, + 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1, + 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff, + 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c, + 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6, + 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02, + 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93, + 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff, + 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63, + 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1, + 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6, + 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, + 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40, + 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2, + 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91, + 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f, + 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, + 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, + 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, + 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a, + 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, + 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4, + 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43, + 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1, + 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3, + 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19, + 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff, + 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02, + 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, + 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15, + 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65, + 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92, + 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09, + 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e, + 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94, + 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90, + 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00, + 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00, + 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f, + 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/sys/dev/usb/wlan/if_zydreg.h b/sys/dev/usb/wlan/if_zydreg.h new file mode 100644 index 0000000..8ef34e3 --- /dev/null +++ b/sys/dev/usb/wlan/if_zydreg.h @@ -0,0 +1,1338 @@ +/* $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_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */ +#define ZYD_MACB_TXPWR_CTL1 0x9b00 +#define ZYD_MACB_TXPWR_CTL2 0x9b04 +#define ZYD_MACB_TXPWR_CTL3 0x9b08 +#define ZYD_MACB_TXPWR_CTL4 0x9b0c +#define ZYD_MACB_AIFS_CTL1 0x9b10 +#define ZYD_MACB_AIFS_CTL2 0x9b14 +#define ZYD_MACB_TXOP 0x9b20 +#define ZYD_MACB_MAX_RETRY 0x9b28 + +/* + * Miscellanous registers. + */ +#define ZYD_FIRMWARE_START_ADDR 0xee00 +#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */ + +/* + * EEPROM registers. + */ +#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */ +#define ZYD_EEPROM_SUBID 0xf817 +#define ZYD_EEPROM_POD 0xf819 +#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */ +#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */ +#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */ +#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */ +#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */ +#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ +#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */ +#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */ +#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */ +#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */ +#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */ +#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */ +#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */ + +/* + * Firmware registers offsets (relative to fwbase). + */ +#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */ +#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */ +#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */ +#define ZYD_FW_LINK_STATUS 0x0003 +#define ZYD_FW_SOFT_RESET 0x0004 +#define ZYD_FW_FLASH_CHK 0x0005 + +/* possible flags for register ZYD_FW_LINK_STATUS */ +#define ZYD_LED1 (1 << 8) +#define ZYD_LED2 (1 << 9) + +/* + * RF IDs. + */ +#define ZYD_RF_UW2451 0x2 /* not supported yet */ +#define ZYD_RF_UCHIP 0x3 /* not supported yet */ +#define ZYD_RF_AL2230 0x4 +#define ZYD_RF_AL7230B 0x5 +#define ZYD_RF_THETA 0x6 /* not supported yet */ +#define ZYD_RF_AL2210 0x7 +#define ZYD_RF_MAXIM_NEW 0x8 +#define ZYD_RF_GCT 0x9 +#define ZYD_RF_AL2230S 0xa /* not supported yet */ +#define ZYD_RF_RALINK 0xb /* not supported yet */ +#define ZYD_RF_INTERSIL 0xc /* not supported yet */ +#define ZYD_RF_RFMD 0xd +#define ZYD_RF_MAXIM_NEW2 0xe +#define ZYD_RF_PHILIPS 0xf /* not supported yet */ + +/* + * PHY registers (8 bits, not documented). + */ +#define ZYD_CR0 0x9000 +#define ZYD_CR1 0x9004 +#define ZYD_CR2 0x9008 +#define ZYD_CR3 0x900c +#define ZYD_CR5 0x9010 +#define ZYD_CR6 0x9014 +#define ZYD_CR7 0x9018 +#define ZYD_CR8 0x901c +#define ZYD_CR4 0x9020 +#define ZYD_CR9 0x9024 +#define ZYD_CR10 0x9028 +#define ZYD_CR11 0x902c +#define ZYD_CR12 0x9030 +#define ZYD_CR13 0x9034 +#define ZYD_CR14 0x9038 +#define ZYD_CR15 0x903c +#define ZYD_CR16 0x9040 +#define ZYD_CR17 0x9044 +#define ZYD_CR18 0x9048 +#define ZYD_CR19 0x904c +#define ZYD_CR20 0x9050 +#define ZYD_CR21 0x9054 +#define ZYD_CR22 0x9058 +#define ZYD_CR23 0x905c +#define ZYD_CR24 0x9060 +#define ZYD_CR25 0x9064 +#define ZYD_CR26 0x9068 +#define ZYD_CR27 0x906c +#define ZYD_CR28 0x9070 +#define ZYD_CR29 0x9074 +#define ZYD_CR30 0x9078 +#define ZYD_CR31 0x907c +#define ZYD_CR32 0x9080 +#define ZYD_CR33 0x9084 +#define ZYD_CR34 0x9088 +#define ZYD_CR35 0x908c +#define ZYD_CR36 0x9090 +#define ZYD_CR37 0x9094 +#define ZYD_CR38 0x9098 +#define ZYD_CR39 0x909c +#define ZYD_CR40 0x90a0 +#define ZYD_CR41 0x90a4 +#define ZYD_CR42 0x90a8 +#define ZYD_CR43 0x90ac +#define ZYD_CR44 0x90b0 +#define ZYD_CR45 0x90b4 +#define ZYD_CR46 0x90b8 +#define ZYD_CR47 0x90bc +#define ZYD_CR48 0x90c0 +#define ZYD_CR49 0x90c4 +#define ZYD_CR50 0x90c8 +#define ZYD_CR51 0x90cc +#define ZYD_CR52 0x90d0 +#define ZYD_CR53 0x90d4 +#define ZYD_CR54 0x90d8 +#define ZYD_CR55 0x90dc +#define ZYD_CR56 0x90e0 +#define ZYD_CR57 0x90e4 +#define ZYD_CR58 0x90e8 +#define ZYD_CR59 0x90ec +#define ZYD_CR60 0x90f0 +#define ZYD_CR61 0x90f4 +#define ZYD_CR62 0x90f8 +#define ZYD_CR63 0x90fc +#define ZYD_CR64 0x9100 +#define ZYD_CR65 0x9104 +#define ZYD_CR66 0x9108 +#define ZYD_CR67 0x910c +#define ZYD_CR68 0x9110 +#define ZYD_CR69 0x9114 +#define ZYD_CR70 0x9118 +#define ZYD_CR71 0x911c +#define ZYD_CR72 0x9120 +#define ZYD_CR73 0x9124 +#define ZYD_CR74 0x9128 +#define ZYD_CR75 0x912c +#define ZYD_CR76 0x9130 +#define ZYD_CR77 0x9134 +#define ZYD_CR78 0x9138 +#define ZYD_CR79 0x913c +#define ZYD_CR80 0x9140 +#define ZYD_CR81 0x9144 +#define ZYD_CR82 0x9148 +#define ZYD_CR83 0x914c +#define ZYD_CR84 0x9150 +#define ZYD_CR85 0x9154 +#define ZYD_CR86 0x9158 +#define ZYD_CR87 0x915c +#define ZYD_CR88 0x9160 +#define ZYD_CR89 0x9164 +#define ZYD_CR90 0x9168 +#define ZYD_CR91 0x916c +#define ZYD_CR92 0x9170 +#define ZYD_CR93 0x9174 +#define ZYD_CR94 0x9178 +#define ZYD_CR95 0x917c +#define ZYD_CR96 0x9180 +#define ZYD_CR97 0x9184 +#define ZYD_CR98 0x9188 +#define ZYD_CR99 0x918c +#define ZYD_CR100 0x9190 +#define ZYD_CR101 0x9194 +#define ZYD_CR102 0x9198 +#define ZYD_CR103 0x919c +#define ZYD_CR104 0x91a0 +#define ZYD_CR105 0x91a4 +#define ZYD_CR106 0x91a8 +#define ZYD_CR107 0x91ac +#define ZYD_CR108 0x91b0 +#define ZYD_CR109 0x91b4 +#define ZYD_CR110 0x91b8 +#define ZYD_CR111 0x91bc +#define ZYD_CR112 0x91c0 +#define ZYD_CR113 0x91c4 +#define ZYD_CR114 0x91c8 +#define ZYD_CR115 0x91cc +#define ZYD_CR116 0x91d0 +#define ZYD_CR117 0x91d4 +#define ZYD_CR118 0x91d8 +#define ZYD_CR119 0x91dc +#define ZYD_CR120 0x91e0 +#define ZYD_CR121 0x91e4 +#define ZYD_CR122 0x91e8 +#define ZYD_CR123 0x91ec +#define ZYD_CR124 0x91f0 +#define ZYD_CR125 0x91f4 +#define ZYD_CR126 0x91f8 +#define ZYD_CR127 0x91fc +#define ZYD_CR128 0x9200 +#define ZYD_CR129 0x9204 +#define ZYD_CR130 0x9208 +#define ZYD_CR131 0x920c +#define ZYD_CR132 0x9210 +#define ZYD_CR133 0x9214 +#define ZYD_CR134 0x9218 +#define ZYD_CR135 0x921c +#define ZYD_CR136 0x9220 +#define ZYD_CR137 0x9224 +#define ZYD_CR138 0x9228 +#define ZYD_CR139 0x922c +#define ZYD_CR140 0x9230 +#define ZYD_CR141 0x9234 +#define ZYD_CR142 0x9238 +#define ZYD_CR143 0x923c +#define ZYD_CR144 0x9240 +#define ZYD_CR145 0x9244 +#define ZYD_CR146 0x9248 +#define ZYD_CR147 0x924c +#define ZYD_CR148 0x9250 +#define ZYD_CR149 0x9254 +#define ZYD_CR150 0x9258 +#define ZYD_CR151 0x925c +#define ZYD_CR152 0x9260 +#define ZYD_CR153 0x9264 +#define ZYD_CR154 0x9268 +#define ZYD_CR155 0x926c +#define ZYD_CR156 0x9270 +#define ZYD_CR157 0x9274 +#define ZYD_CR158 0x9278 +#define ZYD_CR159 0x927c +#define ZYD_CR160 0x9280 +#define ZYD_CR161 0x9284 +#define ZYD_CR162 0x9288 +#define ZYD_CR163 0x928c +#define ZYD_CR164 0x9290 +#define ZYD_CR165 0x9294 +#define ZYD_CR166 0x9298 +#define ZYD_CR167 0x929c +#define ZYD_CR168 0x92a0 +#define ZYD_CR169 0x92a4 +#define ZYD_CR170 0x92a8 +#define ZYD_CR171 0x92ac +#define ZYD_CR172 0x92b0 +#define ZYD_CR173 0x92b4 +#define ZYD_CR174 0x92b8 +#define ZYD_CR175 0x92bc +#define ZYD_CR176 0x92c0 +#define ZYD_CR177 0x92c4 +#define ZYD_CR178 0x92c8 +#define ZYD_CR179 0x92cc +#define ZYD_CR180 0x92d0 +#define ZYD_CR181 0x92d4 +#define ZYD_CR182 0x92d8 +#define ZYD_CR183 0x92dc +#define ZYD_CR184 0x92e0 +#define ZYD_CR185 0x92e4 +#define ZYD_CR186 0x92e8 +#define ZYD_CR187 0x92ec +#define ZYD_CR188 0x92f0 +#define ZYD_CR189 0x92f4 +#define ZYD_CR190 0x92f8 +#define ZYD_CR191 0x92fc +#define ZYD_CR192 0x9300 +#define ZYD_CR193 0x9304 +#define ZYD_CR194 0x9308 +#define ZYD_CR195 0x930c +#define ZYD_CR196 0x9310 +#define ZYD_CR197 0x9314 +#define ZYD_CR198 0x9318 +#define ZYD_CR199 0x931c +#define ZYD_CR200 0x9320 +#define ZYD_CR201 0x9324 +#define ZYD_CR202 0x9328 +#define ZYD_CR203 0x932c +#define ZYD_CR204 0x9330 +#define ZYD_CR205 0x9334 +#define ZYD_CR206 0x9338 +#define ZYD_CR207 0x933c +#define ZYD_CR208 0x9340 +#define ZYD_CR209 0x9344 +#define ZYD_CR210 0x9348 +#define ZYD_CR211 0x934c +#define ZYD_CR212 0x9350 +#define ZYD_CR213 0x9354 +#define ZYD_CR214 0x9358 +#define ZYD_CR215 0x935c +#define ZYD_CR216 0x9360 +#define ZYD_CR217 0x9364 +#define ZYD_CR218 0x9368 +#define ZYD_CR219 0x936c +#define ZYD_CR220 0x9370 +#define ZYD_CR221 0x9374 +#define ZYD_CR222 0x9378 +#define ZYD_CR223 0x937c +#define ZYD_CR224 0x9380 +#define ZYD_CR225 0x9384 +#define ZYD_CR226 0x9388 +#define ZYD_CR227 0x938c +#define ZYD_CR228 0x9390 +#define ZYD_CR229 0x9394 +#define ZYD_CR230 0x9398 +#define ZYD_CR231 0x939c +#define ZYD_CR232 0x93a0 +#define ZYD_CR233 0x93a4 +#define ZYD_CR234 0x93a8 +#define ZYD_CR235 0x93ac +#define ZYD_CR236 0x93b0 +#define ZYD_CR240 0x93c0 +#define ZYD_CR241 0x93c4 +#define ZYD_CR242 0x93c8 +#define ZYD_CR243 0x93cc +#define ZYD_CR244 0x93d0 +#define ZYD_CR245 0x93d4 +#define ZYD_CR251 0x93ec +#define ZYD_CR252 0x93f0 +#define ZYD_CR253 0x93f4 +#define ZYD_CR254 0x93f8 +#define ZYD_CR255 0x93fc + +/* copied nearly verbatim from the Linux driver rewrite */ +#define ZYD_DEF_PHY \ +{ \ + { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ + { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \ + { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \ + { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ + { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ + { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \ + { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \ + { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ + { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ + { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ + { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ + { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ + { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ + { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ + { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ + { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ + { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ + { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ + { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ + { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \ + { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \ + { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \ + { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \ + { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \ + { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \ + { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \ + { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \ + { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ + { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \ + { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \ + { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \ + { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ + { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ + { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ + { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \ + { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ + { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ + { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ + { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \ + { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \ + { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \ + { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \ + { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ + { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ + { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ + { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \ + { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \ + { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \ + { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ + { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \ + { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \ + { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \ + { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \ + { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \ + { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \ + { ZYD_CR203, 0x30 }, { 0, 0} \ +} + +#define ZYD_DEF_PHYB \ +{ \ + { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ + { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \ + { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \ + { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ + { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ + { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \ + { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \ + { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ + { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ + { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ + { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ + { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ + { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \ + { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ + { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ + { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ + { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ + { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ + { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ + { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ + { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \ + { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \ + { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \ + { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \ + { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \ + { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \ + { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \ + { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ + { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \ + { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \ + { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \ + { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \ + { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \ + { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \ + { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \ + { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ + { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \ + { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \ + { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ + { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ + { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ + { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ + { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \ + { 0, 0 } \ +} + +#define ZYD_RFMD_PHY \ +{ \ + { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \ + { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \ + { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \ + { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \ + { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \ + { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \ + { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \ + { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \ + { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \ + { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \ + { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \ + { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \ + { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \ + { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ + { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \ + { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \ + { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \ + { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \ + { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \ + { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \ + { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \ +} + +#define ZYD_RFMD_RF \ +{ \ + 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \ + 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \ + 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \ +} + +#define ZYD_RFMD_CHANTABLE \ +{ \ + { 0x181979, 0x1e6666 }, \ + { 0x181989, 0x1e6666 }, \ + { 0x181999, 0x1e6666 }, \ + { 0x1819a9, 0x1e6666 }, \ + { 0x1819b9, 0x1e6666 }, \ + { 0x1819c9, 0x1e6666 }, \ + { 0x1819d9, 0x1e6666 }, \ + { 0x1819e9, 0x1e6666 }, \ + { 0x1819f9, 0x1e6666 }, \ + { 0x181a09, 0x1e6666 }, \ + { 0x181a19, 0x1e6666 }, \ + { 0x181a29, 0x1e6666 }, \ + { 0x181a39, 0x1e6666 }, \ + { 0x181a60, 0x1c0000 } \ +} + +#define ZYD_AL2230_PHY \ +{ \ + { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ + { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ + { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \ + { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \ + { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \ + { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \ + { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \ + { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \ + { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \ + { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \ + { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \ + { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \ + { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \ + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \ +} + +#define ZYD_AL2230_PHY_B \ +{ \ + { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \ + { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \ + { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \ + { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \ + { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \ + { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ + { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ + { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \ + { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \ + { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \ + { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ + { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \ + { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \ + { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \ +} + +#define ZYD_AL2230_PHY_PART1 \ +{ \ + { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \ +} + +#define ZYD_AL2230_PHY_PART2 \ +{ \ + { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \ +} + +#define ZYD_AL2230_PHY_PART3 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ +} + +#define ZYD_AL2230S_PHY_INIT \ +{ \ + { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \ + { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \ + { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \ + { ZYD_CR130, 0x10 } \ +} + +#define ZYD_AL2230_PHY_FINI_PART1 \ +{ \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \ + { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \ +} + +#define ZYD_AL2230_RF_PART1 \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \ +} + +#define ZYD_AL2230_RF_PART2 \ +{ \ + 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \ +} + +#define ZYD_AL2230_RF_PART3 \ +{ \ + 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ +} + +#define ZYD_AL2230_RF_B \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ + 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \ +} + +#define ZYD_AL2230_RF_B_PART1 \ +{ \ + 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \ +} + +#define ZYD_AL2230_RF_B_PART2 \ +{ \ + 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \ + 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \ +} + +#define ZYD_AL2230_RF_B_PART3 \ +{ \ + 0xf01b00, 0xf01e00, 0xf01a00 \ +} + +#define ZYD_AL2230_CHANTABLE \ +{ \ + { 0x03f790, 0x033331, 0x00000d }, \ + { 0x03f790, 0x0b3331, 0x00000d }, \ + { 0x03e790, 0x033331, 0x00000d }, \ + { 0x03e790, 0x0b3331, 0x00000d }, \ + { 0x03f7a0, 0x033331, 0x00000d }, \ + { 0x03f7a0, 0x0b3331, 0x00000d }, \ + { 0x03e7a0, 0x033331, 0x00000d }, \ + { 0x03e7a0, 0x0b3331, 0x00000d }, \ + { 0x03f7b0, 0x033331, 0x00000d }, \ + { 0x03f7b0, 0x0b3331, 0x00000d }, \ + { 0x03e7b0, 0x033331, 0x00000d }, \ + { 0x03e7b0, 0x0b3331, 0x00000d }, \ + { 0x03f7c0, 0x033331, 0x00000d }, \ + { 0x03e7c0, 0x066661, 0x00000d } \ +} + +#define ZYD_AL2230_CHANTABLE_B \ +{ \ + { 0x09efc0, 0x8cccc0, 0xb00000 }, \ + { 0x09efc0, 0x8cccd0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x05efc0, 0x8cccc0, 0xb00000 }, \ + { 0x05efc0, 0x8cccd0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x0defc0, 0x8cccc0, 0xb00000 }, \ + { 0x0defc0, 0x8cccd0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccc0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccd0, 0xb00000 }, \ + { 0x03efc0, 0x8cccc0, 0xb00000 }, \ + { 0x03e7c0, 0x866660, 0xb00000 } \ +} + +#define ZYD_AL7230B_PHY_1 \ +{ \ + { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \ + { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \ + { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \ + { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \ + { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \ + { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \ + { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \ + { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ + { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \ + { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \ + { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \ + { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \ + { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \ + { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \ + { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \ + { ZYD_CR251, 0x2f } \ +} + +#define ZYD_AL7230B_PHY_2 \ +{ \ + { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \ + { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \ +} + +#define ZYD_AL7230B_PHY_3 \ +{ \ + { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \ +} + +#define ZYD_AL7230B_RF_1 \ +{ \ + 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \ + 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \ + 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \ +} + +#define ZYD_AL7230B_RF_2 \ +{ \ + 0xf15d59, 0xf15d5c, 0xf15d58 \ +} + +#define ZYD_AL7230B_RF_SETCHANNEL \ +{ \ + 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \ + 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \ +} + +#define ZYD_AL7230B_CHANTABLE \ +{ \ + { 0x09ec00, 0x8cccc8 }, \ + { 0x09ec00, 0x8cccd8 }, \ + { 0x09ec00, 0x8cccc0 }, \ + { 0x09ec00, 0x8cccd0 }, \ + { 0x05ec00, 0x8cccc8 }, \ + { 0x05ec00, 0x8cccd8 }, \ + { 0x05ec00, 0x8cccc0 }, \ + { 0x05ec00, 0x8cccd0 }, \ + { 0x0dec00, 0x8cccc8 }, \ + { 0x0dec00, 0x8cccd8 }, \ + { 0x0dec00, 0x8cccc0 }, \ + { 0x0dec00, 0x8cccd0 }, \ + { 0x03ec00, 0x8cccc8 }, \ + { 0x03ec00, 0x866660 } \ +} + +#define ZYD_AL2210_PHY \ +{ \ + { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \ + { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \ + { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \ + { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \ + { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \ + { ZYD_CR127, 0x03 } \ +} + +#define ZYD_AL2210_RF \ +{ \ + 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \ + 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \ +} + +#define ZYD_AL2210_CHANTABLE \ +{ \ + 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \ + 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \ + 0x019a80, 0x019b40 \ +} + +#define ZYD_GCT_PHY \ +{ \ + { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \ + { ZYD_CR20, 0x0c }, { ZYD_CR17, 0x65 }, { ZYD_CR34, 0x04 }, \ + { ZYD_CR35, 0x35 }, { ZYD_CR24, 0x20 }, { ZYD_CR9, 0xe0 }, \ + { ZYD_CR127, 0x02 }, { ZYD_CR10, 0x91 }, { ZYD_CR23, 0x7f }, \ + { ZYD_CR27, 0x10 }, { ZYD_CR28, 0x7a }, { ZYD_CR79, 0xb5 }, \ + { ZYD_CR64, 0x80 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 } \ +} + +#define ZYD_GCT_RF \ +{ \ + 0x1f0000, 0x1f0000, 0x1f0200, 0x1f0600, 0x1f8600, 0x1f8600, \ + 0x002050, 0x1f8000, 0x1f8200, 0x1f8600, 0x1c0000, 0x10c458, \ + 0x088e92, 0x187b82, 0x0401b4, 0x140816, 0x0c7000, 0x1c0000, \ + 0x02ccae, 0x128023, 0x0a0000, 0x1a0000, 0x06e380, 0x16cb94, \ + 0x0e1740, 0x014980, 0x116240, 0x090000, 0x192304, 0x05112f, \ + 0x0d54a8, 0x0f8000, 0x1c0008, 0x1c0000, 0x1a0000, 0x1c0008, \ + 0x150000, 0x0c7000, 0x150800, 0x150000 \ +} + +#define ZYD_GCT_CHANTABLE \ +{ \ + 0x1a0000, 0x1a8000, 0x1a4000, 0x1ac000, 0x1a2000, 0x1aa000, \ + 0x1a6000, 0x1ae000, 0x1a1000, 0x1a9000, 0x1a5000, 0x1ad000, \ + 0x1a3000, 0x1ab000 \ +} + +#define ZYD_MAXIM_PHY \ +{ \ + { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ + { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ + { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ + { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe }, \ + { ZYD_CR150, 0x0d } \ +} + +#define ZYD_MAXIM_RF \ +{ \ + 0x00ccd4, 0x030a03, 0x000400, 0x000ca1, 0x010072, 0x018645, \ + 0x004006, 0x0000a7, 0x008258, 0x003fc9, 0x00040a, 0x00000b, \ + 0x00026c \ +} + +#define ZYD_MAXIM_CHANTABLE \ +{ \ + { 0x0ccd4, 0x30a03 }, \ + { 0x22224, 0x00a13 }, \ + { 0x37774, 0x10a13 }, \ + { 0x0ccd4, 0x30a13 }, \ + { 0x22224, 0x00a23 }, \ + { 0x37774, 0x10a23 }, \ + { 0x0ccd4, 0x30a23 }, \ + { 0x22224, 0x00a33 }, \ + { 0x37774, 0x10a33 }, \ + { 0x0ccd4, 0x30a33 }, \ + { 0x22224, 0x00a43 }, \ + { 0x37774, 0x10a43 }, \ + { 0x0ccd4, 0x30a43 }, \ + { 0x199a4, 0x20a53 } \ +} + +#define ZYD_MAXIM2_PHY \ +{ \ + { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ + { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ + { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ + { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \ +} + +#define ZYD_MAXIM2_RF \ +{ \ + 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \ + 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \ +} + +#define ZYD_MAXIM2_CHANTABLE_F \ +{ \ + 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \ + 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \ +} + +#define ZYD_MAXIM2_CHANTABLE \ +{ \ + { 0x33334, 0x10a03 }, \ + { 0x08884, 0x20a13 }, \ + { 0x1ddd4, 0x30a13 }, \ + { 0x33334, 0x10a13 }, \ + { 0x08884, 0x20a23 }, \ + { 0x1ddd4, 0x30a23 }, \ + { 0x33334, 0x10a23 }, \ + { 0x08884, 0x20a33 }, \ + { 0x1ddd4, 0x30a33 }, \ + { 0x33334, 0x10a33 }, \ + { 0x08884, 0x20a43 }, \ + { 0x1ddd4, 0x30a43 }, \ + { 0x33334, 0x10a43 }, \ + { 0x26664, 0x20a53 } \ +} + +/* + * Control pipe requests. + */ +#define ZYD_DOWNLOADREQ 0x30 +#define ZYD_DOWNLOADSTS 0x31 +#define ZYD_READFWDATAREQ 0x32 + +/* possible values for register ZYD_CR_INTERRUPT */ +#define ZYD_HWINT_MASK 0x004f0000 + +/* possible values for register ZYD_MAC_MISC */ +#define ZYD_UNLOCK_PHY_REGS 0x80 + +/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */ +#define ZYD_ENC_SNIFFER 8 + +/* flags for register ZYD_MAC_RXFILTER */ +#define ZYD_FILTER_ASS_REQ (1 << 0) +#define ZYD_FILTER_ASS_RSP (1 << 1) +#define ZYD_FILTER_REASS_REQ (1 << 2) +#define ZYD_FILTER_REASS_RSP (1 << 3) +#define ZYD_FILTER_PRB_REQ (1 << 4) +#define ZYD_FILTER_PRB_RSP (1 << 5) +#define ZYD_FILTER_BCN (1 << 8) +#define ZYD_FILTER_ATIM (1 << 9) +#define ZYD_FILTER_DEASS (1 << 10) +#define ZYD_FILTER_AUTH (1 << 11) +#define ZYD_FILTER_DEAUTH (1 << 12) +#define ZYD_FILTER_PS_POLL (1 << 26) +#define ZYD_FILTER_RTS (1 << 27) +#define ZYD_FILTER_CTS (1 << 28) +#define ZYD_FILTER_ACK (1 << 29) +#define ZYD_FILTER_CFE (1 << 30) +#define ZYD_FILTER_CFE_A (1 << 31) + +/* helpers for register ZYD_MAC_RXFILTER */ +#define ZYD_FILTER_MONITOR 0xffffffff +#define ZYD_FILTER_BSS \ + (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \ + ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \ + ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \ + (0x3 << 6) | \ + ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \ + ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \ + (0x7 << 13) | \ + ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK) +#define ZYD_FILTER_HOSTAP \ + (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \ + ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \ + ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL) + +struct zyd_tx_desc { + uint8_t phy; +#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf) +#define ZYD_TX_PHY_OFDM (1 << 4) +#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */ +#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */ + uint16_t len; + uint8_t flags; +#define ZYD_TX_FLAG_BACKOFF (1 << 0) +#define ZYD_TX_FLAG_MULTICAST (1 << 1) +#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2) +#define ZYD_TX_TYPE_DATA 0 +#define ZYD_TX_TYPE_PS_POLL 1 +#define ZYD_TX_TYPE_MGMT 2 +#define ZYD_TX_TYPE_CTL 3 +#define ZYD_TX_FLAG_WAKEUP (1 << 4) +#define ZYD_TX_FLAG_RTS (1 << 5) +#define ZYD_TX_FLAG_ENCRYPT (1 << 6) +#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7) + uint16_t pktlen; + uint16_t plcp_length; + uint8_t plcp_service; +#define ZYD_PLCP_LENGEXT 0x80 + uint16_t nextlen; +} __packed; + +struct zyd_plcphdr { + uint8_t signal; + uint8_t reserved[2]; + uint16_t service; /* unaligned! */ +} __packed; + +struct zyd_rx_stat { + uint8_t signal_cck; + uint8_t rssi; + uint8_t signal_ofdm; + uint8_t cipher; +#define ZYD_RX_CIPHER_WEP64 1 +#define ZYD_RX_CIPHER_TKIP 2 +#define ZYD_RX_CIPHER_AES 4 +#define ZYD_RX_CIPHER_WEP128 5 +#define ZYD_RX_CIPHER_WEP256 6 +#define ZYD_RX_CIPHER_WEP \ + (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256) + uint8_t flags; +#define ZYD_RX_OFDM (1 << 0) +#define ZYD_RX_TIMEOUT (1 << 1) +#define ZYD_RX_OVERRUN (1 << 2) +#define ZYD_RX_DECRYPTERR (1 << 3) +#define ZYD_RX_BADCRC32 (1 << 4) +#define ZYD_RX_NOT2ME (1 << 5) +#define ZYD_RX_BADCRC16 (1 << 6) +#define ZYD_RX_ERROR (1 << 7) +} __packed; + +/* this structure may be unaligned */ +struct zyd_rx_desc { +#define ZYD_MAX_RXFRAMECNT 3 + uWord len[ZYD_MAX_RXFRAMECNT]; + uWord tag; +#define ZYD_TAG_MULTIFRAME 0x697e +} __packed; + +/* I2C bus alike */ +struct zyd_rfwrite_cmd { + uint16_t code; + uint16_t width; + uint16_t bit[32]; +#define ZYD_RF_IF_LE (1 << 1) +#define ZYD_RF_CLK (1 << 2) +#define ZYD_RF_DATA (1 << 3) +} __packed; + +struct zyd_cmd { + uint16_t code; +#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */ +#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */ +#define ZYD_CMD_RFCFG 0x0023 /* write RF register */ +#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */ +#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */ +#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */ + uint8_t data[64]; +} __packed; + +/* structure for command ZYD_CMD_IOWR */ +struct zyd_pair { + uint16_t reg; +/* helpers macros to read/write 32-bit registers */ +#define ZYD_REG32_LO(reg) (reg) +#define ZYD_REG32_HI(reg) \ + ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1)) + uint16_t val; +} __packed; + +/* structure for notification ZYD_NOTIF_RETRYSTATUS */ +struct zyd_notif_retry { + uint16_t rate; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t count; +} __packed; + +#define ZYD_CONFIG_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 ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ +#define ZYX_MAX_RXBUFSZ \ + ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \ + sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \ + sizeof (struct zyd_rx_desc)) +#define ZYD_TX_DESC_SIZE (sizeof (struct zyd_tx_desc)) + +#define ZYD_RX_LIST_CNT 1 +#define ZYD_TX_LIST_CNT 5 +#define ZYD_CMD_FLAG_READ (1 << 0) +#define ZYD_CMD_FLAG_SENT (1 << 1) + +/* 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_task { + struct usb2_proc_msg hdr; + struct zyd_softc *sc; +}; + +struct zyd_tx_data { + STAILQ_ENTRY(zyd_tx_data) next; + struct zyd_softc *sc; + struct zyd_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, zyd_tx_data) zyd_txdhead; + +struct zyd_rx_data { + struct mbuf *m; + int rssi; +}; + +struct zyd_node { + struct ieee80211_node ni; /* must be the first */ + struct ieee80211_amrr_node amn; +}; +#define ZYD_NODE(ni) ((struct zyd_node *)(ni)) + +struct zyd_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; +} __packed; + +#define ZYD_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct zyd_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define ZYD_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct zyd_softc; /* forward declaration */ + +struct zyd_rf { + /* RF methods */ + int (*init)(struct zyd_rf *); + int (*switch_radio)(struct zyd_rf *, int); + int (*set_channel)(struct zyd_rf *, uint8_t); + int (*bandedge6)(struct zyd_rf *, + struct ieee80211_channel *); + /* RF attributes */ + struct zyd_softc *rf_sc; /* back-pointer */ + int width; +}; + +struct zyd_rq { + struct zyd_cmd *cmd; + const uint16_t *idata; + struct zyd_pair *odata; + int ilen; + int olen; + int flags; + STAILQ_ENTRY(zyd_rq) rq; +}; + +struct zyd_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + struct ieee80211_amrr amrr; +}; +#define ZYD_VAP(vap) ((struct zyd_vap *)(vap)) + +enum { + ZYD_BULK_WR, + ZYD_BULK_RD, + ZYD_INTR_WR, + ZYD_INTR_RD, + ZYD_N_TRANSFER = 4, +}; + +struct zyd_softc { + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct usb2_process sc_tq; + + struct usb2_xfer *sc_xfer[ZYD_N_TRANSFER]; + + enum ieee80211_state sc_state; + int sc_arg; + int sc_flags; +#define ZYD_FLAG_FWLOADED (1 << 0) +#define ZYD_FLAG_INITONCE (1 << 1) +#define ZYD_FLAG_INITDONE (1 << 2) + int sc_if_flags; + + struct zyd_task sc_synctask[2]; + struct zyd_task sc_mcasttask[2]; + struct zyd_task sc_scantask[2]; + int sc_scan_action; +#define ZYD_SCAN_START 0 +#define ZYD_SCAN_END 1 +#define ZYD_SET_CHANNEL 2 + struct zyd_task sc_task[2]; + + struct zyd_rf sc_rf; + + STAILQ_HEAD(, zyd_rq) sc_rtx; + STAILQ_HEAD(, zyd_rq) sc_rqh; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + uint16_t sc_fwbase; + uint8_t sc_regdomain; + uint8_t sc_macrev; + uint16_t sc_fwrev; + uint8_t sc_rfrev; + uint8_t sc_parev; + uint8_t sc_al2230s; + uint8_t sc_bandedge6; + uint8_t sc_newphy; + uint8_t sc_cckgain; + uint8_t sc_fix_cr157; + uint8_t sc_ledtype; + uint8_t sc_txled; + + uint32_t sc_atim_wnd; + uint32_t sc_pre_tbtt; + uint32_t sc_bcn_int; + + uint8_t sc_pwrcal[14]; + uint8_t sc_pwrint[14]; + uint8_t sc_ofdm36_cal[14]; + uint8_t sc_ofdm48_cal[14]; + uint8_t sc_ofdm54_cal[14]; + + struct mtx sc_mtx; + struct cv sc_intr_cv; + struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT]; + zyd_txdhead tx_q; + zyd_txdhead tx_free; + int tx_nfree; + struct zyd_rx_desc sc_rx_desc; + struct zyd_rx_data sc_rx_data[ZYD_MAX_RXFRAMECNT]; + int sc_rx_count; + + struct zyd_cmd sc_ibuf; + + struct zyd_rx_radiotap_header sc_rxtap; + int sc_rxtap_len; + struct zyd_tx_radiotap_header sc_txtap; + int sc_txtap_len; +}; + +#define ZYD_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define ZYD_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define ZYD_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) + diff --git a/sys/dev/usb/wlan/usb_wlan.h b/sys/dev/usb/wlan/usb_wlan.h new file mode 100644 index 0000000..9db120e --- /dev/null +++ b/sys/dev/usb/wlan/usb_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/dev/usb2/bluetooth/TODO.TXT b/sys/dev/usb2/bluetooth/TODO.TXT deleted file mode 100644 index b0d6695..0000000 --- a/sys/dev/usb2/bluetooth/TODO.TXT +++ /dev/null @@ -1,18 +0,0 @@ -$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 deleted file mode 100644 index 3b1ba31..0000000 --- a/sys/dev/usb2/bluetooth/ng_ubt2.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * ng_ubt.c - */ - -/*- - * Copyright (c) 2001-2009 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$ - */ - -/* - * NOTE: ng_ubt2 driver has a split personality. On one side it is - * a USB device driver and on the other it is a Netgraph node. This - * driver will *NOT* create traditional /dev/ enties, only Netgraph - * node. - * - * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes) - * - * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used - * by USB for any USB request going over device's interface #0 and #1, - * i.e. interrupt, control, bulk and isoc. transfers. - * - * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph - * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook - * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact, - * think of it as a spin lock. - * - * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. - * - * 1) USB context. This is where all the USB related stuff happens. All - * callbacks run in this context. All callbacks are called (by USB) with - * appropriate interface lock held. It is (generally) allowed to grab - * any additional locks. - * - * 2) Netgraph context. This is where all the Netgraph related stuff happens. - * Since we mark node as WRITER, the Netgraph node will be "locked" (from - * Netgraph point of view). Any variable that is only modified from the - * Netgraph context does not require any additonal locking. It is generally - * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT* - * grab any lock in the Netgraph context that could cause de-scheduling of - * the Netgraph thread for significant amount of time. In fact, the only - * lock that is allowed in the Netgraph context is the sc_ng_mtx lock. - * Also make sure that any code that is called from the Netgraph context - * follows the rule above. - * - * 3) Taskqueue context. This is where ubt_task runs. Since we are generally - * NOT allowed to grab any lock that could cause de-scheduling in the - * Netgraph context, and, USB requires us to grab interface lock before - * doing things with transfers, it is safer to transition from the Netgraph - * context to the Taskqueue context before we can call into USB subsystem. - * - * So, to put everything together, the rules are as follows. - * It is OK to call from the USB context or the Taskqueue context into - * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words - * it is allowed to call into the Netgraph context with locks held. - * Is it *NOT* OK to call from the Netgraph context into the USB context, - * because USB requires us to grab interface locks, and, it is safer to - * avoid it. So, to make things safer we set task flags to indicate which - * actions we want to perform and schedule ubt_task which would run in the - * Taskqueue context. - * Is is OK to call from the Taskqueue context into the USB context, - * and, ubt_task does just that (i.e. grabs appropriate interface locks - * before calling into USB). - * Access to the outgoing queues, task flags and hook pointer is - * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again, - * sc_ng_mtx should really be a spin lock (and it is very likely to an - * equivalent of spin lock due to adaptive nature of freebsd mutexes). - * All USB callbacks accept softc pointer as a private data. USB ensures - * that this pointer is valid. - */ - -#include "usbdevs.h" -#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 -#include - -static int ubt_modevent(module_t, int, void *); -static device_probe_t ubt_probe; -static device_attach_t ubt_attach; -static device_detach_t ubt_detach; - -static void ubt_task_schedule(ubt_softc_p, int); -static task_fn_t ubt_task; - -#define ubt_xfer_start(sc, i) usb2_transfer_start((sc)->sc_xfer[(i)]) - -/* 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 specific - **************************************************************************** - ****************************************************************************/ - -/* USB methods */ -static usb2_callback_t ubt_ctrl_write_callback; -static usb2_callback_t ubt_intr_read_callback; -static usb2_callback_t ubt_bulk_read_callback; -static usb2_callback_t ubt_bulk_write_callback; -static usb2_callback_t ubt_isoc_read_callback; -static usb2_callback_t ubt_isoc_write_callback; - -static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **); -static int ubt_isoc_read_one_frame(struct usb2_xfer *, int); - -/* - * USB config - * - * The following desribes usb transfers that could be submitted on USB device. - * - * Interface 0 on the USB device must present the following endpoints - * 1) Interrupt endpoint to receive HCI events - * 2) Bulk IN endpoint to receive ACL data - * 3) Bulk OUT endpoint to send ACL data - * - * Interface 1 on the USB device must present the following endpoints - * 1) Isochronous IN endpoint to receive SCO data - * 2) Isochronous OUT endpoint to send SCO data - */ - -static const struct usb2_config ubt_config[UBT_N_TRANSFER] = -{ - /* - * Interface #0 - */ - - /* Outgoing bulk transfer - ACL packets */ - [UBT_IF_0_BULK_DT_WR] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .if_index = 0, - .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, - .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, }, - .mh.callback = &ubt_bulk_write_callback, - }, - /* Incoming bulk transfer - ACL packets */ - [UBT_IF_0_BULK_DT_RD] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 0, - .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, - .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, - .mh.callback = &ubt_bulk_read_callback, - }, - /* Incoming interrupt transfer - HCI events */ - [UBT_IF_0_INTR_DT_RD] = { - .type = UE_INTERRUPT, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 0, - .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, - .mh.bufsize = UBT_INTR_BUFFER_SIZE, - .mh.callback = &ubt_intr_read_callback, - }, - /* Outgoing control transfer - HCI commands */ - [UBT_IF_0_CTRL_DT_WR] = { - .type = UE_CONTROL, - .endpoint = 0x00, /* control pipe */ - .direction = UE_DIR_ANY, - .if_index = 0, - .mh.bufsize = UBT_CTRL_BUFFER_SIZE, - .mh.callback = &ubt_ctrl_write_callback, - .mh.timeout = 5000, /* 5 seconds */ - }, - - /* - * Interface #1 - */ - - /* Incoming isochronous transfer #1 - SCO packets */ - [UBT_IF_1_ISOC_DT_RD1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 1, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = { .short_xfer_ok = 1, }, - .mh.callback = &ubt_isoc_read_callback, - }, - /* Incoming isochronous transfer #2 - SCO packets */ - [UBT_IF_1_ISOC_DT_RD2] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 1, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = { .short_xfer_ok = 1, }, - .mh.callback = &ubt_isoc_read_callback, - }, - /* Outgoing isochronous transfer #1 - SCO packets */ - [UBT_IF_1_ISOC_DT_WR1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .if_index = 1, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = { .short_xfer_ok = 1, }, - .mh.callback = &ubt_isoc_write_callback, - }, - /* Outgoing isochronous transfer #2 - SCO packets */ - [UBT_IF_1_ISOC_DT_WR2] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .if_index = 1, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = { .short_xfer_ok = 1, }, - .mh.callback = &ubt_isoc_write_callback, - }, -}; - -/* - * If for some reason device should not be attached then put - * VendorID/ProductID pair into the list below. The format is - * as follows: - * - * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, - * - * 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. - * USB context. - */ - -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 (uaa->use_generic == 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)); -} /* ubt_probe */ - -/* - * Attach the device. - * USB context. - */ - -static int -ubt_attach(device_t dev) -{ - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct ubt_softc *sc = device_get_softc(dev); - struct usb2_endpoint_descriptor *ed; - uint16_t wMaxPacketSize; - uint8_t alt_index, i, j; - uint8_t iface_index[2] = { 0, 1 }; - - device_set_usb2_desc(dev); - - sc->sc_dev = dev; - sc->sc_debug = NG_UBT_WARN_LEVEL; - - /* - * Create Netgraph node - */ - - if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { - UBT_ALERT(sc, "could not create Netgraph node\n"); - return (ENXIO); - } - - /* Name Netgraph node */ - if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) { - UBT_ALERT(sc, "could not name Netgraph node\n"); - NG_NODE_UNREF(sc->sc_node); - return (ENXIO); - } - NG_NODE_SET_PRIVATE(sc->sc_node, sc); - NG_NODE_FORCE_WRITER(sc->sc_node); - - /* - * Initialize device softc structure - */ - - /* initialize locks */ - mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF); - mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE); - - /* initialize packet queues */ - NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); - NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); - NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); - - /* initialize glue task */ - TASK_INIT(&sc->sc_task, 0, ubt_task, sc); - - /* - * 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. - */ - - /* - * For interface #1 search alternate settings, and find - * the descriptor with the largest wMaxPacketSize - */ - - wMaxPacketSize = 0; - alt_index = 0; - i = 0; - j = 0; - - /* Search through all the descriptors looking for bidir mode */ - while (1) { - uint16_t temp; - - ed = usb2_find_edesc(usb2_get_config_descriptor(uaa->device), - 1, i, j); - if (ed == NULL) { - if (j != 0) { - /* next interface */ - j = 0; - i ++; - continue; - } - - break; /* end of interfaces */ - } - - temp = UGETW(ed->wMaxPacketSize); - if (temp > wMaxPacketSize) { - wMaxPacketSize = temp; - alt_index = i; - } - - j ++; - } - - /* Set alt configuration on interface #1 only if we found it */ - if (wMaxPacketSize > 0 && - usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { - UBT_ALERT(sc, "could not set alternate setting %d " \ - "for interface 1!\n", alt_index); - goto detach; - } - - /* Setup transfers for both interfaces */ - if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer, - ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) { - UBT_ALERT(sc, "could not allocate transfers\n"); - goto detach; - } - - /* Claim all interfaces on the device */ - for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++) - usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); - - return (0); /* success */ - -detach: - ubt_detach(dev); - - return (ENXIO); -} /* ubt_attach */ - -/* - * Detach the device. - * USB context. - */ - -int -ubt_detach(device_t dev) -{ - struct ubt_softc *sc = device_get_softc(dev); - node_p node = sc->sc_node; - - /* Destroy Netgraph node */ - if (node != NULL) { - sc->sc_node = NULL; - NG_NODE_REALLY_DIE(node); - ng_rmnode_self(node); - } - - /* Make sure ubt_task in gone */ - taskqueue_drain(taskqueue_swi, &sc->sc_task); - - /* Free USB transfers, if any */ - usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); - - /* Destroy queues */ - UBT_NG_LOCK(sc); - NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); - NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); - NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); - UBT_NG_UNLOCK(sc); - - mtx_destroy(&sc->sc_if_mtx); - mtx_destroy(&sc->sc_ng_mtx); - - return (0); -} /* ubt_detach */ - -/* - * Called when outgoing control request (HCI command) has completed, i.e. - * HCI command was sent to the device. - * USB context. - */ - -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: - UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - /* FALLTHROUGH */ - - case USB_ST_SETUP: -send_next: - /* Get next command mbuf, if any */ - UBT_NG_LOCK(sc); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); - UBT_NG_UNLOCK(sc); - - if (m == NULL) { - UBT_INFO(sc, "HCI command queue is empty\n"); - break; /* transfer complete */ - } - - /* 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); - - 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 = 2; - - NG_FREE_M(m); - - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_WARN(sc, "control transfer failed: %s\n", - usb2_errstr(xfer->error)); - - UBT_STAT_OERROR(sc); - goto send_next; - } - - /* transfer cancelled */ - break; - } -} /* ubt_ctrl_write_callback */ - -/* - * Called when incoming interrupt transfer (HCI event) has completed, i.e. - * HCI event was received from the device. - * USB context. - */ - -static void -ubt_intr_read_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - ng_hci_event_pkt_t *hdr; - - m = NULL; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - /* Allocate a new mbuf */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - UBT_STAT_IERROR(sc); - goto submit_next; - } - - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - UBT_STAT_IERROR(sc); - goto submit_next; - } - - /* Add HCI packet type */ - *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; - m->m_pkthdr.len = m->m_len = 1; - - if (xfer->actlen > MCLBYTES - 1) - xfer->actlen = MCLBYTES - 1; - - usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, - xfer->actlen); - m->m_pkthdr.len += xfer->actlen; - m->m_len += xfer->actlen; - - UBT_INFO(sc, "got %d bytes from interrupt pipe\n", - xfer->actlen); - - /* Validate packet and send it up the stack */ - if (m->m_pkthdr.len < sizeof(*hdr)) { - UBT_INFO(sc, "HCI event packet is too short\n"); - - UBT_STAT_IERROR(sc); - goto submit_next; - } - - hdr = mtod(m, ng_hci_event_pkt_t *); - if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { - UBT_ERR(sc, "Invalid HCI event packet size, " \ - "length=%d, pktlen=%d\n", - hdr->length, m->m_pkthdr.len); - - UBT_STAT_IERROR(sc); - goto submit_next; - } - - UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ - "length=%d\n", m->m_pkthdr.len, hdr->length); - - UBT_STAT_PCKTS_RECV(sc); - UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - - ubt_fwd_mbuf_up(sc, &m); - /* m == NULL at this point */ - /* FALLTHROUGH */ - - case USB_ST_SETUP: -submit_next: - NG_FREE_M(m); /* checks for m != NULL */ - - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_WARN(sc, "interrupt transfer failed: %s\n", - usb2_errstr(xfer->error)); - - /* Try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto submit_next; - } - /* transfer cancelled */ - break; - } -} /* ubt_intr_read_callback */ - -/* - * Called when incoming bulk transfer (ACL packet) has completed, i.e. - * ACL packet was received from the device. - * USB context. - */ - -static void -ubt_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - ng_hci_acldata_pkt_t *hdr; - uint16_t len; - - m = NULL; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - /* Allocate new mbuf */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - UBT_STAT_IERROR(sc); - goto submit_next; - } - - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - UBT_STAT_IERROR(sc); - goto submit_next; - } - - /* Add HCI packet type */ - *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; - m->m_pkthdr.len = m->m_len = 1; - - if (xfer->actlen > MCLBYTES - 1) - xfer->actlen = MCLBYTES - 1; - - usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, - xfer->actlen); - m->m_pkthdr.len += xfer->actlen; - m->m_len += xfer->actlen; - - UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", - xfer->actlen); - - /* Validate packet and send it up the stack */ - if (m->m_pkthdr.len < sizeof(*hdr)) { - UBT_INFO(sc, "HCI ACL packet is too short\n"); - - UBT_STAT_IERROR(sc); - goto submit_next; - } - - hdr = mtod(m, ng_hci_acldata_pkt_t *); - len = le16toh(hdr->length); - if (len != (m->m_pkthdr.len - sizeof(*hdr))) { - UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ - "pktlen=%d\n", len, m->m_pkthdr.len); - - UBT_STAT_IERROR(sc); - goto submit_next; - } - - UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ - "length=%d\n", m->m_pkthdr.len, len); - - UBT_STAT_PCKTS_RECV(sc); - UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - - ubt_fwd_mbuf_up(sc, &m); - /* m == NULL at this point */ - /* FALLTHOUGH */ - - case USB_ST_SETUP: -submit_next: - NG_FREE_M(m); /* checks for m != NULL */ - - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_WARN(sc, "bulk-in transfer failed: %s\n", - usb2_errstr(xfer->error)); - - /* Try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto submit_next; - } - /* transfer cancelled */ - break; - } -} /* ubt_bulk_read_callback */ - -/* - * Called when outgoing bulk transfer (ACL packet) has completed, i.e. - * ACL packet was sent to the device. - * USB context. - */ - -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: - UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - /* FALLTHROUGH */ - - case USB_ST_SETUP: -send_next: - /* Get next mbuf, if any */ - UBT_NG_LOCK(sc); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); - UBT_NG_UNLOCK(sc); - - if (m == NULL) { - UBT_INFO(sc, "ACL data queue is empty\n"); - break; /* transfer completed */ - } - - /* - * Copy ACL data frame back to a linear USB transfer buffer - * and schedule transfer - */ - - usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); - xfer->frlengths[0] = m->m_pkthdr.len; - - UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", - m->m_pkthdr.len); - - NG_FREE_M(m); - - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_WARN(sc, "bulk-out transfer failed: %s\n", - usb2_errstr(xfer->error)); - - UBT_STAT_OERROR(sc); - - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto send_next; - } - /* transfer cancelled */ - break; - } -} /* ubt_bulk_write_callback */ - -/* - * Called when incoming isoc transfer (SCO packet) has completed, i.e. - * SCO packet was received from the device. - * USB context. - */ - -static void -ubt_isoc_read_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - int n; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - for (n = 0; n < xfer->nframes; n ++) - if (ubt_isoc_read_one_frame(xfer, n) < 0) - break; - /* FALLTHROUGH */ - - case USB_ST_SETUP: -read_next: - for (n = 0; n < xfer->nframes; n ++) - xfer->frlengths[n] = xfer->max_frame_size; - - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_STAT_IERROR(sc); - goto read_next; - } - - /* transfer cancelled */ - break; - } -} /* ubt_isoc_read_callback */ - -/* - * Helper function. Called from ubt_isoc_read_callback() to read - * SCO data from one frame. - * USB context. - */ - -static int -ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - int len, want, got; - - /* Get existing SCO reassembly buffer */ - m = sc->sc_isoc_in_buffer; - sc->sc_isoc_in_buffer = NULL; - - /* While we have data in the frame */ - while ((len = xfer->frlengths[frame_no]) > 0) { - if (m == NULL) { - /* Start new reassembly buffer */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - UBT_STAT_IERROR(sc); - return (-1); /* XXX out of sync! */ - } - - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - UBT_STAT_IERROR(sc); - NG_FREE_M(m); - return (-1); /* XXX out of sync! */ - } - - /* Expect SCO header */ - *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; - m->m_pkthdr.len = m->m_len = got = 1; - want = sizeof(ng_hci_scodata_pkt_t); - } else { - /* - * Check if we have SCO header and if so - * adjust amount of data we want - */ - got = m->m_pkthdr.len; - want = sizeof(ng_hci_scodata_pkt_t); - - if (got >= want) - want += mtod(m, ng_hci_scodata_pkt_t *)->length; - } - - /* Append frame data to the SCO reassembly buffer */ - if (got + len > want) - len = want - got; - - usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, - mtod(m, uint8_t *) + m->m_pkthdr.len, len); - - m->m_pkthdr.len += len; - m->m_len += len; - xfer->frlengths[frame_no] -= len; - - /* Check if we got everything we wanted, if not - continue */ - if (got != want) - continue; - - /* If we got here then we got complete SCO frame */ - UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ - "length=%d\n", m->m_pkthdr.len, - mtod(m, ng_hci_scodata_pkt_t *)->length); - - UBT_STAT_PCKTS_RECV(sc); - UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - - ubt_fwd_mbuf_up(sc, &m); - /* m == NULL at this point */ - } - - /* Put SCO reassembly buffer back */ - sc->sc_isoc_in_buffer = m; - - return (0); -} /* ubt_isoc_read_one_frame */ - -/* - * Called when outgoing isoc transfer (SCO packet) has completed, i.e. - * SCO packet was sent to the device. - * USB context. - */ - -static void -ubt_isoc_write_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - int n, space, offset; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen); - UBT_STAT_BYTES_SENT(sc, xfer->actlen); - UBT_STAT_PCKTS_SENT(sc); - /* FALLTHROUGH */ - - case USB_ST_SETUP: -send_next: - offset = 0; - space = xfer->max_frame_size * xfer->nframes; - m = NULL; - - while (space > 0) { - if (m == NULL) { - UBT_NG_LOCK(sc); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); - UBT_NG_UNLOCK(sc); - - if (m == NULL) - break; - } - - n = min(space, m->m_pkthdr.len); - if (n > 0) { - usb2_m_copy_in(xfer->frbuffers, offset, m,0, n); - m_adj(m, n); - - offset += n; - space -= n; - } - - if (m->m_pkthdr.len == 0) - NG_FREE_M(m); /* sets m = NULL */ - } - - /* Put whatever is left from mbuf back on queue */ - if (m != NULL) { - UBT_NG_LOCK(sc); - NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); - UBT_NG_UNLOCK(sc); - } - - /* - * Calculate sizes for isoc frames. - * Note that offset could be 0 at this point (i.e. we have - * nothing to send). That is fine, as we have isoc. transfers - * going in both directions all the time. In this case it - * would be just empty isoc. transfer. - */ - - for (n = 0; n < xfer->nframes; n ++) { - xfer->frlengths[n] = min(offset, xfer->max_frame_size); - offset -= xfer->frlengths[n]; - } - - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - UBT_STAT_OERROR(sc); - goto send_next; - } - - /* transfer cancelled */ - break; - } -} - -/* - * Utility function to forward provided mbuf upstream (i.e. up the stack). - * Modifies value of the mbuf pointer (sets it to NULL). - * Save to call from any context. - */ - -static int -ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m) -{ - hook_p hook; - int error; - - /* - * Close the race with Netgraph hook newhook/disconnect methods. - * Save the hook pointer atomically. Two cases are possible: - * - * 1) The hook pointer is NULL. It means disconnect method got - * there first. In this case we are done. - * - * 2) The hook pointer is not NULL. It means that hook pointer - * could be either in valid or invalid (i.e. in the process - * of disconnect) state. In any case grab an extra reference - * to protect the hook pointer. - * - * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as - * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY(). - */ - - UBT_NG_LOCK(sc); - if ((hook = sc->sc_hook) != NULL) - NG_HOOK_REF(hook); - UBT_NG_UNLOCK(sc); - - if (hook == NULL) { - NG_FREE_M(*m); - return (ENETDOWN); - } - - NG_SEND_DATA_ONLY(error, hook, *m); - NG_HOOK_UNREF(hook); - - if (error != 0) - UBT_STAT_IERROR(sc); - - return (error); -} /* ubt_fwd_mbuf_up */ - -/**************************************************************************** - **************************************************************************** - ** Glue - **************************************************************************** - ****************************************************************************/ - -/* - * Schedule glue task. Should be called with sc_ng_mtx held. - * Netgraph context. - */ - -static void -ubt_task_schedule(ubt_softc_p sc, int action) -{ - mtx_assert(&sc->sc_ng_mtx, MA_OWNED); - - /* - * Try to handle corner case when "start all" and "stop all" - * actions can both be set before task is executed. - * - * The rules are - * - * sc_task_flags action new sc_task_flags - * ------------------------------------------------------ - * 0 start start - * 0 stop stop - * start start start - * start stop stop - * stop start stop|start - * stop stop stop - * stop|start start stop|start - * stop|start stop stop - */ - - if (action != 0) { - if ((action & UBT_FLAG_T_STOP_ALL) != 0) - sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; - - sc->sc_task_flags |= action; - } - - if (sc->sc_task_flags & UBT_FLAG_T_PENDING) - return; - - if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { - sc->sc_task_flags |= UBT_FLAG_T_PENDING; - return; - } - - /* XXX: i think this should never happen */ -} /* ubt_task_schedule */ - -/* - * Glue task. Examines sc_task_flags and does things depending on it. - * Taskqueue context. - */ - -static void -ubt_task(void *context, int pending) -{ - ubt_softc_p sc = context; - int task_flags, i; - - UBT_NG_LOCK(sc); - task_flags = sc->sc_task_flags; - sc->sc_task_flags = 0; - UBT_NG_UNLOCK(sc); - - /* - * Stop all USB transfers synchronously. - * Stop interface #0 and #1 transfers at the same time and in the - * same loop. usb2_transfer_drain() will do appropriate locking. - */ - - if (task_flags & UBT_FLAG_T_STOP_ALL) - for (i = 0; i < UBT_N_TRANSFER; i ++) - usb2_transfer_drain(sc->sc_xfer[i]); - - /* Start incoming interrupt and bulk, and all isoc. USB transfers */ - if (task_flags & UBT_FLAG_T_START_ALL) { - /* - * Interface #0 - */ - - mtx_lock(&sc->sc_if_mtx); - - ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); - ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); - - /* - * Interface #1 - * Start both read and write isoc. transfers by default. - * Get them going all the time even if we have nothing - * to send to avoid any delays. - */ - - ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); - ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); - ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); - ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); - - mtx_unlock(&sc->sc_if_mtx); - } - - /* Start outgoing control transfer */ - if (task_flags & UBT_FLAG_T_START_CTRL) { - mtx_lock(&sc->sc_if_mtx); - ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); - mtx_unlock(&sc->sc_if_mtx); - } - - /* Start outgoing bulk transfer */ - if (task_flags & UBT_FLAG_T_START_BULK) { - mtx_lock(&sc->sc_if_mtx); - ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); - mtx_unlock(&sc->sc_if_mtx); - } -} /* ubt_task */ - -/**************************************************************************** - **************************************************************************** - ** Netgraph specific - **************************************************************************** - ****************************************************************************/ - -/* - * Netgraph node constructor. Do not allow to create node of this type. - * Netgraph context. - */ - -static int -ng_ubt_constructor(node_p node) -{ - return (EINVAL); -} /* ng_ubt_constructor */ - -/* - * Netgraph node destructor. Destroy node only when device has been detached. - * Netgraph context. - */ - -static int -ng_ubt_shutdown(node_p node) -{ - if (node->nd_flags & NGF_REALLY_DIE) { - /* - * We came here because the USB device is being - * detached, so stop being persistant. - */ - NG_NODE_SET_PRIVATE(node, NULL); - NG_NODE_UNREF(node); - } else - NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ - - return (0); -} /* ng_ubt_shutdown */ - -/* - * Create new hook. There can only be one. - * Netgraph context. - */ - -static int -ng_ubt_newhook(node_p node, hook_p hook, char const *name) -{ - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - - if (strcmp(name, NG_UBT_HOOK) != 0) - return (EINVAL); - - UBT_NG_LOCK(sc); - if (sc->sc_hook != NULL) { - UBT_NG_UNLOCK(sc); - - return (EISCONN); - } - - sc->sc_hook = hook; - UBT_NG_UNLOCK(sc); - - return (0); -} /* ng_ubt_newhook */ - -/* - * Connect hook. Start incoming USB transfers. - * Netgraph context. - */ - -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)); - - UBT_NG_LOCK(sc); - ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); - UBT_NG_UNLOCK(sc); - - return (0); -} /* ng_ubt_connect */ - -/* - * Disconnect hook. - * Netgraph context. - */ - -static int -ng_ubt_disconnect(hook_p hook) -{ - struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - - UBT_NG_LOCK(sc); - - if (hook != sc->sc_hook) { - UBT_NG_UNLOCK(sc); - - return (EINVAL); - } - - sc->sc_hook = NULL; - - /* Kick off task to stop all USB xfers */ - ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); - - /* Drain queues */ - NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); - NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); - NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - - UBT_NG_UNLOCK(sc); - - return (0); -} /* ng_ubt_disconnect */ - -/* - * Process control message. - * Netgraph context. - */ - -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, *rsp = NULL; - struct ng_bt_mbufq *q; - int error = 0, queue, qlen; - - 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; - break; - } - - snprintf(rsp->data, NG_TEXTRESPONSE, - "Hook: %s\n" \ - "Task 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_task_flags, - sc->sc_debug, - sc->sc_cmdq.len, - sc->sc_cmdq.maxlen, - sc->sc_aclq.len, - sc->sc_aclq.maxlen, - sc->sc_scoq.len, - 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; - break; - } - - 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; - break; - } - - *((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; - break; - } - - queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; - qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; - - 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: - error = EINVAL; - goto done; - /* NOT REACHED */ - } - - 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: - error = EINVAL; - goto done; - /* NOT REACHED */ - } - - 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; - break; - } - - bcopy(&sc->sc_stat, rsp->data, - sizeof(ng_ubt_node_stat_ep)); - break; - - case NGM_UBT_NODE_RESET_STAT: - UBT_STAT_RESET(sc); - break; - - default: - error = EINVAL; - break; - } - break; - - default: - error = EINVAL; - break; - } -done: - NG_RESPOND_MSG(error, node, item, rsp); - NG_FREE_MSG(msg); - - return (error); -} /* ng_ubt_rcvmsg */ - -/* - * Process data. - * Netgraph context. - */ - -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; - int action, error = 0; - - if (hook != sc->sc_hook) { - error = EINVAL; - goto done; - } - - /* Deatch mbuf and get HCI frame type */ - NGI_GET_M(item, m); - - /* - * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, - * 2 bytes connection handle and at least 1 byte of length. - * Panic on data frame that has size smaller than 4 bytes (it - * should not happen) - */ - - if (m->m_pkthdr.len < 4) - panic("HCI frame size is too small! pktlen=%d\n", - m->m_pkthdr.len); - - /* Process HCI frame */ - switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ - case NG_HCI_CMD_PKT: - if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) - panic("HCI command frame size is too big! " \ - "buffer size=%zd, packet len=%d\n", - UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); - - q = &sc->sc_cmdq; - action = UBT_FLAG_T_START_CTRL; - break; - - case NG_HCI_ACL_DATA_PKT: - if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) - panic("ACL data frame size is too big! " \ - "buffer size=%d, packet len=%d\n", - UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); - - q = &sc->sc_aclq; - action = UBT_FLAG_T_START_BULK; - break; - - case NG_HCI_SCO_DATA_PKT: - q = &sc->sc_scoq; - action = 0; - break; - - default: - 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; - /* NOT REACHED */ - } - - UBT_NG_LOCK(sc); - if (NG_BT_MBUFQ_FULL(q)) { - NG_BT_MBUFQ_DROP(q); - UBT_NG_UNLOCK(sc); - - 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 { - /* Loose HCI packet type, enqueue mbuf and kick off task */ - m_adj(m, sizeof(uint8_t)); - NG_BT_MBUFQ_ENQUEUE(q, m); - ubt_task_schedule(sc, action); - UBT_NG_UNLOCK(sc); - } -done: - NG_FREE_ITEM(item); - - return (error); -} /* ng_ubt_rcvdata */ - -/**************************************************************************** - **************************************************************************** - ** Module - **************************************************************************** - ****************************************************************************/ - -/* - * 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 */ - -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), -}; - -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, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); -MODULE_DEPEND(ng_ubt, usb2_bluetooth, 1, 1, 1); -MODULE_DEPEND(ng_ubt, usb2_core, 1, 1, 1); - diff --git a/sys/dev/usb2/bluetooth/ng_ubt2_var.h b/sys/dev/usb2/bluetooth/ng_ubt2_var.h deleted file mode 100644 index 721e2f1..0000000 --- a/sys/dev/usb2/bluetooth/ng_ubt2_var.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * ng_ubt_var.h - */ - -/*- - * Copyright (c) 2001-2009 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_ 1 - -/* Debug printf's */ -#define UBT_DEBUG(level, sc, fmt, ...) \ -do { \ - if ((sc)->sc_debug >= (level)) \ - device_printf((sc)->sc_dev, "%s:%d: " fmt, \ - __FUNCTION__, __LINE__,## __VA_ARGS__); \ -} while (0) - -#define UBT_ALERT(...) UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__) -#define UBT_ERR(...) UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__) -#define UBT_WARN(...) UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__) -#define UBT_INFO(...) UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__) - -#define UBT_NG_LOCK(sc) mtx_lock(&(sc)->sc_ng_mtx) -#define UBT_NG_UNLOCK(sc) mtx_unlock(&(sc)->sc_ng_mtx) - -/* Bluetooth USB control request type */ -#define UBT_HCI_REQUEST 0x20 -#define UBT_DEFAULT_QLEN 64 -#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */ - -/* Bluetooth USB defines */ -enum { - /* Interface #0 transfers */ - UBT_IF_0_BULK_DT_WR = 0, - UBT_IF_0_BULK_DT_RD, - UBT_IF_0_INTR_DT_RD, - UBT_IF_0_CTRL_DT_WR, - - /* Interface #1 transfers */ - UBT_IF_1_ISOC_DT_RD1, - UBT_IF_1_ISOC_DT_RD2, - UBT_IF_1_ISOC_DT_WR1, - UBT_IF_1_ISOC_DT_WR2, - - UBT_N_TRANSFER, /* total number of transfers */ -}; - -/* USB device softc structure */ -struct ubt_softc { - device_t sc_dev; /* for debug printf */ - - /* State */ - ng_ubt_node_debug_ep sc_debug; /* debug level */ - - ng_ubt_node_stat_ep sc_stat; /* statistic */ -#define UBT_STAT_PCKTS_SENT(sc) (sc)->sc_stat.pckts_sent ++ -#define UBT_STAT_BYTES_SENT(sc, n) (sc)->sc_stat.bytes_sent += (n) -#define UBT_STAT_PCKTS_RECV(sc) (sc)->sc_stat.pckts_recv ++ -#define UBT_STAT_BYTES_RECV(sc, n) (sc)->sc_stat.bytes_recv += (n) -#define UBT_STAT_OERROR(sc) (sc)->sc_stat.oerrors ++ -#define UBT_STAT_IERROR(sc) (sc)->sc_stat.ierrors ++ -#define UBT_STAT_RESET(sc) bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat)) - - /* USB device specific */ - struct mtx sc_if_mtx; /* interfaces lock */ - struct usb2_xfer *sc_xfer[UBT_N_TRANSFER]; - - struct mtx sc_ng_mtx; /* lock for shared NG data */ - - /* HCI commands */ - struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ -#define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_device_request) + \ - sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) -#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ - - /* ACL data */ - struct ng_bt_mbufq sc_aclq; /* ACL data queue */ -#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ -#define UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES) - - /* SCO data */ - struct ng_bt_mbufq sc_scoq; /* SCO data queue */ - struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */ - - /* Netgraph specific */ - node_p sc_node; /* pointer back to node */ - hook_p sc_hook; /* upstream hook */ - - /* Glue */ - int sc_task_flags; /* task flags */ -#define UBT_FLAG_T_PENDING (1 << 0) /* task pending */ -#define UBT_FLAG_T_STOP_ALL (1 << 1) /* stop all xfers */ -#define UBT_FLAG_T_START_ALL (1 << 2) /* start all read and isoc - write xfers */ -#define UBT_FLAG_T_START_CTRL (1 << 3) /* start control xfer (write) */ -#define UBT_FLAG_T_START_BULK (1 << 4) /* start bulk xfer (write) */ - - struct task sc_task; -}; -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 deleted file mode 100644 index 91735ed..0000000 --- a/sys/dev/usb2/bluetooth/ubtbcmfw2.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * ubtbcmfw.c - */ - -/*- - * Copyright (c) 2003-2009 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 "usbdevs.h" -#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_BSIZE 1024 -#define UBTBCMFW_IFQ_MAXLEN 2 - -enum { - UBTBCMFW_BULK_DT_WR = 0, - UBTBCMFW_INTR_DT_RD, - UBTBCMFW_N_TRANSFER, -}; - -struct ubtbcmfw_softc { - struct usb2_device *sc_udev; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER]; - struct usb2_fifo_sc sc_fifo; -}; - -/* - * 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_read_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", -}; - -/* - * Device's config structure - */ - -static const struct usb2_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] = -{ - [UBTBCMFW_BULK_DT_WR] = { - .type = UE_BULK, - .endpoint = 0x02, /* fixed */ - .direction = UE_DIR_OUT, - .if_index = UBTBCMFW_IFACE_IDX, - .mh.bufsize = UBTBCMFW_BSIZE, - .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, - .proxy_buffer = 1, }, - .mh.callback = &ubtbcmfw_write_callback, - }, - - [UBTBCMFW_INTR_DT_RD] = { - .type = UE_INTERRUPT, - .endpoint = 0x01, /* fixed */ - .direction = UE_DIR_IN, - .if_index = UBTBCMFW_IFACE_IDX, - .mh.bufsize = UBTBCMFW_BSIZE, - .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, - .proxy_buffer = 1, }, - .mh.callback = &ubtbcmfw_read_callback, - }, -}; - -/* - * 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) -{ - const struct usb2_device_id devs[] = { - /* Broadcom BCM2033 devices only */ - { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) }, - }; - - 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); - - return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa)); -} /* ubtbcmfw_probe */ - -/* - * 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); - uint8_t iface_index; - int error; - - 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; - error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, - ubtbcmfw_config, UBTBCMFW_N_TRANSFER, - sc, &sc->sc_mtx); - if (error != 0) { - device_printf(dev, "allocating USB transfers failed. %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, - &ubtbcmfw_fifo_methods, &sc->sc_fifo, - device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); - if (error != 0) { - device_printf(dev, "could not attach fifo. %s\n", - usb2_errstr(error)); - goto detach; - } - - return (0); /* success */ - -detach: - ubtbcmfw_detach(dev); - - return (ENXIO); /* failure */ -} /* ubtbcmfw_attach */ - -/* - * 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_N_TRANSFER); - - mtx_destroy(&sc->sc_mtx); - - return (0); -} /* ubtbcmfw_detach */ - -/* - * USB write callback - */ - -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_TX]; - uint32_t actlen; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_SETUP: - case USB_ST_TRANSFERRED: -setup_next: - 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 */ - xfer->flags.stall_pipe = 1; - goto setup_next; - } - break; - } -} /* ubtbcmfw_write_callback */ - -/* - * USB read callback - */ - -static void -ubtbcmfw_read_callback(struct usb2_xfer *xfer) -{ - struct ubtbcmfw_softc *sc = xfer->priv_sc; - struct usb2_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX]; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1); - /* FALLTHROUGH */ - - case USB_ST_SETUP: -setup_next: - if (usb2_fifo_put_bytes_max(fifo) > 0) { - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto setup_next; - } - break; - } -} /* ubtbcmfw_read_callback */ - -/* - * Called when we about to start read()ing from the device - */ - -static void -ubtbcmfw_start_read(struct usb2_fifo *fifo) -{ - struct ubtbcmfw_softc *sc = fifo->priv_sc0; - - usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); -} /* ubtbcmfw_start_read */ - -/* - * Called when we about to stop reading (i.e. closing fifo) - */ - -static void -ubtbcmfw_stop_read(struct usb2_fifo *fifo) -{ - struct ubtbcmfw_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); -} /* ubtbcmfw_stop_read */ - -/* - * Called when we about to start write()ing to the device, poll()ing - * for write or flushing fifo - */ - -static void -ubtbcmfw_start_write(struct usb2_fifo *fifo) -{ - struct ubtbcmfw_softc *sc = fifo->priv_sc0; - - usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); -} /* ubtbcmfw_start_write */ - -/* - * Called when we about to stop writing (i.e. closing fifo) - */ - -static void -ubtbcmfw_stop_write(struct usb2_fifo *fifo) -{ - struct ubtbcmfw_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); -} /* ubtbcmfw_stop_write */ - -/* - * Called when fifo is open - */ - -static int -ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) -{ - struct ubtbcmfw_softc *sc = fifo->priv_sc0; - struct usb2_xfer *xfer; - - /* - * f_open fifo method can only be called with either FREAD - * or FWRITE flag set at one time. - */ - - if (fflags & FREAD) - xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD]; - else if (fflags & FWRITE) - xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR]; - else - return (EINVAL); /* should not happen */ - - if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length, - UBTBCMFW_IFQ_MAXLEN) != 0) - return (ENOMEM); - - return (0); -} /* ubtbcmfw_open */ - -/* - * Called when fifo is closed - */ - -static void -ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) -{ - if (fflags & (FREAD | FWRITE)) - usb2_fifo_free_buffer(fifo); -} /* ubtbcmfw_close */ - -/* - * Process ioctl() on USB device - */ - -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: - memcpy(data, usb2_get_device_descriptor(sc->sc_udev), - sizeof(struct usb2_device_descriptor)); - break; - - default: - error = EINVAL; - break; - } - - return (error); -} /* ubtbcmfw_ioctl */ diff --git a/sys/dev/usb2/bluetooth/usb2_bluetooth.c b/sys/dev/usb2/bluetooth/usb2_bluetooth.c deleted file mode 100644 index a8c9f54..0000000 --- a/sys/dev/usb2/bluetooth/usb2_bluetooth.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index b4b1761..0000000 --- a/sys/dev/usb2/bluetooth/usb2_bluetooth.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 501d0c0..0000000 --- a/sys/dev/usb2/controller/at91dci.c +++ /dev/null @@ -1,2467 +0,0 @@ -#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 - -#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 *, usb2_error_t); -static void at91dci_do_poll(struct usb2_bus *); -static void at91dci_root_ctrl_poll(struct at91dci_softc *); -static void at91dci_standard_done(struct usb2_xfer *); - -static usb2_sw_transfer_func_t at91dci_root_intr_done; -static usb2_sw_transfer_func_t at91dci_root_ctrl_done; - -/* - * 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; - } -} - -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); - } -} - -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; - } -} - -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); - } -} - -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); - } -} - -static void -at91dci_wakeup_peer(struct usb2_xfer *xfer) -{ - struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); - uint8_t use_polling; - - if (!(sc->sc_flags.status_suspend)) { - return; - } - use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0; - - AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR); - - /* wait 8 milliseconds */ - if (use_polling) { - /* polling */ - DELAY(8000); - } else { - /* Wait for reset to complete. */ - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); - } - - AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0); -} - -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); -} - -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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - 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; - } - } -} - -void -at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on) -{ - DPRINTFN(5, "vbus = %u\n", is_on); - - USB_BUS_LOCK(&sc->sc_bus); - 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); - } - } - USB_BUS_UNLOCK(&sc->sc_bus); -} - -void -at91dci_interrupt(struct at91dci_softc *sc) -{ - uint32_t status; - - USB_BUS_LOCK(&sc->sc_bus); - - status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); - status &= AT91_UDP_INT_DEFAULT; - - if (!status) { - USB_BUS_UNLOCK(&sc->sc_bus); - 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); - } - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; -} - -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->xroot->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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - 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; - } -} - -static void -at91dci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - at91dci_device_done(xfer, USB_ERR_TIMEOUT); -} - -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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - 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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, - &at91dci_timeout, xfer->timeout); - } - } -} - -static void -at91dci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); - - DPRINTFN(9, "\n"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -/*------------------------------------------------------------------------* - * 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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - uint8_t ep_no; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -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; - - USB_BUS_LOCK_ASSERT(udev->bus, 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); -} - -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); -} - -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); - - USB_BUS_LOCK_ASSERT(udev->bus, 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))); -} - -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; - - USB_BUS_LOCK(&sc->sc_bus); - - /* 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.bus_mtx, hz / 1000); - - /* 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); - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch any lost interrupts */ - - at91dci_do_poll(&sc->sc_bus); - - return (0); /* success */ -} - -void -at91dci_uninit(struct at91dci_softc *sc) -{ - USB_BUS_LOCK(&sc->sc_bus); - - /* 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); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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); - - USB_BUS_LOCK(&sc->sc_bus); - at91dci_interrupt_poll(sc); - at91dci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -static void -at91dci_device_isoc_fs_enter(struct usb2_xfer *xfer) -{ - struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); - 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); -} - -static void -at91dci_device_isoc_fs_start(struct usb2_xfer *xfer) -{ - /* start TD chain */ - at91dci_start_standard_chain(xfer); -} - -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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - at91dci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* - * 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) >> 8, - .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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -at91dci_root_ctrl_task(struct usb2_bus *bus) -{ - at91dci_root_ctrl_poll(AT9100_DCI_BUS2SC(bus)); -} - -static void -at91dci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus); - uint16_t value; - uint16_t index; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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(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; - 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); -} - -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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - at91dci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = AT9100_DCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -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; - - /* - * 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; -} - -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; - } - } -} - -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, - .roothub_exec = &at91dci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/at91dci.h b/sys/dev/usb2/controller/at91dci.h deleted file mode 100644 index d386307..0000000 --- a/sys/dev/usb2/controller/at91dci.h +++ /dev/null @@ -1,245 +0,0 @@ -/* $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_MAX_DEVICES (USB_MIN_DEVICES + 1) - -#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_device *sc_devices[AT91_MAX_DEVICES]; - 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); -void at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on); - -#endif /* _AT9100_DCI_H_ */ diff --git a/sys/dev/usb2/controller/at91dci_atmelarm.c b/sys/dev/usb2/controller/at91dci_atmelarm.c deleted file mode 100644 index 39d3782..0000000 --- a/sys/dev/usb2/controller/at91dci_atmelarm.c +++ /dev/null @@ -1,347 +0,0 @@ -#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 - -#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_poll(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); - at91dci_vbus_interrupt(&sc->sc_dci, vbus_val); -} - -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); -} - -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); -} - -static void -at91_udp_pull_up(void *arg) -{ - at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); -} - -static void -at91_udp_pull_down(void *arg) -{ - at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); -} - -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; - - /* 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; - - /* initialise some bus fields */ - sc->sc_dci.sc_bus.parent = dev; - sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; - sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES; - - /* 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, hz / 100); - - 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); - -#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_poll, 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_poll, 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_poll(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_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/atmegadci.c b/sys/dev/usb2/controller/atmegadci.c deleted file mode 100644 index bea8835d..0000000 --- a/sys/dev/usb2/controller/atmegadci.c +++ /dev/null @@ -1,2327 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -/*- - * Copyright (c) 2009 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 ATMEGA series USB Device - * Controller - */ - -/* - * 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 atmegadci_debug - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define ATMEGA_BUS2SC(bus) \ - ((struct atmegadci_softc *)(((uint8_t *)(bus)) - \ - USB_P2U(&(((struct atmegadci_softc *)0)->sc_bus)))) - -#define ATMEGA_PC2SC(pc) \ - ATMEGA_BUS2SC((pc)->tag_parent->info->bus) - -#if USB_DEBUG -static int atmegadci_debug = 0; - -SYSCTL_NODE(_hw_usb2, OID_AUTO, atmegadci, CTLFLAG_RW, 0, "USB ATMEGA DCI"); -SYSCTL_INT(_hw_usb2_atmegadci, OID_AUTO, debug, CTLFLAG_RW, - &atmegadci_debug, 0, "ATMEGA DCI debug level"); -#endif - -#define ATMEGA_INTR_ENDPT 1 - -/* prototypes */ - -struct usb2_bus_methods atmegadci_bus_methods; -struct usb2_pipe_methods atmegadci_device_bulk_methods; -struct usb2_pipe_methods atmegadci_device_ctrl_methods; -struct usb2_pipe_methods atmegadci_device_intr_methods; -struct usb2_pipe_methods atmegadci_device_isoc_fs_methods; -struct usb2_pipe_methods atmegadci_root_ctrl_methods; -struct usb2_pipe_methods atmegadci_root_intr_methods; - -static atmegadci_cmd_t atmegadci_setup_rx; -static atmegadci_cmd_t atmegadci_data_rx; -static atmegadci_cmd_t atmegadci_data_tx; -static atmegadci_cmd_t atmegadci_data_tx_sync; -static void atmegadci_device_done(struct usb2_xfer *, usb2_error_t); -static void atmegadci_do_poll(struct usb2_bus *); -static void atmegadci_root_ctrl_poll(struct atmegadci_softc *); -static void atmegadci_standard_done(struct usb2_xfer *); - -static usb2_sw_transfer_func_t atmegadci_root_intr_done; -static usb2_sw_transfer_func_t atmegadci_root_ctrl_done; - -/* - * Here is a list of what the chip supports: - */ -static const struct usb2_hw_ep_profile - atmegadci_ep_profile[2] = { - - [0] = { - .max_in_frame_size = 64, - .max_out_frame_size = 64, - .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, - }, -}; - -static void -atmegadci_get_hw_ep_profile(struct usb2_device *udev, - const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) -{ - if (ep_addr == 0) - *ppf = atmegadci_ep_profile; - else if (ep_addr < ATMEGA_EP_MAX) - *ppf = atmegadci_ep_profile + 1; - else - *ppf = NULL; -} - -static void -atmegadci_clocks_on(struct atmegadci_softc *sc) -{ - if (sc->sc_flags.clocks_off && - sc->sc_flags.port_powered) { - - DPRINTFN(5, "\n"); - - /* turn on clocks */ - (sc->sc_clocks_on) (&sc->sc_bus); - - ATMEGA_WRITE_1(sc, ATMEGA_USBCON, - ATMEGA_USBCON_USBE | - ATMEGA_USBCON_OTGPADE | - ATMEGA_USBCON_VBUSTE); - - sc->sc_flags.clocks_off = 0; - - /* enable transceiver ? */ - } -} - -static void -atmegadci_clocks_off(struct atmegadci_softc *sc) -{ - if (!sc->sc_flags.clocks_off) { - - DPRINTFN(5, "\n"); - - /* disable Transceiver ? */ - - ATMEGA_WRITE_1(sc, ATMEGA_USBCON, - ATMEGA_USBCON_USBE | - ATMEGA_USBCON_OTGPADE | - ATMEGA_USBCON_FRZCLK | - ATMEGA_USBCON_VBUSTE); - - /* turn clocks off */ - (sc->sc_clocks_off) (&sc->sc_bus); - - sc->sc_flags.clocks_off = 1; - } -} - -static void -atmegadci_pull_up(struct atmegadci_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; - ATMEGA_WRITE_1(sc, ATMEGA_UDCON, 0); - } -} - -static void -atmegadci_pull_down(struct atmegadci_softc *sc) -{ - /* pulldown D+, if possible */ - - if (sc->sc_flags.d_pulled_up) { - sc->sc_flags.d_pulled_up = 0; - ATMEGA_WRITE_1(sc, ATMEGA_UDCON, ATMEGA_UDCON_DETACH); - } -} - -static void -atmegadci_wakeup_peer(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - uint8_t use_polling; - uint8_t temp; - - if (!sc->sc_flags.status_suspend) { - return; - } - use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0; - - temp = ATMEGA_READ_1(sc, ATMEGA_UDCON); - ATMEGA_WRITE_1(sc, ATMEGA_UDCON, temp | ATMEGA_UDCON_RMWKUP); - - /* wait 8 milliseconds */ - if (use_polling) { - /* polling */ - DELAY(8000); - } else { - /* Wait for reset to complete. */ - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125); - } - - /* hardware should have cleared RMWKUP bit */ -} - -static void -atmegadci_set_address(struct atmegadci_softc *sc, uint8_t addr) -{ - DPRINTFN(5, "addr=%d\n", addr); - - ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr); - - addr |= ATMEGA_UDADDR_ADDEN; - - ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr); -} - -static uint8_t -atmegadci_setup_rx(struct atmegadci_td *td) -{ - struct atmegadci_softc *sc; - struct usb2_device_request req; - uint16_t count; - uint8_t temp; - - /* get pointer to softc */ - sc = ATMEGA_PC2SC(td->pc); - - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); - - /* check endpoint status */ - temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); - - DPRINTFN(5, "UEINTX=0x%02x\n", temp); - - if (!(temp & ATMEGA_UEINTX_RXSTPI)) { - /* abort any ongoing transfer */ - if (!td->did_stall) { - DPRINTFN(5, "stalling\n"); - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, - ATMEGA_UECONX_EPEN | - ATMEGA_UECONX_STALLRQ); - td->did_stall = 1; - } - goto not_complete; - } - /* get the packet byte count */ - count = - (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) | - (ATMEGA_READ_1(sc, ATMEGA_UEBCLX)); - - /* mask away undefined bits */ - count &= 0x7FF; - - /* 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 */ - ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX, - (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; - - /* 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; - } - - /* clear SETUP packet interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ~ATMEGA_UEINTX_RXSTPI); - return (0); /* complete */ - -not_complete: - /* we only want to know if there is a SETUP packet */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, ATMEGA_UEIENX_RXSTPE); - return (1); /* not complete */ -} - -static uint8_t -atmegadci_data_rx(struct atmegadci_td *td) -{ - struct atmegadci_softc *sc; - struct usb2_page_search buf_res; - uint16_t count; - uint8_t temp; - uint8_t to; - uint8_t got_short; - - to = 3; /* don't loop forever! */ - got_short = 0; - - /* get pointer to softc */ - sc = ATMEGA_PC2SC(td->pc); - - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); - -repeat: - /* check if any of the FIFO banks have data */ - /* check endpoint status */ - temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); - - DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder); - - if (temp & ATMEGA_UEINTX_RXSTPI) { - 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 status */ - if (!(temp & (ATMEGA_UEINTX_FIFOCON | - ATMEGA_UEINTX_RXOUTI))) { - /* no data */ - goto not_complete; - } - /* get the packet byte count */ - count = - (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) | - (ATMEGA_READ_1(sc, ATMEGA_UEBCLX)); - - /* mask away undefined bits */ - count &= 0x7FF; - - /* 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 */ - ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX, - buf_res.buffer, buf_res.length); - - /* update counters */ - count -= buf_res.length; - td->offset += buf_res.length; - td->remainder -= buf_res.length; - } - - /* clear OUT packet interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_RXOUTI ^ 0xFF); - - /* release FIFO bank */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_FIFOCON ^ 0xFF); - - /* 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; - } -not_complete: - /* we only want to know if there is a SETUP packet or OUT packet */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, - ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_RXOUTE); - return (1); /* not complete */ -} - -static uint8_t -atmegadci_data_tx(struct atmegadci_td *td) -{ - struct atmegadci_softc *sc; - struct usb2_page_search buf_res; - uint16_t count; - uint8_t to; - uint8_t temp; - - to = 3; /* don't loop forever! */ - - /* get pointer to softc */ - sc = ATMEGA_PC2SC(td->pc); - - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); - -repeat: - - /* check endpoint status */ - temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); - - DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder); - - if (temp & ATMEGA_UEINTX_RXSTPI) { - /* - * The current transfer was aborted - * by the USB Host - */ - td->error = 1; - return (0); /* complete */ - } - if (!(temp & (ATMEGA_UEINTX_FIFOCON | - ATMEGA_UEINTX_TXINI))) { - /* cannot write any data */ - goto not_complete; - } - 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 */ - ATMEGA_WRITE_MULTI_1(sc, ATMEGA_UEDATX, - buf_res.buffer, buf_res.length); - - /* update counters */ - count -= buf_res.length; - td->offset += buf_res.length; - td->remainder -= buf_res.length; - } - - /* clear IN packet interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_TXINI); - - /* allocate FIFO bank */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_FIFOCON); - - /* 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; - } -not_complete: - /* we only want to know if there is a SETUP packet or free IN packet */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, - ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE); - return (1); /* not complete */ -} - -static uint8_t -atmegadci_data_tx_sync(struct atmegadci_td *td) -{ - struct atmegadci_softc *sc; - uint8_t temp; - - /* get pointer to softc */ - sc = ATMEGA_PC2SC(td->pc); - - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no); - - /* check endpoint status */ - temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX); - - DPRINTFN(5, "temp=0x%02x\n", temp); - - if (temp & ATMEGA_UEINTX_RXSTPI) { - DPRINTFN(5, "faking complete\n"); - /* Race condition */ - return (0); /* complete */ - } - /* - * The control endpoint has only got one bank, so if that bank - * is free the packet has been transferred! - */ - if (!(temp & (ATMEGA_UEINTX_FIFOCON | - ATMEGA_UEINTX_TXINI))) { - /* cannot write any data */ - goto not_complete; - } - if (sc->sc_dv_addr != 0xFF) { - /* set new address */ - atmegadci_set_address(sc, sc->sc_dv_addr); - } - return (0); /* complete */ - -not_complete: - /* we only want to know if there is a SETUP packet or free IN packet */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, - ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE); - return (1); /* not complete */ -} - -static uint8_t -atmegadci_xfer_do_fifo(struct usb2_xfer *xfer) -{ - struct atmegadci_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 and transfer - * some flags to the next transfer descriptor - */ - td = td->obj_next; - xfer->td_transfer_cache = td; - } - return (1); /* not complete */ - -done: - /* compute all actual lengths */ - - atmegadci_standard_done(xfer); - return (0); /* complete */ -} - -static void -atmegadci_interrupt_poll(struct atmegadci_softc *sc) -{ - struct usb2_xfer *xfer; - -repeat: - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - if (!atmegadci_xfer_do_fifo(xfer)) { - /* queue has been modified */ - goto repeat; - } - } -} - -static void -atmegadci_vbus_interrupt(struct atmegadci_softc *sc, uint8_t is_on) -{ - DPRINTFN(5, "vbus = %u\n", is_on); - - 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, - &atmegadci_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, - &atmegadci_root_intr_done); - } - } -} - -void -atmegadci_interrupt(struct atmegadci_softc *sc) -{ - uint8_t status; - - USB_BUS_LOCK(&sc->sc_bus); - - /* read interrupt status */ - status = ATMEGA_READ_1(sc, ATMEGA_UDINT); - - /* clear all set interrupts */ - ATMEGA_WRITE_1(sc, ATMEGA_UDINT, ~status); - - /* check for any bus state change interrupts */ - if (status & ATMEGA_UDINT_EORSTI) { - - DPRINTFN(5, "end of reset\n"); - - /* 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 */ - ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, - ATMEGA_UDINT_SUSPE | - ATMEGA_UDINT_EORSTE); - - /* complete root HUB interrupt endpoint */ - usb2_sw_transfer(&sc->sc_root_intr, - &atmegadci_root_intr_done); - } - /* - * 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 (status & ATMEGA_UDINT_EORSMI) { - - DPRINTFN(5, "resume interrupt\n"); - - if (sc->sc_flags.status_suspend) { - /* update status bits */ - sc->sc_flags.status_suspend = 0; - sc->sc_flags.change_suspend = 1; - - /* disable resume interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, - ATMEGA_UDINT_SUSPE | - ATMEGA_UDINT_EORSTE); - - /* complete root HUB interrupt endpoint */ - usb2_sw_transfer(&sc->sc_root_intr, - &atmegadci_root_intr_done); - } - } else if (status & ATMEGA_UDINT_SUSPI) { - - DPRINTFN(5, "suspend interrupt\n"); - - if (!sc->sc_flags.status_suspend) { - /* update status bits */ - sc->sc_flags.status_suspend = 1; - sc->sc_flags.change_suspend = 1; - - /* disable suspend interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, - ATMEGA_UDINT_EORSMI | - ATMEGA_UDINT_EORSTE); - - /* complete root HUB interrupt endpoint */ - usb2_sw_transfer(&sc->sc_root_intr, - &atmegadci_root_intr_done); - } - } - /* check VBUS */ - status = ATMEGA_READ_1(sc, ATMEGA_USBINT); - - /* clear all set interrupts */ - ATMEGA_WRITE_1(sc, ATMEGA_USBINT, ~status); - - if (status & ATMEGA_USBINT_VBUSTI) { - uint8_t temp; - - temp = ATMEGA_READ_1(sc, ATMEGA_USBSTA); - atmegadci_vbus_interrupt(sc, temp & ATMEGA_USBSTA_VBUS); - } - /* check for any endpoint interrupts */ - status = ATMEGA_READ_1(sc, ATMEGA_UEINT); - - /* clear all set interrupts */ - ATMEGA_WRITE_1(sc, ATMEGA_UEINT, ~status); - - if (status) { - - DPRINTFN(5, "real endpoint interrupt 0x%02x\n", status); - - atmegadci_interrupt_poll(sc); - } - USB_BUS_UNLOCK(&sc->sc_bus); -} - -static void -atmegadci_setup_standard_chain_sub(struct atmegadci_std_temp *temp) -{ - struct atmegadci_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; -} - -static void -atmegadci_setup_standard_chain(struct usb2_xfer *xfer) -{ - struct atmegadci_std_temp temp; - struct atmegadci_softc *sc; - struct atmegadci_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->xroot->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 = ATMEGA_BUS2SC(xfer->xroot->bus); - 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 = &atmegadci_setup_rx; - temp.len = xfer->frlengths[0]; - temp.pc = xfer->frbuffers + 0; - temp.short_pkt = temp.len ? 1 : 0; - - atmegadci_setup_standard_chain_sub(&temp); - } - x = 1; - } else { - x = 0; - } - - if (x != xfer->nframes) { - if (xfer->endpoint & UE_DIR_IN) { - temp.func = &atmegadci_data_tx; - need_sync = 1; - } else { - temp.func = &atmegadci_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; - } - - atmegadci_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 = &atmegadci_data_tx_sync; - temp.len = 0; - temp.short_pkt = 0; - - atmegadci_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 = &atmegadci_data_rx; - need_sync = 0; - } else { - temp.func = &atmegadci_data_tx; - need_sync = 1; - } - temp.len = 0; - temp.short_pkt = 0; - - atmegadci_setup_standard_chain_sub(&temp); - if (need_sync) { - /* we need a SYNC point after TX */ - temp.func = &atmegadci_data_tx_sync; - temp.len = 0; - temp.short_pkt = 0; - - atmegadci_setup_standard_chain_sub(&temp); - } - } - /* must have at least one frame! */ - td = temp.td; - xfer->td_transfer_last = td; -} - -static void -atmegadci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - atmegadci_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -atmegadci_start_standard_chain(struct usb2_xfer *xfer) -{ - DPRINTFN(9, "\n"); - - /* poll one time - will turn on interrupts */ - if (atmegadci_xfer_do_fifo(xfer)) { - - /* put transfer on interrupt queue */ - usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, - &atmegadci_timeout, xfer->timeout); - } - } -} - -static void -atmegadci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - - DPRINTFN(9, "\n"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); - - if (std->state != USB_SW_TR_PRE_DATA) { - if (std->state == USB_SW_TR_PRE_CALLBACK) { - /* transfer transferred */ - atmegadci_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 -atmegadci_standard_done_sub(struct usb2_xfer *xfer) -{ - struct atmegadci_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 -atmegadci_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 = atmegadci_standard_done_sub(xfer); - } - xfer->aframes = 1; - - if (xfer->td_transfer_cache == NULL) { - goto done; - } - } - while (xfer->aframes != xfer->nframes) { - - err = atmegadci_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 = atmegadci_standard_done_sub(xfer); - } -done: - atmegadci_device_done(xfer, err); -} - -/*------------------------------------------------------------------------* - * atmegadci_device_done - * - * NOTE: this function can be called more than one time on the - * same USB transfer! - *------------------------------------------------------------------------*/ -static void -atmegadci_device_done(struct usb2_xfer *xfer, usb2_error_t error) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - uint8_t ep_no; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); - - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); - - /* disable endpoint interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0); - - DPRINTFN(15, "disabled interrupts!\n"); - } - /* dequeue transfer and start next transfer */ - usb2_transfer_done(xfer, error); -} - -static void -atmegadci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, - struct usb2_pipe *pipe) -{ - struct atmegadci_softc *sc; - uint8_t ep_no; - - USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); - - DPRINTFN(5, "pipe=%p\n", pipe); - - if (xfer) { - /* cancel any ongoing transfers */ - atmegadci_device_done(xfer, USB_ERR_STALLED); - } - sc = ATMEGA_BUS2SC(udev->bus); - /* get endpoint number */ - ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); - /* select endpoint number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); - /* set stall */ - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, - ATMEGA_UECONX_EPEN | - ATMEGA_UECONX_STALLRQ); -} - -static void -atmegadci_clear_stall_sub(struct atmegadci_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 number */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no); - - /* set endpoint reset */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, ATMEGA_UERST_MASK(ep_no)); - - /* clear endpoint reset */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0); - - /* set stall */ - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, - ATMEGA_UECONX_EPEN | - ATMEGA_UECONX_STALLRQ); - - /* reset data toggle */ - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, - ATMEGA_UECONX_EPEN | - ATMEGA_UECONX_RSTDT); - - /* clear stall */ - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, - ATMEGA_UECONX_EPEN | - ATMEGA_UECONX_STALLRQC); - - if (ep_type == UE_CONTROL) { - /* one bank, 64-bytes wMaxPacket */ - ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X, - ATMEGA_UECFG0X_EPTYPE0); - ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X, - ATMEGA_UECFG1X_ALLOC | - ATMEGA_UECFG1X_EPBK0 | - ATMEGA_UECFG1X_EPSIZE(7)); - } else { - temp = 0; - if (ep_type == UE_BULK) { - temp |= ATMEGA_UECFG0X_EPTYPE2; - } else if (ep_type == UE_INTERRUPT) { - temp |= ATMEGA_UECFG0X_EPTYPE3; - } else { - temp |= ATMEGA_UECFG0X_EPTYPE1; - } - if (ep_dir & UE_DIR_IN) { - temp |= ATMEGA_UECFG0X_EPDIR; - } - /* two banks, 64-bytes wMaxPacket */ - ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X, temp); - ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X, - ATMEGA_UECFG1X_ALLOC | - ATMEGA_UECFG1X_EPBK1 | - ATMEGA_UECFG1X_EPSIZE(7)); - - temp = ATMEGA_READ_1(sc, ATMEGA_UESTA0X); - if (!(temp & ATMEGA_UESTA0X_CFGOK)) { - DPRINTFN(0, "Chip rejected configuration\n"); - } - } -} - -static void -atmegadci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) -{ - struct atmegadci_softc *sc; - struct usb2_endpoint_descriptor *ed; - - DPRINTFN(5, "pipe=%p\n", pipe); - - USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); - - /* check mode */ - if (udev->flags.usb2_mode != USB_MODE_DEVICE) { - /* not supported */ - return; - } - /* get softc */ - sc = ATMEGA_BUS2SC(udev->bus); - - /* get endpoint descriptor */ - ed = pipe->edesc; - - /* reset endpoint */ - atmegadci_clear_stall_sub(sc, - (ed->bEndpointAddress & UE_ADDR), - (ed->bmAttributes & UE_XFERTYPE), - (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); -} - -usb2_error_t -atmegadci_init(struct atmegadci_softc *sc) -{ - uint8_t n; - - DPRINTF("start\n"); - - /* set up the bus structure */ - sc->sc_bus.usbrev = USB_REV_1_1; - sc->sc_bus.methods = &atmegadci_bus_methods; - - USB_BUS_LOCK(&sc->sc_bus); - - /* enable USB PAD regulator */ - ATMEGA_WRITE_1(sc, ATMEGA_UHWCON, - ATMEGA_UHWCON_UVREGE); - - /* turn on clocks */ - (sc->sc_clocks_on) (&sc->sc_bus); - - /* wait a little for things to stabilise */ - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); - - /* enable interrupts */ - ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, - ATMEGA_UDINT_SUSPE | - ATMEGA_UDINT_EORSTE); - - /* reset all endpoints */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, - (1 << ATMEGA_EP_MAX) - 1); - - /* disable reset */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0); - - /* disable all endpoints */ - for (n = 1; n != ATMEGA_EP_MAX; n++) { - - /* select endpoint */ - ATMEGA_WRITE_1(sc, ATMEGA_UENUM, n); - - /* disable endpoint interrupt */ - ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0); - - /* disable endpoint */ - ATMEGA_WRITE_1(sc, ATMEGA_UECONX, 0); - } - - /* turn off clocks */ - - atmegadci_clocks_off(sc); - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch any lost interrupts */ - - atmegadci_do_poll(&sc->sc_bus); - - return (0); /* success */ -} - -void -atmegadci_uninit(struct atmegadci_softc *sc) -{ - USB_BUS_LOCK(&sc->sc_bus); - - /* turn on clocks */ - (sc->sc_clocks_on) (&sc->sc_bus); - - /* disable interrupts */ - ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, 0); - - /* reset all endpoints */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, - (1 << ATMEGA_EP_MAX) - 1); - - /* disable reset */ - ATMEGA_WRITE_1(sc, ATMEGA_UERST, 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; - - atmegadci_pull_down(sc); - atmegadci_clocks_off(sc); - - /* disable USB PAD regulator */ - ATMEGA_WRITE_1(sc, ATMEGA_UHWCON, 0); - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -void -atmegadci_suspend(struct atmegadci_softc *sc) -{ - return; -} - -void -atmegadci_resume(struct atmegadci_softc *sc) -{ - return; -} - -static void -atmegadci_do_poll(struct usb2_bus *bus) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(bus); - - USB_BUS_LOCK(&sc->sc_bus); - atmegadci_interrupt_poll(sc); - atmegadci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/*------------------------------------------------------------------------* - * at91dci bulk support - *------------------------------------------------------------------------*/ -static void -atmegadci_device_bulk_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_bulk_close(struct usb2_xfer *xfer) -{ - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -atmegadci_device_bulk_enter(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_bulk_start(struct usb2_xfer *xfer) -{ - /* setup TDs */ - atmegadci_setup_standard_chain(xfer); - atmegadci_start_standard_chain(xfer); -} - -struct usb2_pipe_methods atmegadci_device_bulk_methods = -{ - .open = atmegadci_device_bulk_open, - .close = atmegadci_device_bulk_close, - .enter = atmegadci_device_bulk_enter, - .start = atmegadci_device_bulk_start, - .enter_is_cancelable = 1, - .start_is_cancelable = 1, -}; - -/*------------------------------------------------------------------------* - * at91dci control support - *------------------------------------------------------------------------*/ -static void -atmegadci_device_ctrl_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_ctrl_close(struct usb2_xfer *xfer) -{ - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -atmegadci_device_ctrl_enter(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_ctrl_start(struct usb2_xfer *xfer) -{ - /* setup TDs */ - atmegadci_setup_standard_chain(xfer); - atmegadci_start_standard_chain(xfer); -} - -struct usb2_pipe_methods atmegadci_device_ctrl_methods = -{ - .open = atmegadci_device_ctrl_open, - .close = atmegadci_device_ctrl_close, - .enter = atmegadci_device_ctrl_enter, - .start = atmegadci_device_ctrl_start, - .enter_is_cancelable = 1, - .start_is_cancelable = 1, -}; - -/*------------------------------------------------------------------------* - * at91dci interrupt support - *------------------------------------------------------------------------*/ -static void -atmegadci_device_intr_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_intr_close(struct usb2_xfer *xfer) -{ - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -atmegadci_device_intr_enter(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_intr_start(struct usb2_xfer *xfer) -{ - /* setup TDs */ - atmegadci_setup_standard_chain(xfer); - atmegadci_start_standard_chain(xfer); -} - -struct usb2_pipe_methods atmegadci_device_intr_methods = -{ - .open = atmegadci_device_intr_open, - .close = atmegadci_device_intr_close, - .enter = atmegadci_device_intr_enter, - .start = atmegadci_device_intr_start, - .enter_is_cancelable = 1, - .start_is_cancelable = 1, -}; - -/*------------------------------------------------------------------------* - * at91dci full speed isochronous support - *------------------------------------------------------------------------*/ -static void -atmegadci_device_isoc_fs_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_device_isoc_fs_close(struct usb2_xfer *xfer) -{ - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -atmegadci_device_isoc_fs_enter(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - 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 = - (ATMEGA_READ_1(sc, ATMEGA_UDFNUMH) << 8) | - (ATMEGA_READ_1(sc, ATMEGA_UDFNUML)); - - nframes &= ATMEGA_FRAME_MASK; - - /* - * check if the frame index is within the window where the frames - * will be inserted - */ - temp = (nframes - xfer->pipe->isoc_next) & ATMEGA_FRAME_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) & ATMEGA_FRAME_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) & ATMEGA_FRAME_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 */ - atmegadci_setup_standard_chain(xfer); -} - -static void -atmegadci_device_isoc_fs_start(struct usb2_xfer *xfer) -{ - /* start TD chain */ - atmegadci_start_standard_chain(xfer); -} - -struct usb2_pipe_methods atmegadci_device_isoc_fs_methods = -{ - .open = atmegadci_device_isoc_fs_open, - .close = atmegadci_device_isoc_fs_close, - .enter = atmegadci_device_isoc_fs_enter, - .start = atmegadci_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 -atmegadci_root_ctrl_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_root_ctrl_close(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* - * USB descriptors for the virtual Root HUB: - */ - -static const struct usb2_device_descriptor atmegadci_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 atmegadci_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 atmegadci_config_desc atmegadci_confd = { - .confd = { - .bLength = sizeof(struct usb2_config_descriptor), - .bDescriptorType = UDESC_CONFIG, - .wTotalLength[0] = sizeof(atmegadci_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 | ATMEGA_INTR_ENDPT), - .bmAttributes = UE_INTERRUPT, - .wMaxPacketSize[0] = 8, - .bInterval = 255, - }, -}; - -static const struct usb2_hub_descriptor_min atmegadci_hubd = { - .bDescLength = sizeof(atmegadci_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) >> 8, - .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, 'G', 0, 'A', 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, atmegadci_langtab); -USB_MAKE_STRING_DESC(STRING_VENDOR, atmegadci_vendor); -USB_MAKE_STRING_DESC(STRING_PRODUCT, atmegadci_product); - -static void -atmegadci_root_ctrl_enter(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_root_ctrl_start(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -atmegadci_root_ctrl_task(struct usb2_bus *bus) -{ - atmegadci_root_ctrl_poll(ATMEGA_BUS2SC(bus)); -} - -static void -atmegadci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - uint16_t value; - uint16_t index; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); - - if (std->state != USB_SW_TR_SETUP) { - if (std->state == USB_SW_TR_PRE_CALLBACK) { - /* transfer transferred */ - atmegadci_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->xroot->xfer_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(atmegadci_devd); - std->ptr = USB_ADD_BYTES(&atmegadci_devd, 0); - goto tr_valid; - case UDESC_CONFIG: - if (value & 0xff) { - goto tr_stalled; - } - std->len = sizeof(atmegadci_confd); - std->ptr = USB_ADD_BYTES(&atmegadci_confd, 0); - goto tr_valid; - case UDESC_STRING: - switch (value & 0xff) { - case 0: /* Language table */ - std->len = sizeof(atmegadci_langtab); - std->ptr = USB_ADD_BYTES(&atmegadci_langtab, 0); - goto tr_valid; - - case 1: /* Vendor */ - std->len = sizeof(atmegadci_vendor); - std->ptr = USB_ADD_BYTES(&atmegadci_vendor, 0); - goto tr_valid; - - case 2: /* Product */ - std->len = sizeof(atmegadci_product); - std->ptr = USB_ADD_BYTES(&atmegadci_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: - atmegadci_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; - atmegadci_pull_down(sc); - atmegadci_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) { - atmegadci_clocks_on(sc); - atmegadci_pull_up(sc); - } else { - atmegadci_pull_down(sc); - atmegadci_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.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(&atmegadci_hubd, 0); - std->len = sizeof(atmegadci_hubd); - goto tr_valid; - -tr_stalled: - std->err = USB_ERR_STALLED; -tr_valid: -done: - return; -} - -static void -atmegadci_root_ctrl_poll(struct atmegadci_softc *sc) -{ - usb2_sw_transfer(&sc->sc_root_ctrl, - &atmegadci_root_ctrl_done); -} - -struct usb2_pipe_methods atmegadci_root_ctrl_methods = -{ - .open = atmegadci_root_ctrl_open, - .close = atmegadci_root_ctrl_close, - .enter = atmegadci_root_ctrl_enter, - .start = atmegadci_root_ctrl_start, - .enter_is_cancelable = 1, - .start_is_cancelable = 0, -}; - -/*------------------------------------------------------------------------* - * at91dci root interrupt support - *------------------------------------------------------------------------*/ -static void -atmegadci_root_intr_open(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_root_intr_close(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - atmegadci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -atmegadci_root_intr_enter(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_root_intr_start(struct usb2_xfer *xfer) -{ - struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -struct usb2_pipe_methods atmegadci_root_intr_methods = -{ - .open = atmegadci_root_intr_open, - .close = atmegadci_root_intr_close, - .enter = atmegadci_root_intr_enter, - .start = atmegadci_root_intr_start, - .enter_is_cancelable = 1, - .start_is_cancelable = 1, -}; - -static void -atmegadci_xfer_setup(struct usb2_setup_params *parm) -{ - const struct usb2_hw_ep_profile *pf; - struct atmegadci_softc *sc; - struct usb2_xfer *xfer; - void *last_obj; - uint32_t ntd; - uint32_t n; - uint8_t ep_no; - - sc = ATMEGA_BUS2SC(parm->udev->bus); - xfer = parm->curr_xfer; - - /* - * 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 == &atmegadci_device_ctrl_methods) { - - ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ - + 1 /* SYNC 2 */ ; - - } else if (parm->methods == &atmegadci_device_bulk_methods) { - - ntd = xfer->nframes + 1 /* SYNC */ ; - - } else if (parm->methods == &atmegadci_device_intr_methods) { - - ntd = xfer->nframes + 1 /* SYNC */ ; - - } else if (parm->methods == &atmegadci_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; - atmegadci_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 atmegadci_td *td; - - if (parm->buf) { - - td = USB_ADD_BYTES(parm->buf, parm->size[0]); - - /* init TD */ - td->max_packet_size = xfer->max_packet_size; - td->ep_no = 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; -} - -static void -atmegadci_xfer_unsetup(struct usb2_xfer *xfer) -{ - return; -} - -static void -atmegadci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, - struct usb2_pipe *pipe) -{ - struct atmegadci_softc *sc = ATMEGA_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 = &atmegadci_root_ctrl_methods; - break; - case UE_DIR_IN | ATMEGA_INTR_ENDPT: - pipe->methods = &atmegadci_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 = &atmegadci_device_ctrl_methods; - break; - case UE_INTERRUPT: - pipe->methods = &atmegadci_device_intr_methods; - break; - case UE_ISOCHRONOUS: - pipe->methods = &atmegadci_device_isoc_fs_methods; - break; - case UE_BULK: - pipe->methods = &atmegadci_device_bulk_methods; - break; - default: - /* do nothing */ - break; - } - } -} - -struct usb2_bus_methods atmegadci_bus_methods = -{ - .pipe_init = &atmegadci_pipe_init, - .xfer_setup = &atmegadci_xfer_setup, - .xfer_unsetup = &atmegadci_xfer_unsetup, - .do_poll = &atmegadci_do_poll, - .get_hw_ep_profile = &atmegadci_get_hw_ep_profile, - .set_stall = &atmegadci_set_stall, - .clear_stall = &atmegadci_clear_stall, - .roothub_exec = &atmegadci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/atmegadci.h b/sys/dev/usb2/controller/atmegadci.h deleted file mode 100644 index 90b3334..0000000 --- a/sys/dev/usb2/controller/atmegadci.h +++ /dev/null @@ -1,273 +0,0 @@ -/* $FreeBSD$ */ -/*- - * Copyright (c) 2009 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 register definitions, copied from ATMEGA - * documentation provided by ATMEL. - */ - -#ifndef _ATMEGADCI_H_ -#define _ATMEGADCI_H_ - -#define ATMEGA_MAX_DEVICES (USB_MIN_DEVICES + 1) - -#ifndef ATMEGA_HAVE_BUS_SPACE -#define ATMEGA_HAVE_BUS_SPACE 1 -#endif - -#define ATMEGA_UEINT 0xF4 -#define ATMEGA_UEINT_MASK(n) (1 << (n)) /* endpoint interrupt mask */ - -#define ATMEGA_UEBCHX 0xF3 /* FIFO byte count high */ -#define ATMEGA_UEBCLX 0xF2 /* FIFO byte count low */ -#define ATMEGA_UEDATX 0xF1 /* FIFO data */ - -#define ATMEGA_UEIENX 0xF0 /* interrupt enable register */ -#define ATMEGA_UEIENX_TXINE (1 << 0) -#define ATMEGA_UEIENX_STALLEDE (1 << 1) -#define ATMEGA_UEIENX_RXOUTE (1 << 2) -#define ATMEGA_UEIENX_RXSTPE (1 << 3) /* received SETUP packet */ -#define ATMEGA_UEIENX_NAKOUTE (1 << 4) -#define ATMEGA_UEIENX_NAKINE (1 << 6) -#define ATMEGA_UEIENX_FLERRE (1 << 7) - -#define ATMEGA_UESTA1X 0xEF -#define ATMEGA_UESTA1X_CURRBK (3 << 0) /* current bank */ -#define ATMEGA_UESTA1X_CTRLDIR (1 << 2) /* control endpoint direction */ - -#define ATMEGA_UESTA0X 0xEE -#define ATMEGA_UESTA0X_NBUSYBK (3 << 0) -#define ATMEGA_UESTA0X_DTSEQ (3 << 2) -#define ATMEGA_UESTA0X_UNDERFI (1 << 5) /* underflow */ -#define ATMEGA_UESTA0X_OVERFI (1 << 6) /* overflow */ -#define ATMEGA_UESTA0X_CFGOK (1 << 7) - -#define ATMEGA_UECFG1X 0xED /* endpoint config register */ -#define ATMEGA_UECFG1X_ALLOC (1 << 1) -#define ATMEGA_UECFG1X_EPBK0 (0 << 2) -#define ATMEGA_UECFG1X_EPBK1 (1 << 2) -#define ATMEGA_UECFG1X_EPBK2 (2 << 2) -#define ATMEGA_UECFG1X_EPBK3 (3 << 2) -#define ATMEGA_UECFG1X_EPSIZE(n) ((n) << 4) - -#define ATMEGA_UECFG0X 0xEC -#define ATMEGA_UECFG0X_EPDIR (1 << 0) /* endpoint direction */ -#define ATMEGA_UECFG0X_EPTYPE0 (0 << 6) -#define ATMEGA_UECFG0X_EPTYPE1 (1 << 6) -#define ATMEGA_UECFG0X_EPTYPE2 (2 << 6) -#define ATMEGA_UECFG0X_EPTYPE3 (3 << 6) - -#define ATMEGA_UECONX 0xEB -#define ATMEGA_UECONX_EPEN (1 << 0) -#define ATMEGA_UECONX_RSTDT (1 << 3) -#define ATMEGA_UECONX_STALLRQC (1 << 4) /* stall request clear */ -#define ATMEGA_UECONX_STALLRQ (1 << 5) /* stall request set */ - -#define ATMEGA_UERST 0xEA /* endpoint reset register */ -#define ATMEGA_UERST_MASK(n) (1 << (n)) - -#define ATMEGA_UENUM 0xE9 /* endpoint number */ - -#define ATMEGA_UEINTX 0xE8 /* interrupt register */ -#define ATMEGA_UEINTX_TXINI (1 << 0) -#define ATMEGA_UEINTX_STALLEDI (1 << 1) -#define ATMEGA_UEINTX_RXOUTI (1 << 2) -#define ATMEGA_UEINTX_RXSTPI (1 << 3) /* received setup packet */ -#define ATMEGA_UEINTX_NAKOUTI (1 << 4) -#define ATMEGA_UEINTX_RWAL (1 << 5) -#define ATMEGA_UEINTX_NAKINI (1 << 6) -#define ATMEGA_UEINTX_FIFOCON (1 << 7) - -#define ATMEGA_UDMFN 0xE6 -#define ATMEGA_UDMFN_FNCERR (1 << 4) - -#define ATMEGA_UDFNUMH 0xE5 /* frame number high */ -#define ATMEGA_UDFNUMH_MASK 7 - -#define ATMEGA_UDFNUML 0xE4 /* frame number low */ -#define ATMEGA_UDFNUML_MASK 0xFF - -#define ATMEGA_FRAME_MASK 0x7FF - -#define ATMEGA_UDADDR 0xE3 /* USB address */ -#define ATMEGA_UDADDR_MASK 0x7F -#define ATMEGA_UDADDR_ADDEN (1 << 7) - -#define ATMEGA_UDIEN 0xE2 /* USB device interrupt enable */ -#define ATMEGA_UDINT_SUSPE (1 << 0) -#define ATMEGA_UDINT_MSOFE (1 << 1) -#define ATMEGA_UDINT_SOFE (1 << 2) -#define ATMEGA_UDINT_EORSTE (1 << 3) -#define ATMEGA_UDINT_WAKEUPE (1 << 4) -#define ATMEGA_UDINT_EORSME (1 << 5) -#define ATMEGA_UDINT_UPRSME (1 << 6) - -#define ATMEGA_UDINT 0xE1 /* USB device interrupt status */ -#define ATMEGA_UDINT_SUSPI (1 << 0) -#define ATMEGA_UDINT_MSOFI (1 << 1) -#define ATMEGA_UDINT_SOFI (1 << 2) -#define ATMEGA_UDINT_EORSTI (1 << 3) -#define ATMEGA_UDINT_WAKEUPI (1 << 4) -#define ATMEGA_UDINT_EORSMI (1 << 5) -#define ATMEGA_UDINT_UPRSMI (1 << 6) - -#define ATMEGA_UDCON 0xE0 /* USB device connection register */ -#define ATMEGA_UDCON_DETACH (1 << 0) -#define ATMEGA_UDCON_RMWKUP (1 << 1) -#define ATMEGA_UDCON_LSM (1 << 2) -#define ATMEGA_UDCON_RSTCPU (1 << 3) - -#define ATMEGA_USBINT 0xDA -#define ATMEGA_USBINT_VBUSTI (1 << 0) /* USB VBUS interrupt */ - -#define ATMEGA_USBSTA 0xD9 -#define ATMEGA_USBSTA_VBUS (1 << 0) -#define ATMEGA_USBSTA_ID (1 << 1) - -#define ATMEGA_USBCON 0xD8 -#define ATMEGA_USBCON_VBUSTE (1 << 0) -#define ATMEGA_USBCON_OTGPADE (1 << 4) -#define ATMEGA_USBCON_FRZCLK (1 << 5) -#define ATMEGA_USBCON_USBE (1 << 7) - -#define ATMEGA_UHWCON 0xD7 -#define ATMEGA_UHWCON_UVREGE (1 << 0) - -#define ATMEGA_READ_1(sc, reg) \ - bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) - -#define ATMEGA_WRITE_1(sc, reg, data) \ - bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) - -#define ATMEGA_WRITE_MULTI_1(sc, reg, ptr, len) \ - bus_space_write_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len) - -#define ATMEGA_READ_MULTI_1(sc, reg, ptr, len) \ - bus_space_read_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len) - -/* - * Maximum number of endpoints supported: - */ -#define ATMEGA_EP_MAX 7 - -struct atmegadci_td; - -typedef uint8_t (atmegadci_cmd_t)(struct atmegadci_td *td); -typedef void (atmegadci_clocks_t)(struct usb2_bus *); - -struct atmegadci_td { - struct atmegadci_td *obj_next; - atmegadci_cmd_t *func; - struct usb2_page_cache *pc; - uint32_t offset; - uint32_t remainder; - uint16_t max_packet_size; - 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 ep_no:3; -}; - -struct atmegadci_std_temp { - atmegadci_cmd_t *func; - struct usb2_page_cache *pc; - struct atmegadci_td *td; - struct atmegadci_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 atmegadci_config_desc { - struct usb2_config_descriptor confd; - struct usb2_interface_descriptor ifcd; - struct usb2_endpoint_descriptor endpd; -} __packed; - -union atmegadci_hub_temp { - uWord wValue; - struct usb2_port_status ps; -}; - -struct atmegadci_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 atmegadci_softc { - struct usb2_bus sc_bus; - union atmegadci_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; - - /* must be set by by the bus interface layer */ - atmegadci_clocks_t *sc_clocks_on; - atmegadci_clocks_t *sc_clocks_off; - - struct usb2_device *sc_devices[ATMEGA_MAX_DEVICES]; - struct resource *sc_irq_res; - void *sc_intr_hdl; -#if (ATMEGA_HAVE_BUS_SPACE != 0) - struct resource *sc_io_res; - bus_space_tag_t sc_io_tag; - bus_space_handle_t sc_io_hdl; -#endif - 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 atmegadci_flags sc_flags; -}; - -/* prototypes */ - -usb2_error_t atmegadci_init(struct atmegadci_softc *sc); -void atmegadci_uninit(struct atmegadci_softc *sc); -void atmegadci_suspend(struct atmegadci_softc *sc); -void atmegadci_resume(struct atmegadci_softc *sc); -void atmegadci_interrupt(struct atmegadci_softc *sc); - -#endif /* _ATMEGADCI_H_ */ diff --git a/sys/dev/usb2/controller/atmegadci_atmelarm.c b/sys/dev/usb2/controller/atmegadci_atmelarm.c deleted file mode 100644 index e63f5cc..0000000 --- a/sys/dev/usb2/controller/atmegadci_atmelarm.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -/*- - * Copyright (c) 2009 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/sys/dev/usb2/controller/ehci2.c b/sys/dev/usb2/controller/ehci2.c deleted file mode 100644 index 87a6d03..0000000 --- a/sys/dev/usb2/controller/ehci2.c +++ /dev/null @@ -1,3965 +0,0 @@ -/*- - * 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 - -#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_softc_t *sc, 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 void ehci_do_poll(struct usb2_bus *bus); -static void ehci_root_ctrl_poll(ehci_softc_t *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 { - ehci_softc_t *sc; - 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; -}; - -/* - * Byte-order conversion functions. - */ -static uint32_t -htoehci32(ehci_softc_t *sc, const uint32_t v) -{ - return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? - htobe32(v) : htole32(v)); -} - -static uint32_t -ehci32toh(ehci_softc_t *sc, const uint32_t v) -{ - return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? - be32toh(v) : le32toh(v)); -} - -void -ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) -{ - ehci_softc_t *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); - } -} - -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.bus_mtx, hz / 1000); - 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.bus_mtx, hz / 1000); - hcr = EOREAD4(sc, EHCI_USBCMD); - if (!(hcr & EHCI_CMD_HCRESET)) { - if (sc->sc_flags & EHCI_SCFLG_SETMODE) - EOWRITE4(sc, 0x68, 0x3); - 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; - - DPRINTF("start\n"); - - usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0); - -#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)); - - USB_BUS_LOCK(&sc->sc_bus); - err = ehci_hc_reset(sc); - USB_BUS_UNLOCK(&sc->sc_bus); - if (err) { - device_printf(sc->sc_bus.bdev, "reset timeout\n"); - return (err); - } - /* - * 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"); - return (USB_ERR_IOERROR); - } - /* 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 = - htoehci32(sc, buf_res.physaddr) | - htoehci32(sc, EHCI_LINK_QH); - - qh->qh_endp = - htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); - qh->qh_endphub = - htoehci32(sc, EHCI_QH_SET_MULT(1)); - qh->qh_curqtd = 0; - - qh->qh_qtd.qtd_next = - htoehci32(sc, EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_altnext = - htoehci32(sc, EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_status = - htoehci32(sc, 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 = htoehci32(sc, 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 = - htoehci32(sc, buf_res.physaddr) | - htoehci32(sc, EHCI_LINK_SITD); - - sitd->sitd_back = - htoehci32(sc, 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 = - htoehci32(sc, buf_res.physaddr) | - htoehci32(sc, 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 = - htoehci32(sc, buf_res.physaddr) | - htoehci32(sc, EHCI_LINK_QH); - - /* fill the QH */ - qh->qh_endp = - htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); - qh->qh_endphub = htoehci32(sc, EHCI_QH_SET_MULT(1)); - qh->qh_link = qh->qh_self; - qh->qh_curqtd = 0; - - /* fill the overlay qTD */ - qh->qh_qtd.qtd_next = htoehci32(sc, EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_altnext = htoehci32(sc, EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_status = htoehci32(sc, 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->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(NULL, hz / 1000); - hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; - if (!hcr) { - break; - } - } - if (hcr) { - device_printf(sc->sc_bus.bdev, "run timeout\n"); - return (USB_ERR_IOERROR); - } - - 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(ehci_softc_t *sc) -{ - USB_BUS_LOCK(&sc->sc_bus); - - usb2_callout_stop(&sc->sc_tmo_pcd); - - EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); - - if (ehci_hc_reset(sc)) { - DPRINTF("reset failed!\n"); - } - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* XXX let stray task complete */ - usb2_pause_mtx(NULL, hz / 20); - - usb2_callout_drain(&sc->sc_tmo_pcd); -} - -void -ehci_suspend(ehci_softc_t *sc) -{ - uint32_t cmd; - uint32_t hcr; - uint8_t i; - - USB_BUS_LOCK(&sc->sc_bus); - - 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.bus_mtx, hz / 1000); - } - - 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.bus_mtx, hz / 1000); - } - - if (hcr != EHCI_STS_HCH) { - device_printf(sc->sc_bus.bdev, - "config timeout\n"); - } - USB_BUS_UNLOCK(&sc->sc_bus); -} - -void -ehci_resume(ehci_softc_t *sc) -{ - struct usb2_page_search buf_res; - uint32_t cmd; - uint32_t hcr; - uint8_t i; - - USB_BUS_LOCK(&sc->sc_bus); - - /* 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.bus_mtx, - USB_MS_TO_TICKS(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.bus_mtx, hz / 1000); - } - if (hcr == EHCI_STS_HCH) { - device_printf(sc->sc_bus.bdev, "config timeout\n"); - } - - USB_BUS_UNLOCK(&sc->sc_bus); - - usb2_pause_mtx(NULL, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); - - /* catch any lost interrupts */ - ehci_do_poll(&sc->sc_bus); -} - -void -ehci_shutdown(ehci_softc_t *sc) -{ - DPRINTF("stopping the HC\n"); - - USB_BUS_LOCK(&sc->sc_bus); - - if (ehci_hc_reset(sc)) { - DPRINTF("reset failed!\n"); - } - USB_BUS_UNLOCK(&sc->sc_bus); -} - -#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))); - } -} - -static void -ehci_dump_link(ehci_softc_t *sc, uint32_t link, int type) -{ - link = ehci32toh(sc, 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(">"); - } -} - -static void -ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd) -{ - uint32_t s; - - printf(" next="); - ehci_dump_link(sc, qtd->qtd_next, 0); - printf(" altnext="); - ehci_dump_link(sc, qtd->qtd_altnext, 0); - printf("\n"); - s = ehci32toh(sc, 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, - ehci32toh(sc, qtd->qtd_buffer[s])); - } - for (s = 0; s < 5; s++) { - printf(" buffer_hi[%d]=0x%08x\n", s, - ehci32toh(sc, qtd->qtd_buffer_hi[s])); - } -} - -static uint8_t -ehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd) -{ - uint8_t temp; - - usb2_pc_cpu_invalidate(sqtd->page_cache); - printf("QTD(%p) at 0x%08x:\n", sqtd, ehci32toh(sc, sqtd->qtd_self)); - ehci_dump_qtd(sc, sqtd); - temp = (sqtd->qtd_next & htoehci32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0; - return (temp); -} - -static void -ehci_dump_sqtds(ehci_softc_t *sc, 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(sc, sqtd); - } - if (sqtd) { - printf("dump aborted, too many TDs\n"); - } -} - -static void -ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh) -{ - uint32_t endp; - uint32_t endphub; - - usb2_pc_cpu_invalidate(qh->page_cache); - printf("QH(%p) at 0x%08x:\n", qh, ehci32toh(sc, qh->qh_self) & ~0x1F); - printf(" link="); - ehci_dump_link(sc, qh->qh_link, 1); - printf("\n"); - endp = ehci32toh(sc, 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 = ehci32toh(sc, 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(sc, qh->qh_curqtd, 0); - printf("\n"); - printf("Overlay qTD:\n"); - ehci_dump_qtd(sc, (void *)&qh->qh_qtd); -} - -static void -ehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd) -{ - usb2_pc_cpu_invalidate(sitd->page_cache); - printf("SITD(%p) at 0x%08x\n", sitd, ehci32toh(sc, sitd->sitd_self) & ~0x1F); - printf(" next=0x%08x\n", ehci32toh(sc, sitd->sitd_next)); - printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", - ehci32toh(sc, sitd->sitd_portaddr), - (sitd->sitd_portaddr & htoehci32(sc, EHCI_SITD_SET_DIR_IN)) - ? "in" : "out", - EHCI_SITD_GET_ADDR(ehci32toh(sc, sitd->sitd_portaddr)), - EHCI_SITD_GET_ENDPT(ehci32toh(sc, sitd->sitd_portaddr)), - EHCI_SITD_GET_PORT(ehci32toh(sc, sitd->sitd_portaddr)), - EHCI_SITD_GET_HUBA(ehci32toh(sc, sitd->sitd_portaddr))); - printf(" mask=0x%08x\n", ehci32toh(sc, sitd->sitd_mask)); - printf(" status=0x%08x <%s> len=0x%x\n", ehci32toh(sc, sitd->sitd_status), - (sitd->sitd_status & htoehci32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", - EHCI_SITD_GET_LEN(ehci32toh(sc, sitd->sitd_status))); - printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", - ehci32toh(sc, sitd->sitd_back), - ehci32toh(sc, sitd->sitd_bp[0]), - ehci32toh(sc, sitd->sitd_bp[1]), - ehci32toh(sc, sitd->sitd_bp_hi[0]), - ehci32toh(sc, sitd->sitd_bp_hi[1])); -} - -static void -ehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd) -{ - usb2_pc_cpu_invalidate(itd->page_cache); - printf("ITD(%p) at 0x%08x\n", itd, ehci32toh(sc, itd->itd_self) & ~0x1F); - printf(" next=0x%08x\n", ehci32toh(sc, itd->itd_next)); - printf(" status[0]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[0]), - (itd->itd_status[0] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[1]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[1]), - (itd->itd_status[1] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[2]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[2]), - (itd->itd_status[2] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[3]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[3]), - (itd->itd_status[3] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[4]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[4]), - (itd->itd_status[4] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[5]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[5]), - (itd->itd_status[5] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[6]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[6]), - (itd->itd_status[6] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[7]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[7]), - (itd->itd_status[7] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" bp[0]=0x%08x\n", ehci32toh(sc, itd->itd_bp[0])); - printf(" addr=0x%02x; endpt=0x%01x\n", - EHCI_ITD_GET_ADDR(ehci32toh(sc, itd->itd_bp[0])), - EHCI_ITD_GET_ENDPT(ehci32toh(sc, itd->itd_bp[0]))); - printf(" bp[1]=0x%08x\n", ehci32toh(sc, itd->itd_bp[1])); - printf(" dir=%s; mpl=0x%02x\n", - (ehci32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", - EHCI_ITD_GET_MPL(ehci32toh(sc, itd->itd_bp[1]))); - printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", - ehci32toh(sc, itd->itd_bp[2]), - ehci32toh(sc, itd->itd_bp[3]), - ehci32toh(sc, itd->itd_bp[4]), - ehci32toh(sc, itd->itd_bp[5]), - ehci32toh(sc, itd->itd_bp[6])); - printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" - " 0x%08x,0x%08x,0x%08x\n", - ehci32toh(sc, itd->itd_bp_hi[0]), - ehci32toh(sc, itd->itd_bp_hi[1]), - ehci32toh(sc, itd->itd_bp_hi[2]), - ehci32toh(sc, itd->itd_bp_hi[3]), - ehci32toh(sc, itd->itd_bp_hi[4]), - ehci32toh(sc, itd->itd_bp_hi[5]), - ehci32toh(sc, itd->itd_bp_hi[6])); -} - -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(sc, itd); - itd = itd->prev; - } - - while (sitd && max && max--) { - ehci_dump_sitd(sc, sitd); - sitd = sitd->prev; - } -} - -#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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout); - } -} - -#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); - - if (sqh->prev != NULL) { - /* should not happen */ - DPRINTFN(0, "QH already linked!\n"); - return (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); - - 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); - } - 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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - 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; - - if (xfer->aframes != xfer->nframes) { - xfer->frlengths[xfer->aframes] = 0; - } - while (1) { - - usb2_pc_cpu_invalidate(td->page_cache); - status = ehci32toh(sc, td->qtd_status); - - len = EHCI_QTD_GET_BYTES(status); - - /* - * Verify the status length and - * add the length to "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] += td->len - 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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - - ehci_dump_sqtds(sc, 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); -} - -/*------------------------------------------------------------------------* - * 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; - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - - 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 = ehci32toh(sc, td->sitd_status); - - /* also check if first is complete */ - - td = xfer->td_transfer_first; - usb2_pc_cpu_invalidate(td->page_cache); - status |= ehci32toh(sc, 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 & htoehci32(sc, 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 = ehci32toh(sc, 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) -{ - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -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; - } - } -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK(&sc->sc_bus); - - 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: - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/* - * called when a request does not complete - */ -static void -ehci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - ehci_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -ehci_do_poll(struct usb2_bus *bus) -{ - ehci_softc_t *sc = EHCI_BUS2SC(bus); - - USB_BUS_LOCK(&sc->sc_bus); - ehci_interrupt_poll(sc); - ehci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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 = htoehci32(temp->sc, 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 | - htoehci32(temp->sc, EHCI_QTD_SET_BYTES(average)); - - if (average == 0) { - - if (temp->auto_data_toggle == 0) { - - /* update data toggle, ZLP case */ - - temp->qtd_status ^= - htoehci32(temp->sc, 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 ^= - htoehci32(temp->sc, 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] = - htoehci32(temp->sc, 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] = - htoehci32(temp->sc, - 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] = - htoehci32(temp->sc, - 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; -} - -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->xroot->udev)); - - temp.average = xfer->max_usb2_frame_size; - temp.max_frame_size = xfer->max_frame_size; - temp.sc = EHCI_BUS2SC(xfer->xroot->bus); - - /* 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 |= - htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); - } - temp.auto_data_toggle = 0; - } else { - temp.auto_data_toggle = 1; - } - - if (usb2_get_speed(xfer->xroot->udev) != USB_SPEED_HIGH) { - /* max 3 retries */ - temp.qtd_status |= - htoehci32(temp.sc, 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 &= - htoehci32(temp.sc, 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 &= - htoehci32(temp.sc, 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) ? - htoehci32(temp.sc, EHCI_QTD_ACTIVE | - EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : - htoehci32(temp.sc, 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 &= htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) | - EHCI_QTD_SET_TOGGLE(1)); - temp.qtd_status |= - (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? - htoehci32(temp.sc, EHCI_QTD_ACTIVE | - EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | - EHCI_QTD_SET_TOGGLE(1)) : - htoehci32(temp.sc, 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 = htoehci32(temp.sc, EHCI_LINK_TERMINATE); - td->qtd_altnext = htoehci32(temp.sc, EHCI_LINK_TERMINATE); - td->qtd_status |= htoehci32(temp.sc, 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(temp.sc, - 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->xroot->udev) == USB_SPEED_HIGH) { - qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | - EHCI_QH_DTC); - if (methods != &ehci_device_intr_methods) - qh_endp |= EHCI_QH_SET_NRL(8); - } else { - - if (usb2_get_speed(xfer->xroot->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 = htoehci32(temp.sc, 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->xroot->udev->hs_hub_addr) | - EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no)); - - qh->qh_endphub = htoehci32(temp.sc, qh_endphub); - qh->qh_curqtd = htoehci32(temp.sc, 0); - - /* fill the overlay qTD */ - qh->qh_qtd.qtd_status = htoehci32(temp.sc, 0); - - if (temp.auto_data_toggle) { - - /* let the hardware compute the data toggle */ - - qh->qh_endp &= htoehci32(temp.sc, ~EHCI_QH_DTC); - - if (xfer->pipe->toggle_next) { - /* DATA1 is next */ - qh->qh_qtd.qtd_status |= - htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); - } - } - td = xfer->td_transfer_first; - - qh->qh_qtd.qtd_next = td->qtd_self; - qh->qh_qtd.qtd_altnext = - htoehci32(temp.sc, EHCI_LINK_TERMINATE); - - usb2_pc_cpu_flush(qh->page_cache); - - if (xfer->xroot->udev->pwr_save.suspended == 0) { - EHCI_APPEND_QH(qh, *qh_last); - } -} - -static void -ehci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - uint16_t i; - uint16_t m; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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(sc, td); - } -#endif - usb2_pc_cpu_invalidate(td->page_cache); - status = ehci32toh(sc, 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; -} - -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(sc, td); - } -#endif - - usb2_pc_cpu_invalidate(td->page_cache); - status = ehci32toh(sc, 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; -} - -/* 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 = EHCI_BUS2SC(xfer->xroot->bus); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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(sc, - 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - uint16_t best; - uint16_t bit; - uint16_t x; - uint8_t slot; - - /* Allocate a microframe slot first: */ - - slot = usb2_intr_schedule_adjust - (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); - - if (usb2_get_speed(xfer->xroot->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); -} - -static void -ehci_device_intr_close(struct usb2_xfer *xfer) -{ - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - uint8_t slot; - - slot = usb2_intr_schedule_adjust - (xfer->xroot->udev, -(xfer->max_frame_size), xfer->usb2_uframe); - - sc->sc_intr_stat[xfer->qh_pos]--; - - ehci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - 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->xroot->udev->hs_hub_addr) | - EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no); - - if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { - sitd_portaddr |= EHCI_SITD_SET_DIR_IN; - } - sitd_portaddr = htoehci32(sc, 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 = htoehci32(sc, EHCI_LINK_TERMINATE); - - usb2_pc_cpu_flush(td->page_cache); - } - } -} - -static void -ehci_device_isoc_fs_close(struct usb2_xfer *xfer) -{ - ehci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) -{ - struct usb2_page_search buf_res; - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - 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->xroot->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] = htoehci32(sc, 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] = htoehci32(sc, temp); - - td->sitd_mask = htoehci32(sc, 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(sc, 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); -} - -static void -ehci_device_isoc_fs_start(struct usb2_xfer *xfer) -{ - /* put transfer on interrupt queue */ - ehci_transfer_intr_enqueue(xfer); -} - -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_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - 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] = htoehci32(sc, temp); - - /* set transfer multiplier */ - td->itd_bp[2] = htoehci32(sc, xfer->max_packet_count & 3); - - usb2_pc_cpu_flush(td->page_cache); - } - } -} - -static void -ehci_device_isoc_hs_close(struct usb2_xfer *xfer) -{ - ehci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) -{ - struct usb2_page_search buf_res; - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - 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] = htoehci32(sc, 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] &= htoehci32(sc, 0xFFF); - td->itd_bp[0] |= htoehci32(sc, 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] |= htoehci32(sc, 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] &= htoehci32(sc, 0xFFF); - td->itd_bp[page_no] |= htoehci32(sc, page_addr); - } - } - } - /* set IOC bit if we are complete */ - if (nframes == 0) { - td->itd_status[7] |= htoehci32(sc, 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(sc, 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); -} - -static void -ehci_device_isoc_hs_start(struct usb2_xfer *xfer) -{ - /* put transfer on interrupt queue */ - ehci_transfer_intr_enqueue(xfer); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - ehci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* 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 = EHCI_BUS2SC(xfer->xroot->bus); - - DPRINTF("\n"); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -ehci_root_ctrl_task(struct usb2_bus *bus) -{ - ehci_root_ctrl_poll(EHCI_BUS2SC(bus)); -} - -static void -ehci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - char *ptr; - uint32_t port; - uint32_t v; - uint16_t i; - uint16_t value; - uint16_t index; - uint8_t l; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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: - if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) { - - /* - * waking up a High Speed device is rather - * complicated if - */ - EOWRITE4(sc, port, v | EHCI_PS_FPR); - } - /* wait 20ms for resume sequence to complete */ - if (use_polling) { - /* polling */ - DELAY(20000); - } else { - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); - } - - EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP | - EHCI_PS_FPR | (3 << 10) /* High Speed */ )); - - /* settle time */ - if (use_polling) { - /* polling */ - DELAY(4000); - } else { - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); - } - 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)); - /* XXX can't find out? */ - sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; - 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); - if (sc->sc_flags & EHCI_SCFLG_FORCESPEED) { - if ((v & 0xc000000) == 0x8000000) - i = UPS_HIGH_SPEED; - else if ((v & 0xc000000) == 0x4000000) - i = UPS_LOW_SPEED; - else - i = 0; - } else { - 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) && !(v & EHCI_PS_FPR)) - 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 (v & EHCI_PS_FPR) - i |= UPS_C_SUSPEND; - 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.bus_mtx, - USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY)); - } - - /* Terminate reset sequence. */ - if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM)) - 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.bus_mtx, - USB_MS_TO_TICKS(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(ehci_softc_t *sc) -{ - usb2_sw_transfer(&sc->sc_root_ctrl, - &ehci_root_ctrl_done); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - ehci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = EHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -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; - - /* - * 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 = htoehci32(sc, 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 = htoehci32(sc, 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 = htoehci32(sc, 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 = htoehci32(sc, 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; - } -} - -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 */ -} - -static void -ehci_device_resume(struct usb2_device *udev) -{ - ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - - if ((methods == &ehci_device_bulk_methods) || - (methods == &ehci_device_ctrl_methods)) { - EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], - sc->sc_async_p_last); - } - if (methods == &ehci_device_intr_methods) { - EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], - sc->sc_intr_p_last[xfer->qh_pos]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -ehci_device_suspend(struct usb2_device *udev) -{ - ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - - if ((methods == &ehci_device_bulk_methods) || - (methods == &ehci_device_ctrl_methods)) { - 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]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -ehci_set_hw_power(struct usb2_bus *bus) -{ - ehci_softc_t *sc = EHCI_BUS2SC(bus); - uint32_t temp; - uint32_t flags; - - DPRINTF("\n"); - - USB_BUS_LOCK(bus); - - flags = bus->hw_power_state; - - temp = EOREAD4(sc, EHCI_USBCMD); - - temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE); - - if (flags & (USB_HW_POWER_CONTROL | - USB_HW_POWER_BULK)) { - DPRINTF("Async is active\n"); - temp |= EHCI_CMD_ASE; - } - if (flags & (USB_HW_POWER_INTERRUPT | - USB_HW_POWER_ISOC)) { - DPRINTF("Periodic is active\n"); - temp |= EHCI_CMD_PSE; - } - EOWRITE4(sc, EHCI_USBCMD, temp); - - USB_BUS_UNLOCK(bus); - - 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, - .device_resume = ehci_device_resume, - .device_suspend = ehci_device_suspend, - .set_hw_power = ehci_set_hw_power, - .roothub_exec = ehci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/ehci2.h b/sys/dev/usb2/controller/ehci2.h deleted file mode 100644 index 9d7baa1..0000000 --- a/sys/dev/usb2/controller/ehci2.h +++ /dev/null @@ -1,532 +0,0 @@ -/* $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_ - -#define EHCI_MAX_DEVICES USB_MAX_DEVICES - -/* 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_USBMODE 0x68 /* RW USB Device mode register */ -#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */ -#define EHCI_UM_CM_IDLE 0x0 /* Idle */ -#define EHCI_UM_CM_HOST 0x3 /* Host Controller */ -#define EHCI_UM_ES 0x00000004 /* R/WO Endian Select */ -#define EHCI_UM_ES_LE 0x0 /* Little-endian byte alignment */ -#define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */ -#define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */ - -#define EHCI_PORT_RESET_COMPLETE 2 /* ms */ - -/* - * 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_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 usb2_device *sc_devices[EHCI_MAX_DEVICES]; - 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; - 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 */ - uint16_t sc_flags; /* chip specific flags */ -#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init */ -#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed */ -#define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence */ -#define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */ -#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */ -#define EHCI_SCFLG_TT 0x0020 /* transaction translator present */ - - 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_ixp4xx.c b/sys/dev/usb2/controller/ehci2_ixp4xx.c deleted file mode 100644 index 91ce863..0000000 --- a/sys/dev/usb2/controller/ehci2_ixp4xx.c +++ /dev/null @@ -1,349 +0,0 @@ -/*- - * Copyright (c) 2008 Sam Leffler. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * IXP435 attachment driver for the USB Enhanced Host Controller. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_bus.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define EHCI_VENDORID_IXP4XX 0x42fa05 -#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller" - -struct ixp_ehci_softc { - ehci_softc_t base; /* storage for EHCI code */ - bus_space_tag_t iot; - bus_space_handle_t ioh; - struct bus_space tag; /* tag for private bus space ops */ -}; - -static device_attach_t ehci_ixp_attach; -static device_detach_t ehci_ixp_detach; -static device_shutdown_t ehci_ixp_shutdown; -static device_suspend_t ehci_ixp_suspend; -static device_resume_t ehci_ixp_resume; - -static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t); -static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t); -static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t); -static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t); -static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t); -static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t); - -static int -ehci_ixp_suspend(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - int err; - - err = bus_generic_suspend(self); - if (err) - return (err); - ehci_suspend(sc); - return (0); -} - -static int -ehci_ixp_resume(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - - ehci_resume(sc); - - bus_generic_resume(self); - - return (0); -} - -static int -ehci_ixp_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 int -ehci_ixp_probe(device_t self) -{ - - device_set_desc(self, EHCI_HC_DEVSTR); - - return (BUS_PROBE_DEFAULT); -} - -static int -ehci_ixp_attach(device_t self) -{ - struct ixp_ehci_softc *isc = device_get_softc(self); - ehci_softc_t *sc = &isc->base; - int err; - int rid; - - /* initialise some bus fields */ - sc->sc_bus.parent = self; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = EHCI_MAX_DEVICES; - - /* 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_bus.usbrev = USB_REV_2_0; - - /* NB: hints fix the memory location and irq */ - - rid = 0; - 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; - } - - /* - * Craft special resource for bus space ops that handle - * byte-alignment of non-word addresses. Also, since - * we're already intercepting bus space ops we handle - * the register window offset that could otherwise be - * done with bus_space_subregion. - */ - isc->iot = rman_get_bustag(sc->sc_io_res); - isc->tag.bs_cookie = isc->iot; - /* read single */ - isc->tag.bs_r_1 = ehci_bs_r_1, - isc->tag.bs_r_2 = ehci_bs_r_2, - isc->tag.bs_r_4 = ehci_bs_r_4, - /* write (single) */ - isc->tag.bs_w_1 = ehci_bs_w_1, - isc->tag.bs_w_2 = ehci_bs_w_2, - isc->tag.bs_w_4 = ehci_bs_w_4, - - sc->sc_io_tag = &isc->tag; - sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); - sc->sc_io_size = IXP435_USB1_SIZE - 0x100; - - rid = 0; - sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, - 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); - device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); - - sprintf(sc->sc_vendor, "Intel"); - - - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); - if (err) { - device_printf(self, "Could not setup irq, %d\n", err); - sc->sc_intr_hdl = NULL; - goto error; - } - - /* - * Arrange to force Host mode, select big-endian byte alignment, - * and arrange to not terminate reset operations (the adapter - * will ignore it if we do but might as well save a reg write). - * Also, the controller has an embedded Transaction Translator - * which means port speed must be read from the Port Status - * register following a port enable. - */ - sc->sc_flags |= EHCI_SCFLG_TT - | EHCI_SCFLG_SETMODE - | EHCI_SCFLG_BIGEDESC - | EHCI_SCFLG_BIGEMMIO - | EHCI_SCFLG_NORESTERM - ; - - 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_ixp_detach(self); - return (ENXIO); -} - -static int -ehci_ixp_detach(device_t self) -{ - struct ixp_ehci_softc *isc = device_get_softc(self); - ehci_softc_t *sc = &isc->base; - device_t bdev; - int err; - - 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 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); - - 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, 0, - sc->sc_io_res); - sc->sc_io_res = NULL; - } - usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); - - return (0); -} - -/* - * Bus space accessors for PIO operations. - */ - -static uint8_t -ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o) -{ - return bus_space_read_1((bus_space_tag_t) t, h, - 0x100 + (o &~ 3) + (3 - (o & 3))); -} - -static void -ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v) -{ - panic("%s", __func__); -} - -static uint16_t -ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o) -{ - return bus_space_read_2((bus_space_tag_t) t, h, - 0x100 + (o &~ 3) + (2 - (o & 3))); -} - -static void -ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v) -{ - panic("%s", __func__); -} - -static uint32_t -ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o) -{ - return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o); -} - -static void -ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v) -{ - bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v); -} - -static device_method_t ehci_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, ehci_ixp_probe), - DEVMETHOD(device_attach, ehci_ixp_attach), - DEVMETHOD(device_detach, ehci_ixp_detach), - DEVMETHOD(device_suspend, ehci_ixp_suspend), - DEVMETHOD(device_resume, ehci_ixp_resume), - DEVMETHOD(device_shutdown, ehci_ixp_shutdown), - - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - - {0, 0} -}; - -static driver_t ehci_driver = { - "ehci", - ehci_methods, - sizeof(struct ixp_ehci_softc), -}; - -static devclass_t ehci_devclass; - -DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb2_controller, 1, 1, 1); -MODULE_DEPEND(ehci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/ehci2_mbus.c b/sys/dev/usb2/controller/ehci2_mbus.c deleted file mode 100644 index 404617b..0000000 --- a/sys/dev/usb2/controller/ehci2_mbus.c +++ /dev/null @@ -1,365 +0,0 @@ -/*- - * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. - * All rights reserved. - * - * Developed by Semihalf. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of MARVELL nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * MBus attachment driver for the USB Enhanced Host Controller. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_bus.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define EHCI_VENDORID_MRVL 0x1286 -#define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller" - -static device_attach_t ehci_mbus_attach; -static device_detach_t ehci_mbus_detach; -static device_shutdown_t ehci_mbus_shutdown; -static device_suspend_t ehci_mbus_suspend; -static device_resume_t ehci_mbus_resume; - -static int err_intr(void *arg); - -static struct resource *irq_err; -static void *ih_err; - -#define USB_BRIDGE_INTR_CAUSE 0x210 -#define USB_BRIDGE_INTR_MASK 0x214 - -#define MV_USB_ADDR_DECODE_ERR (1 << 0) -#define MV_USB_HOST_UNDERFLOW (1 << 1) -#define MV_USB_HOST_OVERFLOW (1 << 2) -#define MV_USB_DEVICE_UNDERFLOW (1 << 3) - -static int -ehci_mbus_suspend(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - int err; - - err = bus_generic_suspend(self); - if (err) - return (err); - ehci_suspend(sc); - return (0); -} - -static int -ehci_mbus_resume(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - - ehci_resume(sc); - - bus_generic_resume(self); - - return (0); -} - -static int -ehci_mbus_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 int -ehci_mbus_probe(device_t self) -{ - - device_set_desc(self, EHCI_HC_DEVSTR); - - return (BUS_PROBE_DEFAULT); -} - -static int -ehci_mbus_attach(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - bus_space_handle_t bsh; - int err; - int rid; - - /* initialise some bus fields */ - sc->sc_bus.parent = self; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = EHCI_MAX_DEVICES; - - /* 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_bus.usbrev = USB_REV_2_0; - - rid = 0; - 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); - bsh = rman_get_bushandle(sc->sc_io_res); - sc->sc_io_size = MV_USB_SIZE - MV_USB_HOST_OFST; - - /* - * Marvell EHCI host controller registers start at certain offset within - * the whole USB registers range, so create a subregion for the host - * mode configuration purposes. - */ - if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST, - sc->sc_io_size, &sc->sc_io_hdl) != 0) - panic("%s: unable to subregion USB host registers", - device_get_name(self)); - - rid = 0; - irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, - RF_SHAREABLE | RF_ACTIVE); - if (irq_err == NULL) { - device_printf(self, "Could not allocate error irq\n"); - ehci_mbus_detach(self); - return (ENXIO); - } - - /* - * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to - * use the correct rid for the main one (controller interrupt) -- - * refer to obio_devices[] for the right resource number to use here. - */ - rid = 1; - sc->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); - device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); - - sprintf(sc->sc_vendor, "Marvell"); - - err = bus_setup_intr(self, irq_err, INTR_FAST | INTR_TYPE_BIO, - err_intr, NULL, sc, &ih_err); - if (err) { - device_printf(self, "Could not setup error irq, %d\n", err); - ih_err = NULL; - goto error; - } - - EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR | - MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW | - MV_USB_DEVICE_UNDERFLOW); - - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); - if (err) { - device_printf(self, "Could not setup irq, %d\n", err); - sc->sc_intr_hdl = NULL; - goto error; - } - - /* - * Workaround for Marvell integrated EHCI controller: reset of - * the EHCI core clears the USBMODE register, which sets the core in - * an undefined state (neither host nor agent), so it needs to be set - * again for proper operation. - * - * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for - * details. - */ - sc->sc_flags |= EHCI_SCFLG_SETMODE; - if (bootverbose) - device_printf(self, "5.24 GL USB-2 workaround enabled\n"); - - /* XXX all MV chips need it? */ - sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM; - - err = ehci_init(sc); - if (!err) { - 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_mbus_detach(self); - return (ENXIO); -} - -static int -ehci_mbus_detach(device_t self) -{ - ehci_softc_t *sc = device_get_softc(self); - device_t bdev; - int err; - - 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 ehci_init - */ - if (sc->sc_io_res) { - EWRITE4(sc, EHCI_USBINTR, 0); - EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); - } - if (sc->sc_irq_res && sc->sc_intr_hdl) { - /* - * only call ehci_detach() after ehci_init() - */ - ehci_detach(sc); - - 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 (irq_err && ih_err) { - err = bus_teardown_intr(self, irq_err, ih_err); - - if (err) - device_printf(self, "Could not tear down irq, %d\n", - err); - ih_err = NULL; - } - if (irq_err) { - bus_release_resource(self, SYS_RES_IRQ, 0, irq_err); - irq_err = NULL; - } - if (sc->sc_irq_res) { - bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); - sc->sc_irq_res = NULL; - } - if (sc->sc_io_res) { - bus_release_resource(self, SYS_RES_MEMORY, 0, - sc->sc_io_res); - sc->sc_io_res = NULL; - } - usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); - - return (0); -} - -static int -err_intr(void *arg) -{ - ehci_softc_t *sc = arg; - unsigned int cause; - - cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE); - if (cause) { - printf("IRQ ERR: cause: 0x%08x\n", cause); - if (cause & MV_USB_ADDR_DECODE_ERR) - printf("IRQ ERR: Address decoding error\n"); - if (cause & MV_USB_HOST_UNDERFLOW) - printf("IRQ ERR: USB Host Underflow\n"); - if (cause & MV_USB_HOST_OVERFLOW) - printf("IRQ ERR: USB Host Overflow\n"); - if (cause & MV_USB_DEVICE_UNDERFLOW) - printf("IRQ ERR: USB Device Underflow\n"); - if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW | - MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW)) - printf("IRQ ERR: Unknown error\n"); - - EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0); - } - return (FILTER_HANDLED); -} - -static device_method_t ehci_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, ehci_mbus_probe), - DEVMETHOD(device_attach, ehci_mbus_attach), - DEVMETHOD(device_detach, ehci_mbus_detach), - DEVMETHOD(device_suspend, ehci_mbus_suspend), - DEVMETHOD(device_resume, ehci_mbus_resume), - DEVMETHOD(device_shutdown, ehci_mbus_shutdown), - - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - - {0, 0} -}; - -static driver_t ehci_driver = { - "ehci", - ehci_methods, - sizeof(ehci_softc_t), -}; - -static devclass_t ehci_devclass; - -DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb2_controller, 1, 1, 1); -MODULE_DEPEND(ehci, usb2_core, 1, 1, 1); diff --git a/sys/dev/usb2/controller/ehci2_pci.c b/sys/dev/usb2/controller/ehci2_pci.c deleted file mode 100644 index 5e39c8f..0000000 --- a/sys/dev/usb2/controller/ehci2_pci.c +++ /dev/null @@ -1,487 +0,0 @@ -/*- - * 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 - -#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; - - /* initialise some bus fields */ - sc->sc_bus.parent = self; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = EHCI_MAX_DEVICES; - - /* get all DMA memory */ - if (usb2_bus_mem_alloc_all(&sc->sc_bus, - USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { - return (ENOMEM); - } - - 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: - /* Quirk for Parallels Desktop 4.0 */ - device_printf(self, "USB revision is unknown. Assuming v2.0.\n"); - sc->sc_bus.usbrev = USB_REV_2_0; - 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)); - } - -#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; - - 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_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, hz / 100); /* wait 10ms */ - } - } -} - -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 deleted file mode 100644 index fd6b6d3..0000000 --- a/sys/dev/usb2/controller/musb2_otg.c +++ /dev/null @@ -1,2875 +0,0 @@ -/* $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 - -#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 *, usb2_error_t); -static void musbotg_do_poll(struct usb2_bus *); -static void musbotg_root_ctrl_poll(struct musbotg_softc *); -static void musbotg_standard_done(struct usb2_xfer *); -static void musbotg_interrupt_poll(struct musbotg_softc *); - -static usb2_sw_transfer_func_t musbotg_root_intr_done; -static usb2_sw_transfer_func_t musbotg_root_ctrl_done; - -/* - * 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; - } -} - -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 */ - } -} - -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; - } -} - -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); -} - -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); - } -} - -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); - } -} - -static void -musbotg_wakeup_peer(struct usb2_xfer *xfer) -{ - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - uint8_t temp; - uint8_t use_polling; - - if (!(sc->sc_flags.status_suspend)) { - return; - } - use_polling = mtx_owned(xfer->xroot->xfer_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.bus_mtx, hz / 125); - } - - temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); - temp &= ~MUSB2_MASK_RESUME; - MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); -} - -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); -} - -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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - - /* 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; - } - } -} - -void -musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on) -{ - DPRINTFN(4, "vbus = %u\n", is_on); - - USB_BUS_LOCK(&sc->sc_bus); - 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); - } - } - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; - - USB_BUS_LOCK(&sc->sc_bus); - -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; - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; -} - -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->xroot->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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - 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; -} - -static void -musbotg_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTFN(1, "xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - musbotg_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -musbotg_ep_int_set(struct usb2_xfer *xfer, uint8_t on) -{ - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - 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); - } - } -} - -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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, - &musbotg_timeout, xfer->timeout); - } - } -} - -static void -musbotg_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - - DPRINTFN(8, "\n"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -/*------------------------------------------------------------------------* - * 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) -{ - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); -} - -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; - - USB_BUS_LOCK_ASSERT(udev->bus, 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); - } -} - -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); - } - } -} - -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); - - USB_BUS_LOCK_ASSERT(udev->bus, 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))); -} - -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; - - USB_BUS_LOCK(&sc->sc_bus); - - /* 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.bus_mtx, hz / 1000); - - /* 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.bus_mtx, hz / 100); - - /* 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); - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch any lost interrupts */ - - musbotg_do_poll(&sc->sc_bus); - - return (0); /* success */ -} - -void -musbotg_uninit(struct musbotg_softc *sc) -{ - USB_BUS_LOCK(&sc->sc_bus); - - /* 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); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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); - - USB_BUS_LOCK(&sc->sc_bus); - musbotg_interrupt_poll(sc); - musbotg_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -static void -musbotg_device_isoc_enter(struct usb2_xfer *xfer) -{ - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - 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->xroot->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); -} - -static void -musbotg_device_isoc_start(struct usb2_xfer *xfer) -{ - /* start TD chain */ - musbotg_start_standard_chain(xfer); -} - -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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - musbotg_device_done(xfer, USB_ERR_CANCELLED); -} - -/* - * 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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -musbotg_root_ctrl_task(struct usb2_bus *bus) -{ - musbotg_root_ctrl_poll(MUSBOTG_BUS2SC(bus)); -} - -static void -musbotg_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - uint16_t value; - uint16_t index; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); -} - -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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - musbotg_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = MUSBOTG_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -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; - - /* - * 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; -} - -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; - } - } -} - -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, - .roothub_exec = &musbotg_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/musb2_otg.h b/sys/dev/usb2/controller/musb2_otg.h deleted file mode 100644 index 0d880e1..0000000 --- a/sys/dev/usb2/controller/musb2_otg.h +++ /dev/null @@ -1,407 +0,0 @@ -/* $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_ - -#define MUSB2_MAX_DEVICES (USB_MIN_DEVICES + 1) - -/* 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_hw_ep_profile sc_hw_ep_profile[16]; - - struct usb2_device *sc_devices[MUSB2_MAX_DEVICES]; - 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); -void musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on); - -#endif /* _MUSB2_OTG_H_ */ diff --git a/sys/dev/usb2/controller/musb2_otg_atmelarm.c b/sys/dev/usb2/controller/musb2_otg_atmelarm.c deleted file mode 100644 index 6477c97..0000000 --- a/sys/dev/usb2/controller/musb2_otg_atmelarm.c +++ /dev/null @@ -1,240 +0,0 @@ -/* $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 - -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_poll(struct musbotg_super_softc *sc) -{ - uint8_t vbus_val = 1; /* fake VBUS on - TODO */ - - /* just forward it */ - musbotg_vbus_interrupt(&sc->sc_otg, vbus_val); -} - -static void -musbotg_clocks_on(void *arg) -{ -#if 0 - struct musbotg_super_softc *sc = arg; - -#endif -} - -static void -musbotg_clocks_off(void *arg) -{ -#if 0 - struct musbotg_super_softc *sc = arg; - -#endif -} - -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; - - /* 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; - - /* initialise some bus fields */ - sc->sc_otg.sc_bus.parent = dev; - sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; - sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES; - - /* 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); - -#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_poll(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_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 deleted file mode 100644 index 4c65ea7..0000000 --- a/sys/dev/usb2/controller/ohci2.c +++ /dev/null @@ -1,2862 +0,0 @@ -/*- - * 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 - -#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 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); - } -} - -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(NULL, hz / 1000); - 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(NULL, - USB_MS_TO_TICKS(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(NULL, - USB_MS_TO_TICKS(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(NULL, - USB_MS_TO_TICKS(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(NULL, - USB_MS_TO_TICKS(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; - - 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.bus_mtx, 0); - -#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)) { - return (USB_ERR_INVAL); - } else { - /* 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) -{ - USB_BUS_LOCK(&sc->sc_bus); - - usb2_callout_stop(&sc->sc_tmo_rhsc); - - OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); - OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* XXX let stray task complete */ - usb2_pause_mtx(NULL, hz / 20); - - usb2_callout_drain(&sc->sc_tmo_rhsc); -} - -/* NOTE: suspend/resume is called from - * interrupt context and cannot sleep! - */ -void -ohci_suspend(ohci_softc_t *sc) -{ - uint32_t ctl; - - USB_BUS_LOCK(&sc->sc_bus); - -#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.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -void -ohci_resume(ohci_softc_t *sc) -{ - uint32_t ctl; - -#if USB_DEBUG - DPRINTF("\n"); - if (ohcidebug > 2) { - ohci_dumpregs(sc); - } -#endif - /* some broken BIOSes never initialize the Controller chip */ - ohci_controller_init(sc); - - USB_BUS_LOCK(&sc->sc_bus); - 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.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_DELAY)); - ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; - OWRITE4(sc, OHCI_CONTROL, ctl); - usb2_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_RECOVERY)); - sc->sc_control = sc->sc_intre = 0; - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch any lost interrupts */ - ohci_do_poll(&sc->sc_bus); -} - -#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)); -} -static void -ohci_dump_tds(ohci_td_t *std) -{ - for (; std; std = std->obj_next) { - if (ohci_dump_td(std)) { - break; - } - } -} - -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; - } - } -} - -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)); -} - -#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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout); - } -} - -#define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last) -static ohci_ed_t * -_ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last) -{ - DPRINTFN(11, "%p to %p\n", sed, last); - - if (sed->prev != NULL) { - /* should not happen */ - DPRINTFN(0, "ED already linked!\n"); - return (last); - } - /* (sc->sc_bus.bus_mtx) must be locked */ - - sed->next = last->next; - sed->ed_next = last->ed_next; - sed->ed_tailp = 0; - - 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.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); - } - 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); -} - -#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; - - if (xfer->aframes != xfer->nframes) { - xfer->frlengths[xfer->aframes] = 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) { - /* - * Sum up total transfer length - * in "frlengths[]": - */ - xfer->frlengths[xfer->aframes] += td->len - temp; - } - } else { - if (xfer->aframes != xfer->nframes) { - /* transfer was complete */ - xfer->frlengths[xfer->aframes] += td->len; - } - } - /* 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); -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -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; - } - } -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK(&sc->sc_bus); - - 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: - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/* - * called when a request does not complete - */ -static void -ohci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - ohci_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -ohci_do_poll(struct usb2_bus *bus) -{ - struct ohci_softc *sc = OHCI_BUS2SC(bus); - - USB_BUS_LOCK(&sc->sc_bus); - ohci_interrupt_poll(sc); - ohci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; -} - -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->xroot->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->xroot->udev->speed == USB_SPEED_LOW) { - ed_flags |= OHCI_ED_SPEED; - } - ed->ed_flags = htole32(ed_flags); - - td = xfer->td_transfer_first; - - ed->ed_headp = td->td_self; - - if (xfer->xroot->udev->pwr_save.suspended == 0) { - /* the append function will flush the endpoint descriptor */ - OHCI_APPEND_QH(ed, *ed_last); - - if (methods == &ohci_device_bulk_methods) { - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); - } - if (methods == &ohci_device_ctrl_methods) { - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); - } - } else { - usb2_pc_cpu_flush(ed->page_cache); - } -} - -static void -ohci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - uint32_t hstatus; - uint16_t i; - uint16_t m; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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 = OHCI_BUS2SC(xfer->xroot->bus); - ohci_ed_t *ed; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - 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); -} - -static void -ohci_device_intr_close(struct usb2_xfer *xfer) -{ - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_intr_stat[xfer->qh_pos]--; - - ohci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - /* 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); -} - -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); -} - -static void -ohci_device_isoc_enter(struct usb2_xfer *xfer) -{ - struct usb2_page_search buf_res; - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - 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->xroot->udev->speed == USB_SPEED_LOW) { - ed_flags |= OHCI_ED_SPEED; - } - ed->ed_flags = htole32(ed_flags); - - td = xfer->td_transfer_first; - - ed->ed_headp = td->itd_self; - - /* isochronous transfers are not affected by suspend / resume */ - /* the append function will flush the endpoint descriptor */ - - OHCI_APPEND_QH(ed, sc->sc_isoc_p_last); -} - -static void -ohci_device_isoc_start(struct usb2_xfer *xfer) -{ - /* put transfer on interrupt queue */ - ohci_transfer_intr_enqueue(xfer); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - ohci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* 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 = OHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -ohci_root_ctrl_task(struct usb2_bus *bus) -{ - ohci_root_ctrl_poll(OHCI_BUS2SC(bus)); -} - -static void -ohci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); - char *ptr; - uint32_t port; - uint32_t v; - uint16_t value; - uint16_t index; - uint8_t l; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); - 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.bus_mtx, - USB_MS_TO_TICKS(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); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - ohci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = OHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -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; - - 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; - } -} - -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; - } - } -} - -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 */ -} - -static void -ohci_device_resume(struct usb2_device *udev) -{ - struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - ohci_ed_t *ed; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; - - if (methods == &ohci_device_bulk_methods) { - OHCI_APPEND_QH(ed, sc->sc_bulk_p_last); - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); - } - if (methods == &ohci_device_ctrl_methods) { - OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last); - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); - } - if (methods == &ohci_device_intr_methods) { - OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -ohci_device_suspend(struct usb2_device *udev) -{ - struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - ohci_ed_t *ed; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; - - 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]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -ohci_set_hw_power(struct usb2_bus *bus) -{ - struct ohci_softc *sc = OHCI_BUS2SC(bus); - uint32_t temp; - uint32_t flags; - - DPRINTF("\n"); - - USB_BUS_LOCK(bus); - - flags = bus->hw_power_state; - - temp = OREAD4(sc, OHCI_CONTROL); - temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE); - - if (flags & USB_HW_POWER_CONTROL) - temp |= OHCI_CLE; - - if (flags & USB_HW_POWER_BULK) - temp |= OHCI_BLE; - - if (flags & USB_HW_POWER_INTERRUPT) - temp |= OHCI_PLE; - - if (flags & USB_HW_POWER_ISOC) - temp |= OHCI_IE | OHCI_PLE; - - OWRITE4(sc, OHCI_CONTROL, temp); - - USB_BUS_UNLOCK(bus); - - 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, - .device_resume = ohci_device_resume, - .device_suspend = ohci_device_suspend, - .set_hw_power = ohci_set_hw_power, - .roothub_exec = ohci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/ohci2.h b/sys/dev/usb2/controller/ohci2.h deleted file mode 100644 index 84a6afd..0000000 --- a/sys/dev/usb2/controller/ohci2.h +++ /dev/null @@ -1,366 +0,0 @@ -/* $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_ - -#define OHCI_MAX_DEVICES USB_MAX_DEVICES - -/* 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_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 usb2_device *sc_devices[OHCI_MAX_DEVICES]; - 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 deleted file mode 100644 index a7f086c..0000000 --- a/sys/dev/usb2/controller/ohci2_atmelarm.c +++ /dev/null @@ -1,224 +0,0 @@ -/*- - * 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 - -#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; - - /* initialise some bus fields */ - sc->sc_ohci.sc_bus.parent = dev; - sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; - sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; - - /* 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)); - -#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_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 deleted file mode 100644 index 7d3506e..0000000 --- a/sys/dev/usb2/controller/ohci2_pci.c +++ /dev/null @@ -1,388 +0,0 @@ -/*- - * 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 - -#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; - - /* initialise some bus fields */ - sc->sc_bus.parent = self; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = OHCI_MAX_DEVICES; - - /* 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); - - /* - * Some Sun PCIO-2 USB controllers have their intpin register - * bogusly set to 0, although it should be 4. Correct that. - */ - if (pci_get_devid(self) == 0x1103108e && pci_get_intpin(self) == 0) - pci_set_intpin(self, 4); - - 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)); - } - - /* 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; - - 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_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 deleted file mode 100644 index a5af720..0000000 --- a/sys/dev/usb2/controller/uhci2.c +++ /dev/null @@ -1,3381 +0,0 @@ -/*- - * 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 - -#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 void uhci_root_ctrl_poll(struct uhci_softc *); -static void uhci_do_poll(struct usb2_bus *); -static void uhci_device_done(struct usb2_xfer *, usb2_error_t); -static void uhci_transfer_intr_enqueue(struct usb2_xfer *); -static void uhci_root_intr_check(void *); -static void uhci_timeout(void *); -static uint8_t uhci_check_transfer(struct usb2_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); - } -} - -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; -} - -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; -} - -void -uhci_reset(uhci_softc_t *sc) -{ - struct usb2_page_search buf_res; - uint16_t n; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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.bus_mtx, - USB_MS_TO_TICKS(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.bus_mtx, hz / 1000); - - 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.bus_mtx, hz / 1000); - - /* 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); -} - -static void -uhci_start(uhci_softc_t *sc) -{ - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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.bus_mtx, hz / 1000); - - /* 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; - - 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; - - USB_BUS_LOCK(&sc->sc_bus); - /* reset the controller */ - uhci_reset(sc); - - /* start the controller */ - uhci_start(sc); - USB_BUS_UNLOCK(&sc->sc_bus); - - /* 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) -{ - USB_BUS_LOCK(&sc->sc_bus); - -#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.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -void -uhci_resume(uhci_softc_t *sc) -{ - USB_BUS_LOCK(&sc->sc_bus); - - /* reset the controller */ - - uhci_reset(sc); - - /* force global resume */ - - UHCICMD(sc, UHCI_CMD_FGR); - - usb2_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_DELAY)); - - /* and start traffic again */ - - uhci_start(sc); - -#if USB_DEBUG - if (uhcidebug > 2) { - uhci_dumpregs(sc); - } -#endif - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch lost interrupts */ - uhci_do_poll(&sc->sc_bus); -} - -#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)); -} - -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); -} - -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"); -} - -static void -uhci_dump_tds(uhci_td_t *td) -{ - for (; - td != NULL; - td = td->obj_next) { - if (uhci_dump_td(td)) { - break; - } - } -} - -#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); - } -} - -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); - } -} - -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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout); - } -} - -#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,last) (last) = _uhci_append_qh(sqh,last) -static uhci_qh_t * -_uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last) -{ - DPRINTFN(11, "%p to %p\n", sqh, last); - - if (sqh->h_prev != NULL) { - /* should not happen */ - DPRINTFN(0, "QH already linked!\n"); - return (last); - } - /* (sc->sc_bus.mtx) must be locked */ - - 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); - } - 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; -} - -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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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; - } - } -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK(&sc->sc_bus); - - 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 */ - DPRINTF("%s: host controller halted\n", - __FUNCTION__); -#if USB_DEBUG - if (uhcidebug > 0) { - 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: - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/* - * called when a request does not complete - */ -static void -uhci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - uhci_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -uhci_do_poll(struct usb2_bus *bus) -{ - struct uhci_softc *sc = UHCI_BUS2SC(bus); - - USB_BUS_LOCK(&sc->sc_bus); - uhci_interrupt_poll(sc); - uhci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; -} - -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->xroot->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->xroot->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 = UHCI_BUS2SC(xfer->xroot->bus); - uhci_qh_t *qh; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); - } - 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->xroot->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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - 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]; - - qh->e_next = td; - qh->qh_e_next = td->td_self; - - if (xfer->xroot->udev->pwr_save.suspended == 0) { - UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); - uhci_add_loop(sc); - xfer->flags_int.bandwidth_reclaimed = 1; - } else { - usb2_pc_cpu_flush(qh->page_cache); - } - - /* put transfer on interrupt queue */ - uhci_transfer_intr_enqueue(xfer); -} - -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); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - 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]; - - qh->e_next = td; - qh->qh_e_next = td->td_self; - - /* - * NOTE: some devices choke on bandwidth- reclamation for control - * transfers - */ - if (xfer->xroot->udev->pwr_save.suspended == 0) { - if (xfer->xroot->udev->speed == USB_SPEED_LOW) { - UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); - } else { - UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); - } - } else { - usb2_pc_cpu_flush(qh->page_cache); - } - /* put transfer on interrupt queue */ - uhci_transfer_intr_enqueue(xfer); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - 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); -} - -static void -uhci_device_intr_close(struct usb2_xfer *xfer) -{ - uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_intr_stat[xfer->qh_pos]--; - - uhci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - 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]; - - qh->e_next = td; - qh->qh_e_next = td->td_self; - - if (xfer->xroot->udev->pwr_save.suspended == 0) { - - /* enter QHs into the controller data structures */ - UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); - - } else { - usb2_pc_cpu_flush(qh->page_cache); - } - - /* put transfer on interrupt queue */ - uhci_transfer_intr_enqueue(xfer); -} - -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); - } - } -} - -static void -uhci_device_isoc_close(struct usb2_xfer *xfer) -{ - uhci_device_done(xfer, USB_ERR_CANCELLED); -} - -static void -uhci_device_isoc_enter(struct usb2_xfer *xfer) -{ - struct uhci_mem_layout ml; - uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); - 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); -} - -static void -uhci_device_isoc_start(struct usb2_xfer *xfer) -{ - /* put transfer on interrupt queue */ - uhci_transfer_intr_enqueue(xfer); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - uhci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* 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); - - /* - * Before we do anything, turn on SOF messages on the USB - * BUS. Some USB devices do not cope without them! - */ - if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { - - DPRINTF("Activating SOFs!\n"); - - UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); - - /* wait a little bit */ - if (use_polling) { - DELAY(10000); - } else { - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); - } - } - - 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.bus_mtx, - USB_MS_TO_TICKS(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); - - - mtx_unlock(&sc->sc_bus.bus_mtx); - - /* - * This delay needs to be exactly 100us, else some USB devices - * fail to attach! - */ - DELAY(100); - - mtx_lock(&sc->sc_bus.bus_mtx); - - 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.bus_mtx, - USB_MS_TO_TICKS(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 = UHCI_BUS2SC(xfer->xroot->bus); - - DPRINTF("\n"); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -uhci_root_ctrl_task(struct usb2_bus *bus) -{ - uhci_root_ctrl_poll(UHCI_BUS2SC(bus)); -} - -static void -uhci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); - char *ptr; - uint16_t x; - uint16_t port; - uint16_t value; - uint16_t index; - uint16_t status; - uint16_t change; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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_C_PORT_SUSPEND: - sc->sc_isresumed &= ~(1 << index); - break; - case UHF_PORT_CONNECTION: - case UHF_PORT_OVER_CURRENT: - case UHF_PORT_POWER: - case UHF_PORT_LOW_SPEED: - 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_LSDA) - status |= UPS_LOW_SPEED; - if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD)) { - /* need to do a write back */ - UWRITE2(sc, port, URWMASK(x)); - - /* wait 20ms for resume sequence to complete */ - if (use_polling) { - /* polling */ - DELAY(20000); - } else { - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); - } - - /* clear suspend and resume detect */ - UWRITE2(sc, port, URWMASK(x) & ~(UHCI_PORTSC_RD | - UHCI_PORTSC_SUSP)); - - /* wait a little bit */ - usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 500); - - sc->sc_isresumed |= (1 << index); - - } else if (x & UHCI_PORTSC_SUSP) { - status |= UPS_SUSPEND; - } - status |= UPS_PORT_POWER; - if (sc->sc_isresumed & (1 << index)) - change |= UPS_C_SUSPEND; - 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); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - uhci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = UHCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; - - usb2_transfer_timeout_ms(xfer, - &uhci_root_intr_check, xfer->interval); -} - -static void -uhci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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 = UHCI_BUS2SC(xfer->xroot->bus); - - DPRINTFN(21, "\n"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); - - sc->sc_hub_idata[0] = 0; - - if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | - UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { - sc->sc_hub_idata[0] |= 1 << 1; - } - if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | - UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { - sc->sc_hub_idata[0] |= 1 << 2; - } - if (sc->sc_hub_idata[0] == 0) { - /* - * 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); - } -} - -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; - - 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; - } -} - -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; - } - } -} - -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 */ -} - -static void -uhci_device_resume(struct usb2_device *udev) -{ - struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - uhci_qh_t *qh; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; - - if (methods == &uhci_device_bulk_methods) { - UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); - uhci_add_loop(sc); - xfer->flags_int.bandwidth_reclaimed = 1; - } - if (methods == &uhci_device_ctrl_methods) { - if (xfer->xroot->udev->speed == USB_SPEED_LOW) { - UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); - } else { - UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); - } - } - if (methods == &uhci_device_intr_methods) { - UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -uhci_device_suspend(struct usb2_device *udev) -{ - struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); - struct usb2_xfer *xfer; - struct usb2_pipe_methods *methods; - uhci_qh_t *qh; - - DPRINTF("\n"); - - USB_BUS_LOCK(udev->bus); - - TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { - - if (xfer->xroot->udev == udev) { - - methods = xfer->pipe->methods; - qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; - - 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->xroot->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]); - } - } - } - - USB_BUS_UNLOCK(udev->bus); - - return; -} - -static void -uhci_set_hw_power(struct usb2_bus *bus) -{ - struct uhci_softc *sc = UHCI_BUS2SC(bus); - uint32_t flags; - - DPRINTF("\n"); - - USB_BUS_LOCK(bus); - - flags = bus->hw_power_state; - - /* - * WARNING: Some FULL speed USB devices require periodic SOF - * messages! If any USB devices are connected through the - * UHCI, power save will be disabled! - */ - if (flags & (USB_HW_POWER_CONTROL | - USB_HW_POWER_NON_ROOT_HUB | - USB_HW_POWER_BULK | - USB_HW_POWER_INTERRUPT | - USB_HW_POWER_ISOC)) { - DPRINTF("Some USB transfer is " - "active on %u.\n", - device_get_unit(sc->sc_bus.bdev)); - UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); - } else { - DPRINTF("Power save on %u.\n", - device_get_unit(sc->sc_bus.bdev)); - UHCICMD(sc, UHCI_CMD_MAXP); - } - - USB_BUS_UNLOCK(bus); - - 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, - .device_resume = uhci_device_resume, - .device_suspend = uhci_device_suspend, - .set_hw_power = uhci_set_hw_power, - .roothub_exec = uhci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/uhci2.h b/sys/dev/usb2/controller/uhci2.h deleted file mode 100644 index 9365a4c..0000000 --- a/sys/dev/usb2/controller/uhci2.h +++ /dev/null @@ -1,321 +0,0 @@ -/* $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_ - -#define UHCI_MAX_DEVICES USB_MAX_DEVICES - -/* 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 */ - union uhci_hub_desc sc_hub_desc; - struct usb2_sw_transfer sc_root_ctrl; - struct usb2_sw_transfer sc_root_intr; - - struct usb2_device *sc_devices[UHCI_MAX_DEVICES]; - 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; /* bits set if a root hub is reset */ - uint8_t sc_isresumed; /* bits set if a port was resumed */ - 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 deleted file mode 100644 index 725cd84..0000000 --- a/sys/dev/usb2/controller/uhci2_pci.c +++ /dev/null @@ -1,444 +0,0 @@ -/*- - * 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 - -#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; - - /* initialise some bus fields */ - sc->sc_bus.parent = self; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = UHCI_MAX_DEVICES; - - /* 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: - /* Quirk for Parallels Desktop 4.0 */ - device_printf(self, "USB revision is unknown. Assuming v1.1.\n"); - sc->sc_bus.usbrev = USB_REV_1_1; - break; - } - -#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; - - 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) { - USB_BUS_LOCK(&sc->sc_bus); - - /* stop the controller */ - uhci_reset(sc); - - USB_BUS_UNLOCK(&sc->sc_bus); - } - 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_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 deleted file mode 100644 index 59287c4..0000000 --- a/sys/dev/usb2/controller/usb2_bus.h +++ /dev/null @@ -1,104 +0,0 @@ -/* $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_process roothub_proc; - struct root_hold_token *bus_roothold; - /* - * There are two callback processes. One for Giant locked - * callbacks. One for non-Giant locked callbacks. This should - * avoid congestion and reduce response time in most cases. - */ - struct usb2_process giant_callback_proc; - struct usb2_process non_giant_callback_proc; - struct usb2_bus_msg explore_msg[2]; - struct usb2_bus_msg detach_msg[2]; - struct usb2_bus_msg attach_msg[2]; - struct usb2_bus_msg roothub_msg[2]; - /* - * This mutex protects the USB hardware: - */ - struct mtx bus_mtx; - struct usb2_perm perm; - struct usb2_xfer_queue intr_q; - struct usb2_callout power_wdog; /* power management */ - - device_t parent; - 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; - - uint32_t hw_power_state; /* see USB_HW_POWER_XXX */ - 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 deleted file mode 100644 index cd334ab..0000000 --- a/sys/dev/usb2/controller/usb2_controller.c +++ /dev/null @@ -1,623 +0,0 @@ -/* $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, struct usb2_bus *); -static void usb2_post_init(void *); -static void usb2_bus_mem_flush_all_cb(struct usb2_bus *, - struct usb2_page_cache *, struct usb2_page *, uint32_t, - uint32_t); -static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *, - struct usb2_page_cache *, struct usb2_page *, uint32_t, - uint32_t); -static void usb2_bus_mem_free_all_cb(struct usb2_bus *, - struct usb2_page_cache *, struct usb2_page *, uint32_t, - uint32_t); -static void usb2_bus_roothub(struct usb2_proc_msg *pm); - -/* 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); - } - - /* delay vfs_mountroot until the bus is explored */ - bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); - - 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); - } - /* Stop power watchdog */ - usb2_callout_drain(&bus->power_wdog); - - /* Let the USB explore process detach all devices. */ - if (bus->bus_roothold != NULL) { - root_mount_rel(bus->bus_roothold); - bus->bus_roothold = NULL; - } - - USB_BUS_LOCK(bus); - 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]); - - USB_BUS_UNLOCK(bus); - - /* Get rid of USB callback processes */ - - usb2_proc_free(&bus->giant_callback_proc); - usb2_proc_free(&bus->non_giant_callback_proc); - - /* Get rid of USB roothub process */ - - usb2_proc_free(&bus->roothub_proc); - - /* Get rid of USB explore process */ - - usb2_proc_free(&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; - } - USB_BUS_UNLOCK(bus); - - mtx_lock(&Giant); - - /* - * First update the USB power state! - */ - usb2_bus_powerd(bus); - - /* - * Explore the Root USB HUB. This call can sleep, - * exiting Giant, which is actually Giant. - */ - (udev->hub->explore) (udev); - - mtx_unlock(&Giant); - - USB_BUS_LOCK(bus); - } - if (bus->bus_roothold != NULL) { - root_mount_rel(bus->bus_roothold); - bus->bus_roothold = NULL; - } -} - -/*------------------------------------------------------------------------* - * 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); - USB_BUS_UNLOCK(bus); - - 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); - USB_BUS_LOCK(bus); - /* clear bdev variable last */ - bus->bdev = NULL; -} - -static void -usb2_power_wdog(void *arg) -{ - struct usb2_bus *bus = arg; - - USB_BUS_LOCK_ASSERT(bus, MA_OWNED); - - usb2_callout_reset(&bus->power_wdog, - 4 * hz, usb2_power_wdog, arg); - - USB_BUS_UNLOCK(bus); - - usb2_bus_power_update(bus); - - return; -} - -/*------------------------------------------------------------------------* - * usb2_bus_attach - * - * This function attaches USB in context of the explore thread. - *------------------------------------------------------------------------*/ -static void -usb2_bus_attach(struct usb2_proc_msg *pm) -{ - struct usb2_bus *bus; - struct usb2_device *child; - device_t dev; - usb2_error_t err; - uint8_t speed; - - bus = ((struct usb2_bus_msg *)pm)->bus; - dev = bus->bdev; - - DPRINTF("\n"); - - 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; - } - - USB_BUS_UNLOCK(bus); - mtx_lock(&Giant); /* XXX not required by USB */ - - /* 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; - } - - mtx_unlock(&Giant); - USB_BUS_LOCK(bus); - - if (err) { - device_printf(bus->bdev, "Root HUB problem, error=%s\n", - usb2_errstr(err)); - } - - /* set softc - we are ready */ - device_set_softc(dev, bus); - - /* start watchdog - this function will unlock the BUS lock ! */ - usb2_power_wdog(bus); - - /* need to return locked */ - USB_BUS_LOCK(bus); -} - -/*------------------------------------------------------------------------* - * usb2_attach_sub - * - * This function creates a thread which runs the USB 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) -{ - const char *pname = device_get_nameunit(dev); - - /* 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; - - bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach; - bus->attach_msg[0].bus = bus; - bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach; - bus->attach_msg[1].bus = bus; - - bus->roothub_msg[0].hdr.pm_callback = &usb2_bus_roothub; - bus->roothub_msg[0].bus = bus; - bus->roothub_msg[1].hdr.pm_callback = &usb2_bus_roothub; - bus->roothub_msg[1].bus = bus; - - /* Create USB explore, roothub and callback processes */ - - if (usb2_proc_create(&bus->giant_callback_proc, - &bus->bus_mtx, pname, USB_PRI_MED)) { - printf("WARNING: Creation of USB Giant " - "callback process failed.\n"); - } else if (usb2_proc_create(&bus->non_giant_callback_proc, - &bus->bus_mtx, pname, USB_PRI_HIGH)) { - printf("WARNING: Creation of USB non-Giant " - "callback process failed.\n"); - } else if (usb2_proc_create(&bus->roothub_proc, - &bus->bus_mtx, pname, USB_PRI_HIGH)) { - printf("WARNING: Creation of USB roothub " - "process failed.\n"); - } else if (usb2_proc_create(&bus->explore_proc, - &bus->bus_mtx, pname, USB_PRI_MED)) { - printf("WARNING: Creation of USB explore " - "process failed.\n"); - } else { - /* Get final attach going */ - USB_BUS_LOCK(bus); - if (usb2_proc_msignal(&bus->explore_proc, - &bus->attach_msg[0], &bus->attach_msg[1])) { - /* ignore */ - } - USB_BUS_UNLOCK(bus); - } -} - -/*------------------------------------------------------------------------* - * 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); -} - -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); -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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; - - mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), - NULL, MTX_DEF | MTX_RECURSE); - - usb2_callout_init_mtx(&bus->power_wdog, - &bus->bus_mtx, CALLOUT_RETURNUNLOCKED); - - TAILQ_INIT(&bus->intr_q.head); - - usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, - dmat, &bus->bus_mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX); - - if ((bus->devices_max > USB_MAX_DEVICES) || - (bus->devices_max < USB_MIN_DEVICES) || - (bus->devices == NULL)) { - DPRINTFN(0, "Devices field has not been " - "initialised properly!\n"); - bus->alloc_failed = 1; /* failure */ - } - 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); -} - -/*------------------------------------------------------------------------* - * 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->bus_mtx); -} - -/*------------------------------------------------------------------------* - * usb2_bus_roothub - * - * This function is used to execute roothub control requests on the - * roothub and is called from the roothub process. - *------------------------------------------------------------------------*/ -static void -usb2_bus_roothub(struct usb2_proc_msg *pm) -{ - struct usb2_bus *bus; - - bus = ((struct usb2_bus_msg *)pm)->bus; - - USB_BUS_LOCK_ASSERT(bus, MA_OWNED); - - (bus->methods->roothub_exec) (bus); -} - -/*------------------------------------------------------------------------* - * usb2_bus_roothub_exec - * - * This function is used to schedule the "roothub_done" bus callback - * method. The bus lock must be locked when calling this function. - *------------------------------------------------------------------------*/ -void -usb2_bus_roothub_exec(struct usb2_bus *bus) -{ - USB_BUS_LOCK_ASSERT(bus, MA_OWNED); - - if (usb2_proc_msignal(&bus->roothub_proc, - &bus->roothub_msg[0], &bus->roothub_msg[1])) { - /* ignore */ - } -} diff --git a/sys/dev/usb2/controller/usb2_controller.h b/sys/dev/usb2/controller/usb2_controller.h deleted file mode 100644 index 80633d9..0000000 --- a/sys/dev/usb2/controller/usb2_controller.h +++ /dev/null @@ -1,199 +0,0 @@ -/* $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); - void (*device_suspend) (struct usb2_device *udev); - void (*device_resume) (struct usb2_device *udev); - void (*set_hw_power) (struct usb2_bus *bus); - /* - * The following flag is set if one or more control transfers are - * active: - */ -#define USB_HW_POWER_CONTROL 0x01 - /* - * The following flag is set if one or more bulk transfers are - * active: - */ -#define USB_HW_POWER_BULK 0x02 - /* - * The following flag is set if one or more interrupt transfers are - * active: - */ -#define USB_HW_POWER_INTERRUPT 0x04 - /* - * The following flag is set if one or more isochronous transfers - * are active: - */ -#define USB_HW_POWER_ISOC 0x08 - /* - * The following flag is set if one or more non-root-HUB devices - * are present on the given USB bus: - */ -#define USB_HW_POWER_NON_ROOT_HUB 0x10 - - /* 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); - - /* USB Device and Host mode - Optional */ - - void (*roothub_exec) (struct usb2_bus *); -}; - -/* - * 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 */ - - 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); -void usb2_bus_roothub_exec(struct usb2_bus *bus); -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 deleted file mode 100644 index 9297c29..0000000 --- a/sys/dev/usb2/controller/usb2_pci.h +++ /dev/null @@ -1,39 +0,0 @@ -/* $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 deleted file mode 100644 index 1e034bf..0000000 --- a/sys/dev/usb2/controller/uss820dci.c +++ /dev/null @@ -1,2489 +0,0 @@ -/* $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 - -#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 *, usb2_error_t); -static void uss820dci_do_poll(struct usb2_bus *); -static void uss820dci_root_ctrl_poll(struct uss820dci_softc *); -static void uss820dci_standard_done(struct usb2_xfer *); -static void uss820dci_intr_set(struct usb2_xfer *, uint8_t); -static void uss820dci_update_shared_1(struct uss820dci_softc *, uint8_t, - uint8_t, uint8_t); - -static usb2_sw_transfer_func_t uss820dci_root_intr_done; -static usb2_sw_transfer_func_t uss820dci_root_ctrl_done; - -/* - * 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); -} - -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; - } -} - -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); - } -} - -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); - } -} - -static void -uss820dci_wakeup_peer(struct uss820dci_softc *sc) -{ - if (!(sc->sc_flags.status_suspend)) { - return; - } - DPRINTFN(0, "not supported\n"); -} - -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); -} - -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; - } - } -} - -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); -} - -void -uss820dci_interrupt(struct uss820dci_softc *sc) -{ - uint8_t ssr; - uint8_t event; - - USB_BUS_LOCK(&sc->sc_bus); - - 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); - - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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; -} - -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->xroot->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 = USS820_DCI_BUS2SC(xfer->xroot->bus); - 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; -} - -static void -uss820dci_timeout(void *arg) -{ - struct usb2_xfer *xfer = arg; - - DPRINTF("xfer=%p\n", xfer); - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* transfer is transferred */ - uss820dci_device_done(xfer, USB_ERR_TIMEOUT); -} - -static void -uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set) -{ - struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); - 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); -} - -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->xroot->bus->intr_q, xfer); - - /* start timeout, if any */ - if (xfer->timeout != 0) { - usb2_transfer_timeout_ms(xfer, - &uss820dci_timeout, xfer->timeout); - } - } -} - -static void -uss820dci_root_intr_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); - - DPRINTFN(9, "\n"); - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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); -} - -/*------------------------------------------------------------------------* - * 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) -{ - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); -} - -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; - - USB_BUS_LOCK_ASSERT(udev->bus, 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); -} - -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); - } -} - -static void -uss820dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) -{ - struct uss820dci_softc *sc; - struct usb2_endpoint_descriptor *ed; - - USB_BUS_LOCK_ASSERT(udev->bus, 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))); -} - -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; - - USB_BUS_LOCK(&sc->sc_bus); - - /* 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) { - USB_BUS_UNLOCK(&sc->sc_bus); - 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.bus_mtx, hz / 100); - - /* check hardware revision */ - temp = USS820_READ_1(sc, USS820_REV); - - if (temp < 0x13) { - USB_BUS_UNLOCK(&sc->sc_bus); - return (USB_ERR_INVAL); - } - /* enable interrupts */ - USS820_WRITE_1(sc, USS820_SCR, - USS820_SCR_T_IRQ | - USS820_SCR_IE_RESET | - /* USS820_SCR_RWUPE | */ - 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); - } - - USB_BUS_UNLOCK(&sc->sc_bus); - - /* catch any lost interrupts */ - - uss820dci_do_poll(&sc->sc_bus); - - return (0); /* success */ -} - -void -uss820dci_uninit(struct uss820dci_softc *sc) -{ - uint8_t temp; - - USB_BUS_LOCK(&sc->sc_bus); - - /* 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); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -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); - - USB_BUS_LOCK(&sc->sc_bus); - uss820dci_interrupt_poll(sc); - uss820dci_root_ctrl_poll(sc); - USB_BUS_UNLOCK(&sc->sc_bus); -} - -/*------------------------------------------------------------------------* - * 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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -static void -uss820dci_device_isoc_fs_enter(struct usb2_xfer *xfer) -{ - struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); - 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); -} - -static void -uss820dci_device_isoc_fs_start(struct usb2_xfer *xfer) -{ - /* start TD chain */ - uss820dci_start_standard_chain(xfer); -} - -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 = USS820_DCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_ctrl.xfer == xfer) { - sc->sc_root_ctrl.xfer = NULL; - } - uss820dci_device_done(xfer, USB_ERR_CANCELLED); -} - -/* - * 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) >> 8, - .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 = USS820_DCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_ctrl.xfer = xfer; - - usb2_bus_roothub_exec(xfer->xroot->bus); -} - -static void -uss820dci_root_ctrl_task(struct usb2_bus *bus) -{ - uss820dci_root_ctrl_poll(USS820_DCI_BUS2SC(bus)); -} - -static void -uss820dci_root_ctrl_done(struct usb2_xfer *xfer, - struct usb2_sw_transfer *std) -{ - struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus); - uint16_t value; - uint16_t index; - uint8_t use_polling; - - USB_BUS_LOCK_ASSERT(&sc->sc_bus, 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->xroot->xfer_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); -} - -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 = USS820_DCI_BUS2SC(xfer->xroot->bus); - - if (sc->sc_root_intr.xfer == xfer) { - sc->sc_root_intr.xfer = NULL; - } - uss820dci_device_done(xfer, USB_ERR_CANCELLED); -} - -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 = USS820_DCI_BUS2SC(xfer->xroot->bus); - - sc->sc_root_intr.xfer = xfer; -} - -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; - - /* - * 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; -} - -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; - } - } -} - -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, - .roothub_exec = &uss820dci_root_ctrl_task, -}; diff --git a/sys/dev/usb2/controller/uss820dci.h b/sys/dev/usb2/controller/uss820dci.h deleted file mode 100644 index f99e2d5..0000000 --- a/sys/dev/usb2/controller/uss820dci.h +++ /dev/null @@ -1,377 +0,0 @@ -/* $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_MAX_DEVICES (USB_MIN_DEVICES + 1) - -#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_device *sc_devices[USS820_MAX_DEVICES]; - 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 deleted file mode 100644 index 387c167..0000000 --- a/sys/dev/usb2/controller/uss820dci_atmelarm.c +++ /dev/null @@ -1,239 +0,0 @@ -#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 - -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; - - /* initialise some bus fields */ - sc->sc_bus.parent = dev; - sc->sc_bus.devices = sc->sc_devices; - sc->sc_bus.devices_max = USS820_MAX_DEVICES; - - /* 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); - -#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_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 deleted file mode 100644 index 7367230..0000000 --- a/sys/dev/usb2/core/README.TXT +++ /dev/null @@ -1,411 +0,0 @@ - -$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 deleted file mode 100644 index 8254e6e..0000000 --- a/sys/dev/usb2/core/usb2_busdma.c +++ /dev/null @@ -1,1426 +0,0 @@ -/* $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 - -static void usb2_dma_tag_create(struct usb2_dma_tag *, uint32_t, uint32_t); -static void usb2_dma_tag_destroy(struct usb2_dma_tag *); - -#ifdef __FreeBSD__ -static void usb2_dma_lock_cb(void *, bus_dma_lock_op_t); -static int32_t usb2_m_copy_in_cb(void *, void *, uint32_t); -static void usb2_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); -static void usb2_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); -static void usb2_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, - uint8_t); -#endif - -#ifdef __NetBSD__ -static int32_t usb2_m_copy_in_cb(void *, caddr_t, uint32_t); -static void usb2_pc_common_mem_cb(struct usb2_page_cache *, - bus_dma_segment_t *, int, int, uint8_t); -#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; - } -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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; - } -} - - -#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 */ -} - -/*------------------------------------------------------------------------* - * 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 */ BUS_DMA_KEEP_PG_OFFSET, - /* lockfn */ &usb2_dma_lock_cb, - /* lockarg */ NULL, - &tag)) { - tag = NULL; - } - udt->tag = tag; -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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--; -#if (USB_DEBUG != 0) - if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { - /* - * This check verifies that the physical address is correct: - */ - DPRINTFN(0, "Page offset was not preserved!\n"); - error = 1; - goto done; - } -#endif - 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); -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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; - - /* - * We have to unload the previous loaded DMA - * pages before trying to load a new one! - */ - bus_dmamap_unload(pc->tag, pc->map); - - /* - * 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 { - - /* - * We have to unload the previous loaded DMA - * pages before trying to load a new one! - */ - bus_dmamap_unload(pc->tag, pc->map); - - /* - * 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) -{ - if (pc->page_offset_end == pc->page_offset_buf) { - /* nothing has been loaded into this page cache! */ - return; - } - bus_dmamap_sync(pc->tag, pc->map, - BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); -} - -/*------------------------------------------------------------------------* - * usb2_pc_cpu_flush - flush CPU cache - *------------------------------------------------------------------------*/ -void -usb2_pc_cpu_flush(struct usb2_page_cache *pc) -{ - if (pc->page_offset_end == pc->page_offset_buf) { - /* nothing has been loaded into this page cache! */ - return; - } - bus_dmamap_sync(pc->tag, pc->map, - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -#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; -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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--; -#if (USB_DEBUG != 0) - if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { - /* - * This check verifies that the physical address is correct: - */ - DPRINTFN(0, "Page offset was not preserved!\n"); - error = 1; - goto done; - } -#endif - 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); -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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) { - - /* - * We have to unload the previous loaded DMA - * pages before trying to load a new one! - */ - bus_dmamap_unload(pc->tag, pc->map); - - /* 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; - - if (len == 0) { - /* nothing has been loaded into this page cache */ - return; - } - bus_dmamap_sync(pc->tag, pc->map, 0, len, - BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); -} - -/*------------------------------------------------------------------------* - * 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; - - if (len == 0) { - /* nothing has been loaded into this page cache */ - return; - } - bus_dmamap_sync(pc->tag, pc->map, 0, len, - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -#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++; - } -} - -/*------------------------------------------------------------------------* - * 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 - } -} - -/*------------------------------------------------------------------------* - * 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->xroot; - - mtx_assert(info->xfer_mtx, MA_OWNED); - - if (xfer->error) { - /* some error happened */ - USB_BUS_LOCK(info->bus); - usb2_transfer_done(xfer, 0); - USB_BUS_UNLOCK(info->bus); - 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) { - USB_BUS_LOCK(info->bus); - usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); - USB_BUS_UNLOCK(info->bus); - 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); -} - -/*------------------------------------------------------------------------* - * 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->xfer_mtx, MA_OWNED); - - /* copy error */ - info->dma_error = udpt->dma_error; - - /* enter workloop again */ - usb2_command_wrapper(&info->dma_q, - info->dma_q.curr); -} - -/*------------------------------------------------------------------------* - * 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->isread) { - usb2_pc_cpu_invalidate(pc); - } else { - usb2_pc_cpu_flush(pc); - } - pc++; - } -} - -/*------------------------------------------------------------------------* - * 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->isread) { - usb2_pc_cpu_invalidate(pc); - } - pc++; - } -} diff --git a/sys/dev/usb2/core/usb2_busdma.h b/sys/dev/usb2/core/usb2_busdma.h deleted file mode 100644 index 3c1600b..0000000 --- a/sys/dev/usb2/core/usb2_busdma.h +++ /dev/null @@ -1,183 +0,0 @@ -/* $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 deleted file mode 100644 index 4f52ea3..0000000 --- a/sys/dev/usb2/core/usb2_compat_linux.c +++ /dev/null @@ -1,1653 +0,0 @@ -/* $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 *); -static int usb_start_wait_urb(struct urb *, uint32_t, uint16_t *); -static const struct usb_device_id *usb_linux_lookup_id( - const struct usb_device_id *, struct usb2_attach_arg *); -static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *); -static struct usb_device *usb_linux_create_usb_device(struct usb2_device *, - device_t); -static void usb_linux_cleanup_interface(struct usb_device *, - struct usb_interface *); -static void usb_linux_complete(struct usb2_xfer *); -static int usb_unlink_urb_sub(struct urb *, uint8_t); - -/*------------------------------------------------------------------------* - * 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; - - 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) -{ - ; /* indent fix */ - switch (usb2_get_speed(dev->bsd_udev)) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - return (USB_MAX_FULL_SPEED_ISOC_FRAMES); - default: - return (USB_MAX_HIGH_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); - } -} - -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(); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * usb_buffer_free - *------------------------------------------------------------------------*/ -void -usb_buffer_free(struct usb_device *dev, uint32_t size, - void *addr, uint8_t dma_addr) -{ - free(addr, M_USBDEV); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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)); -} - -/*------------------------------------------------------------------------* - * usb_kill_urb - *------------------------------------------------------------------------*/ -void -usb_kill_urb(struct urb *urb) -{ - if (usb_unlink_urb_sub(urb, 1)) { - /* ignore */ - } -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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++; - } -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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 deleted file mode 100644 index 8ebb7e3..0000000 --- a/sys/dev/usb2/core/usb2_compat_linux.h +++ /dev/null @@ -1,472 +0,0 @@ -/* $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_core.c b/sys/dev/usb2/core/usb2_core.c deleted file mode 100644 index b96f53c..0000000 --- a/sys/dev/usb2/core/usb2_core.c +++ /dev/null @@ -1,40 +0,0 @@ -/* $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 deleted file mode 100644 index 06f77cb..0000000 --- a/sys/dev/usb2/core/usb2_core.h +++ /dev/null @@ -1,467 +0,0 @@ -/* $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_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) - -#define USB_BUS_LOCK(_b) mtx_lock(&(_b)->bus_mtx) -#define USB_BUS_UNLOCK(_b) mtx_unlock(&(_b)->bus_mtx) -#define USB_BUS_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_mtx, _t) -#define USB_XFER_LOCK(_x) mtx_lock((_x)->xroot->xfer_mtx) -#define USB_XFER_UNLOCK(_x) mtx_unlock((_x)->xroot->xfer_mtx) -#define USB_XFER_LOCK_ASSERT(_x, _t) mtx_assert((_x)->xroot->xfer_mtx, _t) -/* 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_xfer_root *xroot; /* 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; /* bus index */ - uint8_t dev_index; /* device index */ - uint8_t iface_index; /* interface index */ - uint8_t fifo_index; /* FIFO index */ - uint8_t is_read; /* set if location has read access */ - uint8_t is_write; /* set if location has write access */ - uint8_t is_uref; /* set if USB refcount decr. needed */ - uint8_t is_usbfs; /* set if USB-FS is active */ -}; - -/* 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_mode(struct usb2_device *udev); -uint8_t usb2_get_speed(struct usb2_device *udev); -uint32_t usb2_get_isoc_fps(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); -uint8_t usb2_transfer_pending(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); -void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode); - -#endif /* _USB2_CORE_H_ */ diff --git a/sys/dev/usb2/core/usb2_debug.c b/sys/dev/usb2/core/usb2_debug.c deleted file mode 100644 index 46d27b4..0000000 --- a/sys/dev/usb2/core/usb2_debug.c +++ /dev/null @@ -1,152 +0,0 @@ -/* $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 - -/* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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"); -} - -/*------------------------------------------------------------------------* - * 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"); - } -} - -/*------------------------------------------------------------------------* - * usb2_dump_xfer - * - * This function dumps information about an USB transfer. - *------------------------------------------------------------------------*/ -void -usb2_dump_xfer(struct usb2_xfer *xfer) -{ - struct usb2_device *udev; - printf("usb2_dump_xfer: xfer=%p\n", xfer); - if (xfer == NULL) { - return; - } - if (xfer->pipe == NULL) { - printf("xfer %p: pipe=NULL\n", - xfer); - return; - } - udev = xfer->xroot->udev; - printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d " - "pipe=%p ep=0x%02x attr=0x%02x\n", - xfer, udev, - UGETW(udev->ddesc.idVendor), - UGETW(udev->ddesc.idProduct), - udev->address, xfer->pipe, - xfer->pipe->edesc->bEndpointAddress, - xfer->pipe->edesc->bmAttributes); -} diff --git a/sys/dev/usb2/core/usb2_debug.h b/sys/dev/usb2/core/usb2_debug.h deleted file mode 100644 index 92dcbd5..0000000 --- a/sys/dev/usb2/core/usb2_debug.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $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 deleted file mode 100644 index 18b1f9f..0000000 --- a/sys/dev/usb2/core/usb2_dev.c +++ /dev/null @@ -1,2814 +0,0 @@ -/* $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 **); -static uint32_t usb2_path_convert(const char *); -static int usb2_check_access(int, struct usb2_perm *); -static int usb2_fifo_open(struct usb2_fifo *, struct file *, - struct thread *, int); -static void usb2_fifo_close(struct usb2_fifo *, struct thread *, int); -static void usb2_dev_init(void *); -static void usb2_dev_init_post(void *); -static void usb2_dev_uninit(void *); -static int usb2_fifo_uiomove(struct usb2_fifo *, void *, int, - struct uio *); -static void usb2_fifo_check_methods(struct usb2_fifo_methods *); -static void usb2_clone(void *, USB_UCRED char *, int, struct cdev **); -static struct usb2_fifo *usb2_fifo_alloc(void); -static struct usb2_pipe *usb2_dev_get_pipe(struct usb2_device *, uint8_t, - uint8_t, uint8_t); - -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); - - } -} - -/*------------------------------------------------------------------------* - * 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 dev_ep_index; - - if (fp) { - /* check if we need uref */ - ploc->is_uref = devloc ? 0 : 1; - /* get devloc - already verified */ - devloc = USB_P2U(fp->f_data); - /* get file flags */ - fflags = fp->f_flag; - } else { - /* 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->fifo_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; - } - /* 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; - ploc->is_usbfs = 0; - /* NOTE: variable overloading: */ - dev_ep_index = ploc->fifo_index; - } else { - /* initialise "is_usbfs" flag */ - ploc->is_usbfs = 0; - dev_ep_index = 255; /* dummy */ - - /* check for write */ - if (fflags & FWRITE) { - ppf = ploc->udev->fifo; - f = ppf[ploc->fifo_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; - } - /* check if USB-FS is active */ - if (f->fs_ep_max != 0) { - ploc->is_usbfs = 1; - } - /* - * Get real endpoint index associated with - * this FIFO: - */ - dev_ep_index = f->dev_ep_index; - } else { - ploc->txfifo = NULL; - ploc->is_write = 0; /* no ref */ - } - - /* check for read */ - if (fflags & FREAD) { - ppf = ploc->udev->fifo; - f = ppf[ploc->fifo_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; - } - /* check if USB-FS is active */ - if (f->fs_ep_max != 0) { - ploc->is_usbfs = 1; - } - /* - * Get real endpoint index associated with - * this FIFO: - */ - dev_ep_index = f->dev_ep_index; - } else { - ploc->rxfifo = NULL; - ploc->is_read = 0; /* no ref */ - } - } - - /* check if we require an interface */ - ploc->iface = usb2_get_iface(ploc->udev, ploc->iface_index); - if (dev_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; - } - } - /* when everything is OK we increment the refcounts */ - if (ploc->is_write) { - DPRINTFN(2, "ref write\n"); - ploc->txfifo->refcount++; - } - if (ploc->is_read) { - DPRINTFN(2, "ref read\n"); - ploc->rxfifo->refcount++; - } - if (ploc->is_uref) { - DPRINTFN(2, "ref udev - needed\n"); - ploc->udev->refcount++; - } - 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_uref_location - * - * This function is used to upgrade an USB reference to include the - * USB device reference on a USB location. - * - * Return values: - * 0: Success, refcount incremented on the given USB device. - * Else: Failure. - *------------------------------------------------------------------------*/ -static usb2_error_t -usb2_uref_location(struct usb2_location *ploc) -{ - /* - * Check if we already got an USB reference on this location: - */ - if (ploc->is_uref) { - return (0); /* success */ - } - mtx_lock(&usb2_ref_lock); - if (ploc->bus != devclass_get_softc(usb2_devclass_ptr, ploc->bus_index)) { - DPRINTFN(2, "bus changed at %u\n", ploc->bus_index); - goto error; - } - if (ploc->udev != ploc->bus->devices[ploc->dev_index]) { - DPRINTFN(2, "device changed at %u\n", ploc->dev_index); - goto error; - } - if (ploc->udev->refcount == USB_DEV_REF_MAX) { - DPRINTFN(2, "no dev ref\n"); - goto error; - } - DPRINTFN(2, "ref udev\n"); - ploc->udev->refcount++; - mtx_unlock(&usb2_ref_lock); - - /* set "uref" */ - ploc->is_uref = 1; - - /* - * 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); -} - -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; - - /* NOTE: variable overloading: */ - uint8_t dev_ep_index = ploc->fifo_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; - 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; - 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); -} - -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; - } - } -} - -/*------------------------------------------------------------------------* - * 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"); -} - -/*------------------------------------------------------------------------* - * 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; - - if (ep_index != 0) { - /* - * Non-control endpoints are always - * associated with an interface: - */ - iface = usb2_get_iface(udev, iface_index); - if (iface == NULL) { - return (EINVAL); - } - if (iface->idesc == NULL) { - return (EINVAL); - } - } else { - iface = NULL; - } - /* scan down the permissions tree */ - if ((iface != NULL) && - (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); - } - /* - * NOTE: Variable overloading. "usb2_fifo_create" will update - * the FIFO index. Right here we can assume that the - * "fifo_index" is the same like the endpoint number without - * direction mask, if the "fifo_index" is less than 16. - */ - err = usb2_check_thread_perm(loc.udev, td, fflags, - loc.iface_index, loc.fifo_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; -} - -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); -} - -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 0666, because we - * check the permissions again in the open routine against the - * real USB permissions which are not 0666. Else USB access - * will be limited to one user and one group. - */ - usb2_dev = make_dev(&usb2_devsw, 0, UID_ROOT, GID_OPERATOR, - 0666, 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"); - } -} - -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); -} - -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 /* need uref */ );; - - /* 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 (ENOIOCTL); - } - 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; - struct usb2_fifo *f; - int fflags; - int err; - - err = usb2_ref_device(fp, &loc, 1 /* no uref */ );; - if (err) { - return (ENXIO); - } - fflags = fp->f_flag; - - DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd); - - f = NULL; /* set default value */ - err = ENOIOCTL; /* set default value */ - - if (fflags & FWRITE) { - f = loc.txfifo; - err = usb2_ioctl_f_sub(f, cmd, addr, td); - } - if (fflags & FREAD) { - f = loc.rxfifo; - err = usb2_ioctl_f_sub(f, cmd, addr, td); - } - if (err == ENOIOCTL) { - err = (f->methods->f_ioctl) (f, cmd, addr, fflags, td); - if (err == ENOIOCTL) { - if (usb2_uref_location(&loc)) { - err = ENXIO; - goto done; - } - err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags, td); - } - } - if (err == ENOIOCTL) { - err = ENOTTY; - } -done: - 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; - - revents = usb2_ref_device(fp, &loc, 1 /* no uref */ );; - if (revents) { - return (POLLHUP); - } - fflags = fp->f_flag; - - /* Figure out who needs service */ - - if ((events & (POLLOUT | POLLWRNORM)) && - (fflags & FWRITE)) { - - f = loc.txfifo; - - mtx_lock(f->priv_mtx); - - if (!loc.is_usbfs) { - 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 (!loc.is_usbfs) { - 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); - - if (!loc.is_usbfs) { - /* 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); - - /* check for permanent read error */ - if (f->flag_iserror) { - err = EIO; - goto done; - } - /* check if USB-FS interface is active */ - if (loc.is_usbfs) { - /* - * The queue is used for events that should be - * retrieved using the "USB_FS_COMPLETE" ioctl. - */ - err = EINVAL; - goto done; - } - while (uio->uio_resid > 0) { - - USB_IF_DEQUEUE(&f->used_q, m); - - 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; - } - if (f->methods->f_filter_read) { - /* - * Sometimes it is convenient to process data at the - * expense of a userland process instead of a kernel - * process. - */ - (f->methods->f_filter_read) (f, m); - } - 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); - - /* check for permanent write error */ - if (f->flag_iserror) { - err = EIO; - goto done; - } - /* check if USB-FS interface is active */ - if (loc.is_usbfs) { - /* - * The queue is used for events that should be - * retrieved using the "USB_FS_COMPLETE" ioctl. - */ - err = EINVAL; - goto done; - } - if (f->queue_data == NULL) { - /* start write transfer, if not already started */ - (f->methods->f_start_write) (f); - } - /* we allow writing zero length data */ - do { - USB_IF_DEQUEUE(&f->free_q, m); - - 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; - } - 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; - } - if (f->methods->f_filter_write) { - /* - * Sometimes it is convenient to process data at the - * expense of a userland process instead of a kernel - * process. - */ - (f->methods->f_filter_write) (f, m); - } - 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); - } -} - -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); - } -} - -/*------------------------------------------------------------------------* - * 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 (ENOIOCTL); -} - -static void -usb2_fifo_dummy_cmd(struct usb2_fifo *fifo) -{ - fifo->flag_flushing = 0; /* not flushing */ -} - -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_ioctl_post == NULL) - pm->f_ioctl_post = &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; -} - -/*------------------------------------------------------------------------* - * 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_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_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)); -} - -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); -} - -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; - } - } -} - -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; - } - } -} - -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); -} - -/*------------------------------------------------------------------------* - * 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_POLL(&f->used_q, m); - - if (m) { - *plen = m->cur_data_len; - *pptr = m->cur_data_ptr; - - return (1); - } - return (0); -} - -void -usb2_fifo_get_data_error(struct usb2_fifo *f) -{ - f->flag_iserror = 1; - usb2_fifo_wakeup(f); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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 deleted file mode 100644 index 6203572..0000000 --- a/sys/dev/usb2/core/usb2_dev.h +++ /dev/null @@ -1,168 +0,0 @@ -/* $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; -struct usb2_mbuf; - -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); -typedef void (usb2_fifo_filter_t)(struct usb2_fifo *fifo, struct usb2_mbuf *m); - -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" and "usb2_fifo_filter_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; - /* - * NOTE: The post-ioctl callback is called after the USB reference - * gets locked in the IOCTL handler: - */ - usb2_fifo_ioctl_t *f_ioctl_post; - 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; - usb2_fifo_filter_t *f_filter_read; - usb2_fifo_filter_t *f_filter_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_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_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 deleted file mode 100644 index 31e8a84..0000000 --- a/sys/dev/usb2/core/usb2_device.c +++ /dev/null @@ -1,2192 +0,0 @@ -/* $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 "usbdevs.h" - -#define USB_DEBUG_VAR usb2_debug - -#include -#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 *, uint8_t, - struct usb2_endpoint_descriptor *, struct usb2_pipe *); -static void usb2_free_pipe_data(struct usb2_device *, uint8_t, uint8_t); -static void usb2_free_iface_data(struct usb2_device *); -static void usb2_detach_device_sub(struct usb2_device *, device_t *, - uint8_t); -static uint8_t usb2_probe_and_attach_sub(struct usb2_device *, - struct usb2_attach_arg *); -static void usb2_init_attach_arg(struct usb2_device *, - struct usb2_attach_arg *); -static void usb2_suspend_resume_sub(struct usb2_device *, device_t, - uint8_t); -static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm); -static void usb2_check_strings(struct usb2_device *); -static usb2_error_t usb2_fill_iface_data(struct usb2_device *, uint8_t, - uint8_t); -static void usb2_notify_addq(const char *type, struct usb2_device *); -static void usb2_fifo_free_wrap(struct usb2_device *, uint8_t, uint8_t); - -/* 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) { - USB_BUS_LOCK(udev->bus); - (udev->bus->methods->clear_stall) (udev, pipe); - USB_BUS_UNLOCK(udev->bus); - } -} - -/*------------------------------------------------------------------------* - * 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++; - } -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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 generic FIFOs for this interface, except control - * endpoint FIFOs: - */ - 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); - } - USB_BUS_LOCK(udev->bus); - - /* 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 */ - USB_BUS_UNLOCK(udev->bus); - 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); - } - USB_BUS_UNLOCK(udev->bus); - 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"); -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -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; -} - -/*------------------------------------------------------------------------* - * 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"); - } -} - -/*------------------------------------------------------------------------* - * usb2_suspend_resume - * - * 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); - - USB_BUS_LOCK(udev->bus); - /* filter the suspend events */ - if (udev->flags.suspended == do_suspend) { - USB_BUS_UNLOCK(udev->bus); - /* nothing to do */ - return (0); - } - udev->flags.suspended = do_suspend; - USB_BUS_UNLOCK(udev->bus); - - /* 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 */ - USB_BUS_UNLOCK(udev->bus); - mtx_lock(udev->default_mtx); - - /* Start clear stall callback */ - usb2_transfer_start(udev->default_xfer[1]); - - /* Change lock */ - mtx_unlock(udev->default_mtx); - USB_BUS_LOCK(udev->bus); -} - -/*------------------------------------------------------------------------* - * 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. - * - * Device index zero is not used and device index 1 should - * always be the root hub. - */ - for (device_index = USB_ROOT_HUB_ADDR; - (device_index != bus->devices_max) && - (bus->devices[device_index] != NULL); - device_index++) /* nop */; - - if (device_index == bus->devices_max) { - 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; - /* - * We need to force the power mode to "on" because there are plenty - * of USB devices out there that do not work very well with - * automatic suspend and resume! - */ - udev->power_mode = USB_POWER_MODE_ON; - udev->pwr_save.last_xfer_time = ticks; - - /* 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; - - /* speed combination should be checked by the parent HUB */ - - hub = udev->parent_hub; - - /* 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_MS_TO_TICKS(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); - /* XXX try to re-enumerate the device */ - err = usb2_req_re_enumerate(udev, &Giant); - if (err) { - 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; - uint8_t set_config_failed = 0; - - /* - * 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) { - if (udev->ddesc.bNumConfigurations != 0) { - if (!set_config_failed) { - set_config_failed = 1; - /* XXX try to re-enumerate the device */ - err = usb2_req_re_enumerate( - udev, &Giant); - if (err == 0) - goto repeat_set_config; - } - DPRINTFN(0, "Failure selecting " - "configuration index %u: %s, port %u, " - "addr %u (ignored)\n", - config_index, usb2_errstr(err), udev->port_no, - udev->address); - } - /* - * Some USB devices do not have any - * configurations. Ignore any set config - * failures! - */ - err = 0; - } 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) == 0) { - DPRINTFN(0, "Found possible auto-install " - "disk (trying next config)\n"); - config_index++; - goto repeat_set_config; - } - } - } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 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; - - printf("ugen%u.%u: <%s> at %s (disconnected)\n", - device_get_unit(bus->bdev), - udev->device_index, udev->manufacturer, - device_get_nameunit(bus->bdev)); - - /* - * 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); - - /* template unsetup, if any */ - (usb2_temp_unsetup_p) (udev); - - /* - * Make sure that our clear-stall messages are not queued - * anywhere: - */ - USB_BUS_LOCK(udev->bus); - usb2_proc_mwait(&udev->bus->non_giant_callback_proc, - &udev->cs_msg[0], &udev->cs_msg[1]); - USB_BUS_UNLOCK(udev->bus); - - 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); -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -#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 "usbdevs.h" -#include "usbdevs_data.h" -#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); - } -} - -/* - * Returns: - * See: USB_MODE_XXX - */ -uint8_t -usb2_get_mode(struct usb2_device *udev) -{ - return (udev->flags.usb2_mode); -} - -/* - * Returns: - * See: USB_SPEED_XXX - */ -uint8_t -usb2_get_speed(struct usb2_device *udev) -{ - return (udev->speed); -} - -uint32_t -usb2_get_isoc_fps(struct usb2_device *udev) -{ - ; /* indent fix */ - switch (udev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - return (1000); - default: - return (8000); - } -} - -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); -} - -/*------------------------------------------------------------------------* - * usb2_fifo_free_wrap - * - * This function will free the FIFOs. - * - * Flag values, if "iface_index" is equal to "USB_IFACE_INDEX_ANY". - * 0: Free all FIFOs except generic control endpoints. - * 1: Free all FIFOs. - * - * Flag values, if "iface_index" is not equal to "USB_IFACE_INDEX_ANY". - * Not used. - *------------------------------------------------------------------------*/ -static void -usb2_fifo_free_wrap(struct usb2_device *udev, - uint8_t iface_index, uint8_t flag) -{ - struct usb2_fifo *f; - 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; - } - /* Check if the interface index matches */ - if (iface_index == f->iface_index) { - if (f->methods != &usb2_ugen_methods) { - /* - * Don't free any non-generic FIFOs in - * this case. - */ - continue; - } - if ((f->dev_ep_index == 0) && - (f->fs_xfer == NULL)) { - /* no need to free this FIFO */ - continue; - } - } else if (iface_index == USB_IFACE_INDEX_ANY) { - if ((f->methods == &usb2_ugen_methods) && - (f->dev_ep_index == 0) && (flag == 0) && - (f->fs_xfer == NULL)) { - /* no need to free this FIFO */ - continue; - } - } else { - /* no need to free this FIFO */ - continue; - } - /* free this FIFO */ - usb2_fifo_free(f); - } -} - -/*------------------------------------------------------------------------* - * usb2_peer_can_wakeup - * - * Return values: - * 0: Peer cannot do resume signalling. - * Else: Peer can do resume signalling. - *------------------------------------------------------------------------*/ -uint8_t -usb2_peer_can_wakeup(struct usb2_device *udev) -{ - const struct usb2_config_descriptor *cdp; - - cdp = udev->cdesc; - if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) { - return (cdp->bmAttributes & UC_REMOTE_WAKEUP); - } - return (0); /* not supported */ -} diff --git a/sys/dev/usb2/core/usb2_device.h b/sys/dev/usb2/core/usb2_device.h deleted file mode 100644 index 11686e2..0000000 --- a/sys/dev/usb2/core/usb2_device.h +++ /dev/null @@ -1,187 +0,0 @@ -/* $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 is used for power-save purposes. The data - * in this structure is protected by the USB BUS lock. - */ -struct usb2_power_save { - int last_xfer_time; /* copy of "ticks" */ - uint32_t type_refs[4]; /* transfer reference count */ - uint32_t read_refs; /* data read references */ - uint32_t write_refs; /* data write references */ - uint8_t suspended; /* set if USB device is suspended */ -}; - -/* - * 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_power_save pwr_save;/* power save data */ - - 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); -uint8_t usb2_peer_can_wakeup(struct usb2_device *udev); - -#endif /* _USB2_DEVICE_H_ */ diff --git a/sys/dev/usb2/core/usb2_dynamic.c b/sys/dev/usb2/core/usb2_dynamic.c deleted file mode 100644 index 3e0bdf1..0000000 --- a/sys/dev/usb2/core/usb2_dynamic.c +++ /dev/null @@ -1,155 +0,0 @@ -/* $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_test_huawei_autoinst_t usb2_test_huawei_autoinst_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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_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; -} - -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; - } -} - -static uint8_t -usb2_test_huawei_autoinst_w(struct usb2_device *udev, - struct usb2_attach_arg *uaa) -{ - return (USB_ERR_INVAL); -} - -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); -} - -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); -} - -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); -} - -void -usb2_test_huawei_unload(void *arg) -{ - /* reset function pointers */ - - usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w; - - /* wait for CPU to exit the loaded functions, if any */ - - /* XXX this is a tradeoff */ - - pause("WAIT", 16*hz); -} diff --git a/sys/dev/usb2/core/usb2_dynamic.h b/sys/dev/usb2/core/usb2_dynamic.h deleted file mode 100644 index 2c45d09..0000000 --- a/sys/dev/usb2/core/usb2_dynamic.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $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 usb2_error_t (usb2_test_huawei_autoinst_t)(struct usb2_device *udev, - struct usb2_attach_arg *uaa); -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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p; -extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p; -extern devclass_t usb2_devclass_ptr; - -/* function prototypes */ - -void usb2_test_huawei_unload(void *); -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 deleted file mode 100644 index 8ee57b4..0000000 --- a/sys/dev/usb2/core/usb2_error.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $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 - -static const char* usb_errstr_table[USB_ERR_MAX] = { - [USB_ERR_NORMAL_COMPLETION] = "USB_ERR_NORMAL_COMPLETION", - [USB_ERR_PENDING_REQUESTS] = "USB_ERR_PENDING_REQUESTS", - [USB_ERR_NOT_STARTED] = "USB_ERR_NOT_STARTED", - [USB_ERR_INVAL] = "USB_ERR_INVAL", - [USB_ERR_NOMEM] = "USB_ERR_NOMEM", - [USB_ERR_CANCELLED] = "USB_ERR_CANCELLED", - [USB_ERR_BAD_ADDRESS] = "USB_ERR_BAD_ADDRESS", - [USB_ERR_BAD_BUFSIZE] = "USB_ERR_BAD_BUFSIZE", - [USB_ERR_BAD_FLAG] = "USB_ERR_BAD_FLAG", - [USB_ERR_NO_CALLBACK] = "USB_ERR_NO_CALLBACK", - [USB_ERR_IN_USE] = "USB_ERR_IN_USE", - [USB_ERR_NO_ADDR] = "USB_ERR_NO_ADDR", - [USB_ERR_NO_PIPE] = "USB_ERR_NO_PIPE", - [USB_ERR_ZERO_NFRAMES] = "USB_ERR_ZERO_NFRAMES", - [USB_ERR_ZERO_MAXP] = "USB_ERR_ZERO_MAXP", - [USB_ERR_SET_ADDR_FAILED] = "USB_ERR_SET_ADDR_FAILED", - [USB_ERR_NO_POWER] = "USB_ERR_NO_POWER", - [USB_ERR_TOO_DEEP] = "USB_ERR_TOO_DEEP", - [USB_ERR_IOERROR] = "USB_ERR_IOERROR", - [USB_ERR_NOT_CONFIGURED] = "USB_ERR_NOT_CONFIGURED", - [USB_ERR_TIMEOUT] = "USB_ERR_TIMEOUT", - [USB_ERR_SHORT_XFER] = "USB_ERR_SHORT_XFER", - [USB_ERR_STALLED] = "USB_ERR_STALLED", - [USB_ERR_INTERRUPTED] = "USB_ERR_INTERRUPTED", - [USB_ERR_DMA_LOAD_FAILED] = "USB_ERR_DMA_LOAD_FAILED", - [USB_ERR_BAD_CONTEXT] = "USB_ERR_BAD_CONTEXT", - [USB_ERR_NO_ROOT_HUB] = "USB_ERR_NO_ROOT_HUB", - [USB_ERR_NO_INTR_THREAD] = "USB_ERR_NO_INTR_THREAD", - [USB_ERR_NOT_LOCKED] = "USB_ERR_NOT_LOCKED", -}; - -/*------------------------------------------------------------------------* - * 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_errstr_table[err] : "USB_ERR_UNKNOWN"); -} diff --git a/sys/dev/usb2/core/usb2_generic.c b/sys/dev/usb2/core/usb2_generic.c deleted file mode 100644 index 49f683d..0000000 --- a/sys/dev/usb2/core/usb2_generic.c +++ /dev/null @@ -1,2195 +0,0 @@ -/* $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_ioctl_t ugen_ioctl_post; -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 *, - const struct usb2_config *, uint8_t); -static int ugen_open_pipe_write(struct usb2_fifo *); -static int ugen_open_pipe_read(struct usb2_fifo *); -static int ugen_set_config(struct usb2_fifo *, uint8_t); -static int ugen_set_interface(struct usb2_fifo *, uint8_t, uint8_t); -static int ugen_get_cdesc(struct usb2_fifo *, struct usb2_gen_descriptor *); -static int ugen_get_sdesc(struct usb2_fifo *, struct usb2_gen_descriptor *); -static int ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd); -static int usb2_gen_fill_deviceinfo(struct usb2_fifo *, - struct usb2_device_info *); -static int ugen_re_enumerate(struct usb2_fifo *); -static int ugen_iface_ioctl(struct usb2_fifo *, u_long, void *, int); -static uint8_t ugen_fs_get_complete(struct usb2_fifo *, uint8_t *); -static int ugen_fs_uninit(struct usb2_fifo *f); - -/* structures */ - -struct usb2_fifo_methods usb2_ugen_methods = { - .f_open = &ugen_open, - .f_close = &ugen_close, - .f_ioctl = &ugen_ioctl, - .f_ioctl_post = &ugen_ioctl_post, - .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); - switch (usb2_get_speed(f->udev)) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - f->nframes = UGEN_HW_FRAMES; - f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; - break; - default: - f->nframes = UGEN_HW_FRAMES * 8; - f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; - break; - } - - 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 */ - DPRINTFN(6, "no FIFOs\n"); - } -} - -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); - } - 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); - } - 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]); -} - -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]); -} - -static void -ugen_stop_io(struct usb2_fifo *f) -{ - /* stop transfers */ - usb2_transfer_stop(f->xfer[0]); - usb2_transfer_stop(f->xfer[1]); -} - -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; - } -} - -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; - } -} - -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); - } -} - -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); - } -} - -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; - } -} - -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; - } -} - -static int -ugen_set_config(struct usb2_fifo *f, uint8_t index) -{ - DPRINTFN(2, "index %u\n", index); - - 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); - } - /* make sure all FIFO's are gone */ - /* else there can be a deadlock */ - if (ugen_fs_uninit(f)) { - /* ignore any errors */ - DPRINTFN(6, "no FIFOs\n"); - } - /* 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->udev->flags.usb2_mode != USB_MODE_HOST) { - /* not possible in device side mode */ - return (ENOTTY); - } - /* make sure all FIFO's are gone */ - /* else there can be a deadlock */ - if (ugen_fs_uninit(f)) { - /* ignore any errors */ - DPRINTFN(6, "no FIFOs\n"); - } - /* 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 (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 (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); -} - -/*------------------------------------------------------------------------* - * ugen_get_iface_driver - * - * This function generates an USB interface description for userland. - * - * Returns: - * 0: Success - * Else: Failure - *------------------------------------------------------------------------*/ -static int -ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd) -{ - struct usb2_device *udev = f->udev; - struct usb2_interface *iface; - const char *ptr; - const char *desc; - unsigned int len; - unsigned int maxlen; - char buf[128]; - int error; - - DPRINTFN(6, "\n"); - - if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { - /* userland pointer should not be zero */ - return (EINVAL); - } - - iface = usb2_get_iface(udev, ugd->ugd_iface_index); - if ((iface == NULL) || (iface->idesc == NULL)) { - /* invalid interface index */ - return (EINVAL); - } - - /* read out device nameunit string, if any */ - if ((iface->subdev != NULL) && - device_is_attached(iface->subdev) && - (ptr = device_get_nameunit(iface->subdev)) && - (desc = device_get_desc(iface->subdev))) { - - /* print description */ - snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); - - /* range checks */ - maxlen = ugd->ugd_maxlen - 1; - len = strlen(buf); - if (len > maxlen) - len = maxlen; - - /* update actual length, including terminating zero */ - ugd->ugd_actlen = len + 1; - - /* copy out interface description */ - error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); - } else { - /* zero length string is default */ - error = copyout("", ugd->ugd_data, 1); - } - return (error); -} - -/*------------------------------------------------------------------------* - * 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; - - 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 (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; - - /* - * This request can be useful for testing USB drivers: - */ - error = priv_check(curthread, PRIV_DRIVER); - if (error) { - return (error); - } - /* get the device unconfigured */ - error = ugen_set_config(f, USB_UNCONFIG_INDEX); - if (error) { - return (error); - } - /* do a bus-reset */ - mtx_lock(f->priv_mtx); - error = usb2_req_re_enumerate(udev, f->priv_mtx); - mtx_unlock(f->priv_mtx); - - if (error) { - return (ENXIO); - } - /* restore configuration to index 0 */ - error = ugen_set_config(f, 0); - if (error) { - return (error); - } - return (0); -} - -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; - 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); -} - -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_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags, - struct thread *td) -{ - 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_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; - - DPRINTFN(6, "cmd=0x%08lx\n", cmd); - - 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_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; - } 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 = ENOIOCTL; - break; - } - - DPRINTFN(6, "error=%d\n", error); - - 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; - uint8_t old_mode; - - if ((udev == NULL) || - (udev->parent_hub == NULL)) { - return (EINVAL); - } - err = priv_check(curthread, PRIV_ROOT); - if (err) - return (err); - - /* get old power mode */ - old_mode = udev->power_mode; - - /* if no change, then just return */ - if (old_mode == mode) - return (0); - - switch (mode) { - case USB_POWER_MODE_OFF: - /* get the device unconfigured */ - err = ugen_set_config(f, USB_UNCONFIG_INDEX); - if (err) { - DPRINTFN(0, "Could not unconfigure " - "device (ignored)\n"); - } - - /* 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: - case USB_POWER_MODE_SAVE: - break; - - case USB_POWER_MODE_RESUME: - err = usb2_req_clear_port_feature(udev->parent_hub, - NULL, udev->port_no, UHF_PORT_SUSPEND); - mode = USB_POWER_MODE_SAVE; - break; - - case USB_POWER_MODE_SUSPEND: - err = usb2_req_set_port_feature(udev->parent_hub, - NULL, udev->port_no, UHF_PORT_SUSPEND); - mode = USB_POWER_MODE_SAVE; - break; - - default: - return (EINVAL); - } - - if (err) - return (ENXIO); /* I/O failure */ - - /* if we are powered off we need to re-enumerate first */ - if (old_mode == USB_POWER_MODE_OFF) { - err = ugen_re_enumerate(f); - if (err) - return (err); - } - - /* set new power mode */ - usb2_set_power_mode(udev, 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 = ENOIOCTL; - break; - } - return (error); -} - -static int -ugen_ioctl_post(struct usb2_fifo *f, u_long cmd, void *addr, int fflags, - struct thread *td) -{ - 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; - struct usb2_fs_init *pinit; - struct usb2_fs_uninit *puninit; - 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; - - DPRINTFN(6, "cmd=0x%08lx\n", cmd); - - 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_GET_IFACE_DRIVER: - error = ugen_get_iface_driver(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_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; - - 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->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; - - default: - mtx_lock(f->priv_mtx); - error = ugen_iface_ioctl(f, cmd, addr, fflags); - mtx_unlock(f->priv_mtx); - break; - } - 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; - } -} diff --git a/sys/dev/usb2/core/usb2_generic.h b/sys/dev/usb2/core/usb2_generic.h deleted file mode 100644 index 3a4e7c9..0000000 --- a/sys/dev/usb2/core/usb2_generic.h +++ /dev/null @@ -1,33 +0,0 @@ -/* $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 deleted file mode 100644 index 65f0f01..0000000 --- a/sys/dev/usb2/core/usb2_handle_request.c +++ /dev/null @@ -1,756 +0,0 @@ -/* $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 *, uint8_t); -static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *, uint8_t); -static usb2_error_t usb2_handle_request(struct usb2_xfer *); -static usb2_error_t usb2_handle_set_config(struct usb2_xfer *, uint8_t); -static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *, uint8_t, - uint8_t); -static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *, void **, - uint16_t *, struct usb2_device_request, uint16_t, - uint8_t); - -/*------------------------------------------------------------------------* - * 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->xroot->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); -} - -/*------------------------------------------------------------------------* - * 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) -{ - struct usb2_device *udev = xfer->xroot->udev; - usb2_error_t err = 0; - - /* - * We need to protect against other threads doing probe and - * attach: - */ - USB_XFER_UNLOCK(xfer); - mtx_lock(&Giant); /* XXX */ - sx_xlock(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(udev, conf_no)) { - DPRINTF("set config %d failed\n", conf_no); - err = USB_ERR_STALLED; - goto done; - } - if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { - DPRINTF("probe and attach failed\n"); - err = USB_ERR_STALLED; - goto done; - } -done: - mtx_unlock(&Giant); /* XXX */ - sx_unlock(udev->default_sx + 1); - USB_XFER_LOCK(xfer); - 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->xroot->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: - */ - USB_XFER_UNLOCK(xfer); - 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); - USB_XFER_LOCK(xfer); - return (0); - -tr_stalled: - mtx_unlock(&Giant); - sx_unlock(udev->default_sx + 1); - USB_XFER_LOCK(xfer); - 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) -{ - struct usb2_device *udev = xfer->xroot->udev; - usb2_error_t err; - - USB_XFER_UNLOCK(xfer); - err = usb2_set_endpoint_stall(udev, - usb2_get_pipe_by_addr(udev, ep), do_stall); - USB_XFER_LOCK(xfer); - 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); - } - USB_BUS_LOCK(udev->bus); - halted = pipe->is_stalled; - USB_BUS_UNLOCK(udev->bus); - - 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->xroot->udev; - bus = udev->bus; - - USB_BUS_LOCK(bus); - - if (is_on) { - udev->flags.remote_wakeup = 1; - } else { - udev->flags.remote_wakeup = 0; - } - - USB_BUS_UNLOCK(bus); - - /* In case we are out of sync, update the power state. */ - - usb2_bus_power_update(udev->bus); - - 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->xroot->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; - - USB_BUS_LOCK(udev->bus); - if (udev->flags.remote_wakeup) { - wValue |= UDS_REMOTE_WAKEUP; - } - if (udev->flags.self_powered) { - wValue |= UDS_SELF_POWERED; - } - USB_BUS_UNLOCK(udev->bus); - - 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 deleted file mode 100644 index 6cc0503..0000000 --- a/sys/dev/usb2/core/usb2_handle_request.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 04d7d4d..0000000 --- a/sys/dev/usb2/core/usb2_hid.c +++ /dev/null @@ -1,582 +0,0 @@ -/* $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 deleted file mode 100644 index 3fb7368..0000000 --- a/sys/dev/usb2/core/usb2_hid.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $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 deleted file mode 100644 index 8c2d639..0000000 --- a/sys/dev/usb2/core/usb2_hub.c +++ /dev/null @@ -1,1842 +0,0 @@ -/* $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 -#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 */ -#define UHUB_N_TRANSFER 1 - -#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 - -static int usb2_power_timeout = 30; /* seconds */ - -SYSCTL_INT(_hw_usb2, OID_AUTO, power_timeout, CTLFLAG_RW, - &usb2_power_timeout, 0, "USB power timeout"); - -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[UHUB_N_TRANSFER]; /* interrupt xfer */ - uint8_t sc_flags; -#define UHUB_FLAG_DID_EXPLORE 0x01 - 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 device_suspend_t uhub_suspend; -static device_resume_t uhub_resume; - -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 void usb2_dev_resume_peer(struct usb2_device *udev); -static void usb2_dev_suspend_peer(struct usb2_device *udev); - -static const struct usb2_config uhub_config[UHUB_N_TRANSFER] = { - - [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, - }, -}; - -/* - * 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, uhub_suspend), - DEVMETHOD(device_resume, uhub_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_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: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* - * Do a clear-stall. The "stall_pipe" flag - * will get cleared before next callback by - * the USB stack. - */ - xfer->flags.stall_pipe = 1; - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - } - break; - } -} - -/*------------------------------------------------------------------------* - * 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); - - if (sc->sc_st.port_status & UPS_SUSPEND) { - DPRINTF("Port %d was still " - "suspended, clearing.\n", portno); - err = usb2_req_clear_port_feature(sc->sc_udev, - &Giant, portno, UHF_PORT_SUSPEND); - } - /* USB Host Mode */ - - /* wait for maximum device power up time */ - - usb2_pause_mtx(&Giant, - USB_MS_TO_TICKS(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 - */ - switch (udev->speed) { - case USB_SPEED_HIGH: - if (sc->sc_st.port_status & UPS_HIGH_SPEED) - speed = USB_SPEED_HIGH; - else if (sc->sc_st.port_status & UPS_LOW_SPEED) - speed = USB_SPEED_LOW; - else - speed = USB_SPEED_FULL; - break; - case USB_SPEED_FULL: - if (sc->sc_st.port_status & UPS_LOW_SPEED) - speed = USB_SPEED_LOW; - else - speed = USB_SPEED_FULL; - break; - case USB_SPEED_LOW: - speed = USB_SPEED_LOW; - break; - default: - /* same speed like parent */ - speed = udev->speed; - break; - } - /* - * Figure out the device mode - * - * NOTE: This part is currently FreeBSD specific. - */ - if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) - usb2_mode = USB_MODE_DEVICE; - else - usb2_mode = 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) { - DPRINTF("clearing suspend failed.\n"); - goto done; - } - /* get fresh status */ - - err = uhub_read_port_status(sc, portno); - if (err) { - DPRINTF("reading port status failed.\n"); - goto done; - } - /* get current state */ - - if (sc->sc_st.port_status & UPS_SUSPEND) { - is_suspend = 1; - } else { - is_suspend = 0; - } - - DPRINTF("suspended=%u\n", is_suspend); - - /* do the suspend or resume */ - - if (child) { - /* - * This code handle two cases: 1) Host Mode - we can only - * receive resume here 2) Device Mode - we can receive - * suspend and resume here - */ - if (is_suspend == 0) - usb2_dev_resume_peer(child); - else if (child->flags.usb2_mode == USB_MODE_DEVICE) - usb2_dev_suspend_peer(child); - } -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); - } - if (udev->pwr_save.suspended) { - /* need to wait until the child signals resume */ - DPRINTF("Device is suspended!\n"); - return (0); - } - 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_OVERCURRENT_INDICATOR) { - DPRINTF("Overcurrent on port %u.\n", portno); - err = usb2_req_clear_port_feature( - udev, &Giant, portno, UHF_C_PORT_OVER_CURRENT); - if (err) { - /* most likely the HUB is gone */ - break; - } - } - if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { - /* - * Fake a connect status change so that the - * status gets checked initially! - */ - sc->sc_st.port_change |= - UPS_C_CONNECT_STATUS; - } - 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; - } - - /* initial status checked */ - sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; - - /* return success */ - 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; - - 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, UHUB_N_TRANSFER, 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_MS_TO_TICKS(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, USB_MS_TO_TICKS(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 */ - - USB_XFER_LOCK(sc->sc_xfer[0]); - usb2_transfer_start(sc->sc_xfer[0]); - USB_XFER_UNLOCK(sc->sc_xfer[0]); - - /* Enable automatic power save on all USB HUBs */ - - usb2_set_power_mode(udev, USB_POWER_MODE_SAVE); - - return (0); - -error: - usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); - - 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, UHUB_N_TRANSFER); - - free(hub, M_USBDEV); - sc->sc_udev->hub = NULL; - return (0); -} - -static int -uhub_suspend(device_t dev) -{ - DPRINTF("\n"); - /* Sub-devices are not suspended here! */ - return (0); -} - -static int -uhub_resume(device_t dev) -{ - DPRINTF("\n"); - /* Sub-devices are not resumed here! */ - return (0); -} - -static void -uhub_driver_added(device_t dev, driver_t *driver) -{ - usb2_needs_explore_all(); -} - -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; -} - -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; - uint8_t speed; - - USB_BUS_LOCK_ASSERT(bus, MA_OWNED); - - speed = usb2_get_speed(udev); - - switch (speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - if (speed == 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; - break; - default: - 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; - break; - } - 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; -} - -/*------------------------------------------------------------------------* - * 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++; - } -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK_ASSERT(bus, 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); -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_BUS_LOCK(bus); - if (do_probe) { - bus->do_probe = 1; - } - if (usb2_proc_msignal(&bus->explore_proc, - &bus->explore_msg[0], &bus->explore_msg[1])) { - /* ignore */ - } - USB_BUS_UNLOCK(bus); -} - -/*------------------------------------------------------------------------* - * 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--; - } -} - -/*------------------------------------------------------------------------* - * usb2_bus_power_update - * - * This function will ensure that all USB devices on the given bus are - * properly suspended or resumed according to the device transfer - * state. - *------------------------------------------------------------------------*/ -void -usb2_bus_power_update(struct usb2_bus *bus) -{ - usb2_needs_explore(bus, 0 /* no probe */ ); -} - -/*------------------------------------------------------------------------* - * usb2_transfer_power_ref - * - * This function will modify the power save reference counts and - * wakeup the USB device associated with the given USB transfer, if - * needed. - *------------------------------------------------------------------------*/ -void -usb2_transfer_power_ref(struct usb2_xfer *xfer, int val) -{ - static const uint32_t power_mask[4] = { - [UE_CONTROL] = USB_HW_POWER_CONTROL, - [UE_BULK] = USB_HW_POWER_BULK, - [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, - [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, - }; - struct usb2_device *udev; - uint8_t needs_explore; - uint8_t needs_hw_power; - uint8_t xfer_type; - - udev = xfer->xroot->udev; - - if (udev->device_index == USB_ROOT_HUB_ADDR) { - /* no power save for root HUB */ - return; - } - USB_BUS_LOCK(udev->bus); - - xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE; - - udev->pwr_save.last_xfer_time = ticks; - udev->pwr_save.type_refs[xfer_type] += val; - - if (xfer->flags_int.control_xfr) { - udev->pwr_save.read_refs += val; - if (xfer->flags_int.usb2_mode == USB_MODE_HOST) { - /* - * it is not allowed to suspend during a control - * transfer - */ - udev->pwr_save.write_refs += val; - } - } else if (USB_GET_DATA_ISREAD(xfer)) { - udev->pwr_save.read_refs += val; - } else { - udev->pwr_save.write_refs += val; - } - - if (udev->pwr_save.suspended) - needs_explore = - (udev->pwr_save.write_refs != 0) || - ((udev->pwr_save.read_refs != 0) && - (usb2_peer_can_wakeup(udev) == 0)); - else - needs_explore = 0; - - if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { - DPRINTF("Adding type %u to power state\n", xfer_type); - udev->bus->hw_power_state |= power_mask[xfer_type]; - needs_hw_power = 1; - } else { - needs_hw_power = 0; - } - - USB_BUS_UNLOCK(udev->bus); - - if (needs_explore) { - DPRINTF("update\n"); - usb2_bus_power_update(udev->bus); - } else if (needs_hw_power) { - DPRINTF("needs power\n"); - if (udev->bus->methods->set_hw_power != NULL) { - (udev->bus->methods->set_hw_power) (udev->bus); - } - } - return; -} - -/*------------------------------------------------------------------------* - * usb2_bus_powerd - * - * This function implements the USB power daemon and is called - * regularly from the USB explore thread. - *------------------------------------------------------------------------*/ -void -usb2_bus_powerd(struct usb2_bus *bus) -{ - struct usb2_device *udev; - unsigned int temp; - unsigned int limit; - unsigned int mintime; - uint32_t type_refs[5]; - uint8_t x; - uint8_t rem_wakeup; - - limit = usb2_power_timeout; - if (limit == 0) - limit = hz; - else if (limit > 255) - limit = 255 * hz; - else - limit = limit * hz; - - DPRINTF("bus=%p\n", bus); - - USB_BUS_LOCK(bus); - - /* - * The root HUB device is never suspended - * and we simply skip it. - */ - for (x = USB_ROOT_HUB_ADDR + 1; - x != bus->devices_max; x++) { - - udev = bus->devices[x]; - if (udev == NULL) - continue; - - rem_wakeup = usb2_peer_can_wakeup(udev); - - temp = ticks - udev->pwr_save.last_xfer_time; - - if ((udev->power_mode == USB_POWER_MODE_ON) || - (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || - (udev->pwr_save.write_refs != 0) || - ((udev->pwr_save.read_refs != 0) && - (rem_wakeup == 0))) { - - /* check if we are suspended */ - if (udev->pwr_save.suspended != 0) { - USB_BUS_UNLOCK(bus); - usb2_dev_resume_peer(udev); - USB_BUS_LOCK(bus); - } - } else if (temp >= limit) { - - /* check if we are not suspended */ - if (udev->pwr_save.suspended == 0) { - USB_BUS_UNLOCK(bus); - usb2_dev_suspend_peer(udev); - USB_BUS_LOCK(bus); - } - } - } - - /* reset counters */ - - mintime = 0 - 1; - type_refs[0] = 0; - type_refs[1] = 0; - type_refs[2] = 0; - type_refs[3] = 0; - type_refs[4] = 0; - - /* Re-loop all the devices to get the actual state */ - - for (x = USB_ROOT_HUB_ADDR + 1; - x != bus->devices_max; x++) { - - udev = bus->devices[x]; - if (udev == NULL) - continue; - - /* we found a non-Root-Hub USB device */ - type_refs[4] += 1; - - /* "last_xfer_time" can be updated by a resume */ - temp = ticks - udev->pwr_save.last_xfer_time; - - /* - * Compute minimum time since last transfer for the complete - * bus: - */ - if (temp < mintime) - mintime = temp; - - if (udev->pwr_save.suspended == 0) { - type_refs[0] += udev->pwr_save.type_refs[0]; - type_refs[1] += udev->pwr_save.type_refs[1]; - type_refs[2] += udev->pwr_save.type_refs[2]; - type_refs[3] += udev->pwr_save.type_refs[3]; - } - } - - if (mintime >= (1 * hz)) { - /* recompute power masks */ - DPRINTF("Recomputing power masks\n"); - bus->hw_power_state = 0; - if (type_refs[UE_CONTROL] != 0) - bus->hw_power_state |= USB_HW_POWER_CONTROL; - if (type_refs[UE_BULK] != 0) - bus->hw_power_state |= USB_HW_POWER_BULK; - if (type_refs[UE_INTERRUPT] != 0) - bus->hw_power_state |= USB_HW_POWER_INTERRUPT; - if (type_refs[UE_ISOCHRONOUS] != 0) - bus->hw_power_state |= USB_HW_POWER_ISOC; - if (type_refs[4] != 0) - bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB; - } - USB_BUS_UNLOCK(bus); - - if (bus->methods->set_hw_power != NULL) { - /* always update hardware power! */ - (bus->methods->set_hw_power) (bus); - } - return; -} - -/*------------------------------------------------------------------------* - * usb2_dev_resume_peer - * - * This function will resume an USB peer and do the required USB - * signalling to get an USB device out of the suspended state. - *------------------------------------------------------------------------*/ -static void -usb2_dev_resume_peer(struct usb2_device *udev) -{ - struct usb2_bus *bus; - int err; - - /* be NULL safe */ - if (udev == NULL) - return; - - /* check if already resumed */ - if (udev->pwr_save.suspended == 0) - return; - - /* we need a parent HUB to do resume */ - if (udev->parent_hub == NULL) - return; - - DPRINTF("udev=%p\n", udev); - - if ((udev->flags.usb2_mode == USB_MODE_DEVICE) && - (udev->flags.remote_wakeup == 0)) { - /* - * If the host did not set the remote wakeup feature, we can - * not wake it up either! - */ - DPRINTF("remote wakeup is not set!\n"); - return; - } - /* get bus pointer */ - bus = udev->bus; - - /* resume parent hub first */ - usb2_dev_resume_peer(udev->parent_hub); - - /* resume current port (Valid in Host and Device Mode) */ - err = usb2_req_clear_port_feature(udev->parent_hub, - &Giant, udev->port_no, UHF_PORT_SUSPEND); - if (err) { - DPRINTFN(0, "Resuming port failed!\n"); - return; - } - /* resume settle time */ - usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); - - if (bus->methods->device_resume != NULL) { - /* resume USB device on the USB controller */ - (bus->methods->device_resume) (udev); - } - USB_BUS_LOCK(bus); - /* set that this device is now resumed */ - udev->pwr_save.suspended = 0; - /* make sure that we don't go into suspend right away */ - udev->pwr_save.last_xfer_time = ticks; - - /* make sure the needed power masks are on */ - if (udev->pwr_save.type_refs[UE_CONTROL] != 0) - bus->hw_power_state |= USB_HW_POWER_CONTROL; - if (udev->pwr_save.type_refs[UE_BULK] != 0) - bus->hw_power_state |= USB_HW_POWER_BULK; - if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) - bus->hw_power_state |= USB_HW_POWER_INTERRUPT; - if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) - bus->hw_power_state |= USB_HW_POWER_ISOC; - USB_BUS_UNLOCK(bus); - - if (bus->methods->set_hw_power != NULL) { - /* always update hardware power! */ - (bus->methods->set_hw_power) (bus); - } - sx_xlock(udev->default_sx + 1); - /* notify all sub-devices about resume */ - err = usb2_suspend_resume(udev, 0); - sx_unlock(udev->default_sx + 1); - - /* check if peer has wakeup capability */ - if (usb2_peer_can_wakeup(udev)) { - /* clear remote wakeup */ - err = usb2_req_clear_device_feature(udev, - &Giant, UF_DEVICE_REMOTE_WAKEUP); - if (err) { - DPRINTFN(0, "Clearing device " - "remote wakeup failed: %s!\n", - usb2_errstr(err)); - } - } - return; -} - -/*------------------------------------------------------------------------* - * usb2_dev_suspend_peer - * - * This function will suspend an USB peer and do the required USB - * signalling to get an USB device into the suspended state. - *------------------------------------------------------------------------*/ -static void -usb2_dev_suspend_peer(struct usb2_device *udev) -{ - struct usb2_device *hub; - struct usb2_device *child; - uint32_t temp; - int err; - uint8_t x; - uint8_t nports; - uint8_t suspend_parent; - -repeat: - /* be NULL safe */ - if (udev == NULL) - return; - - /* check if already suspended */ - if (udev->pwr_save.suspended) - return; - - /* we need a parent HUB to do suspend */ - if (udev->parent_hub == NULL) - return; - - DPRINTF("udev=%p\n", udev); - - /* check if all devices on the parent hub are suspended */ - hub = udev->parent_hub; - if (hub != NULL) { - nports = hub->hub->nports; - suspend_parent = 1; - - for (x = 0; x != nports; x++) { - - child = usb2_bus_port_get_device(hub->bus, - hub->hub->ports + x); - - if (child == NULL) - continue; - - if (child->pwr_save.suspended) - continue; - - if (child == udev) - continue; - - /* another device on the HUB is not suspended */ - suspend_parent = 0; - - break; - } - } else { - suspend_parent = 0; - } - - sx_xlock(udev->default_sx + 1); - /* notify all sub-devices about suspend */ - err = usb2_suspend_resume(udev, 1); - sx_unlock(udev->default_sx + 1); - - if (usb2_peer_can_wakeup(udev)) { - /* allow device to do remote wakeup */ - err = usb2_req_set_device_feature(udev, - &Giant, UF_DEVICE_REMOTE_WAKEUP); - if (err) { - DPRINTFN(0, "Setting device " - "remote wakeup failed!\n"); - } - } - USB_BUS_LOCK(udev->bus); - /* - * Set that this device is suspended. This variable must be set - * before calling USB controller suspend callbacks. - */ - udev->pwr_save.suspended = 1; - USB_BUS_UNLOCK(udev->bus); - - if (udev->bus->methods->device_suspend != NULL) { - - /* suspend device on the USB controller */ - (udev->bus->methods->device_suspend) (udev); - - /* do DMA delay */ - temp = usb2_get_dma_delay(udev->bus); - usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(temp)); - - } - /* suspend current port */ - err = usb2_req_set_port_feature(udev->parent_hub, - &Giant, udev->port_no, UHF_PORT_SUSPEND); - if (err) { - DPRINTFN(0, "Suspending port failed\n"); - return; - } - if (suspend_parent) { - udev = udev->parent_hub; - goto repeat; - } - return; -} - -/*------------------------------------------------------------------------* - * usb2_set_power_mode - * - * This function will set the power mode, see USB_POWER_MODE_XXX for a - * USB device. - *------------------------------------------------------------------------*/ -void -usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode) -{ - /* filter input argument */ - if ((power_mode != USB_POWER_MODE_ON) && - (power_mode != USB_POWER_MODE_OFF)) { - power_mode = USB_POWER_MODE_SAVE; - } - udev->power_mode = power_mode; /* update copy of power mode */ - - usb2_bus_power_update(udev->bus); - - return; -} diff --git a/sys/dev/usb2/core/usb2_hub.h b/sys/dev/usb2/core/usb2_hub.h deleted file mode 100644 index 87d85b3..0000000 --- a/sys/dev/usb2/core/usb2_hub.h +++ /dev/null @@ -1,80 +0,0 @@ -/* $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); -void usb2_bus_power_update(struct usb2_bus *bus); -void usb2_bus_powerd(struct usb2_bus *bus); - -#endif /* _USB2_HUB_H_ */ diff --git a/sys/dev/usb2/core/usb2_if.m b/sys/dev/usb2/core/usb2_if.m deleted file mode 100644 index d0db8d4..0000000 --- a/sys/dev/usb2/core/usb2_if.m +++ /dev/null @@ -1,52 +0,0 @@ -#- -# 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 deleted file mode 100644 index eaab8a3..0000000 --- a/sys/dev/usb2/core/usb2_lookup.c +++ /dev/null @@ -1,134 +0,0 @@ -/* $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 deleted file mode 100644 index e447292..0000000 --- a/sys/dev/usb2/core/usb2_lookup.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $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 deleted file mode 100644 index 1f06f0f..0000000 --- a/sys/dev/usb2/core/usb2_mbuf.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $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 deleted file mode 100644 index 109340c..0000000 --- a/sys/dev/usb2/core/usb2_mbuf.h +++ /dev/null @@ -1,102 +0,0 @@ -/* $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; - uint8_t last_packet:1; - uint8_t unused:7; -}; - -/* - * 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 deleted file mode 100644 index fbd7dbc..0000000 --- a/sys/dev/usb2/core/usb2_msctest.c +++ /dev/null @@ -1,578 +0,0 @@ -/* $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 -#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,}, - .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); -} - -static void -bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) -{ - sc->state = xfer_index; - usb2_transfer_start(sc->xfer[xfer_index]); -} - -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; - } - } -} - -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; - sc->cbw.bCDBLength = sc->cmd_len; - 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; - } -} - -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; - } -} - -static void -bbb_data_rd_cs_callback(struct usb2_xfer *xfer) -{ - bbb_data_clear_stall_callback(xfer, ST_STATUS, - ST_DATA_RD); -} - -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); -} - -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; - } -} - -/*------------------------------------------------------------------------* - * 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, - uint8_t do_eject) -{ - 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 */ - sc->cbw.CBWCDB[1] = 0; - sc->cbw.CBWCDB[2] = 0; - sc->cbw.CBWCDB[3] = 0; - sc->cbw.CBWCDB[4] = 0x24; /* length */ - sc->cbw.CBWCDB[5] = 0; - err = bbb_command_start(sc, DIR_IN, 0, - sc->buffer, 0x24, 6, USB_MS_HZ); - - if ((sc->actlen != 0) && (err == 0)) { - sid_type = sc->buffer[0] & 0x1F; - if (sid_type == 0x05) { - /* CD-ROM */ - if (do_eject) { - /* 0: opcode: SCSI START/STOP */ - sc->cbw.CBWCDB[0] = 0x1b; - /* 1: byte2: Not immediate */ - sc->cbw.CBWCDB[1] = 0x00; - /* 2..3: reserved */ - sc->cbw.CBWCDB[2] = 0x00; - sc->cbw.CBWCDB[3] = 0x00; - /* 4: Load/Eject command */ - sc->cbw.CBWCDB[4] = 0x02; - /* 5: control */ - sc->cbw.CBWCDB[5] = 0x00; - err = bbb_command_start(sc, DIR_OUT, 0, - NULL, 0, 6, USB_MS_HZ); - - DPRINTFN(0, "Eject CD command " - "status: %s\n", usb2_errstr(err)); - } - err = 0; - goto done; - } - } else if ((err != 2) && --timeout) { - usb2_pause_mtx(&sc->mtx, 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); -} diff --git a/sys/dev/usb2/core/usb2_msctest.h b/sys/dev/usb2/core/usb2_msctest.h deleted file mode 100644 index 5bf64d0..0000000 --- a/sys/dev/usb2/core/usb2_msctest.h +++ /dev/null @@ -1,33 +0,0 @@ -/* $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, uint8_t do_eject); - -#endif /* _USB2_MSCTEST_H_ */ diff --git a/sys/dev/usb2/core/usb2_parse.c b/sys/dev/usb2/core/usb2_parse.c deleted file mode 100644 index 1f722b5..0000000 --- a/sys/dev/usb2/core/usb2_parse.c +++ /dev/null @@ -1,225 +0,0 @@ -/* $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) -{ - uint8_t *desc_next; - uint8_t *start; - uint8_t *end; - uint8_t *desc; - - /* be NULL safe */ - if (cd == NULL) - return (NULL); - - /* We assume that the "wTotalLength" has been checked. */ - start = (uint8_t *)cd; - end = start + UGETW(cd->wTotalLength); - desc = (uint8_t *)_desc; - - /* Get start of next USB descriptor. */ - if (desc == NULL) - desc = start; - else - desc = desc + desc[0]; - - /* Check that the next USB descriptor is within the range. */ - if ((desc < start) || (desc >= end)) - return (NULL); /* out of range, or EOD */ - - /* Check that the second next USB descriptor is within range. */ - desc_next = desc + desc[0]; - if ((desc_next < start) || (desc_next > end)) - return (NULL); /* out of range */ - - /* Check minimum descriptor length. */ - if (desc[0] < 3) - return (NULL); /* too short descriptor */ - - /* Return start of next descriptor. */ - return ((struct usb2_descriptor *)desc); -} - -/*------------------------------------------------------------------------* - * 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 deleted file mode 100644 index a9e6509..0000000 --- a/sys/dev/usb2/core/usb2_parse.h +++ /dev/null @@ -1,41 +0,0 @@ -/* $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 deleted file mode 100644 index 4c50695..0000000 --- a/sys/dev/usb2/core/usb2_process.c +++ /dev/null @@ -1,426 +0,0 @@ -/* $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); -} - -/*------------------------------------------------------------------------* - * usb2_proc_create - * - * 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 - *------------------------------------------------------------------------*/ -int -usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx, - const char *pmesg, 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, pmesg)) { - DPRINTFN(0, "Unable to create USB process."); - up->up_ptr = NULL; - goto error; - } - return (0); - -error: - usb2_proc_free(up); - return (ENOMEM); -} - -/*------------------------------------------------------------------------* - * usb2_proc_free - * - * 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_free(struct usb2_process *up) -{ - /* check if not initialised */ - if (up->up_mtx == NULL) - 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; -} - -/*------------------------------------------------------------------------* - * 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; - - /* check if gone, return dummy value */ - if (up->up_gone) - return (_pm0); - - 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) -{ - if (up->up_gone) - return (1); - - mtx_assert(up->up_mtx, MA_OWNED); - return (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; - - /* check if gone */ - if (up->up_gone) - return; - - 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); - } -} - -/*------------------------------------------------------------------------* - * 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) -{ - /* check if not initialised */ - if (up->up_mtx == NULL) - return; - /* handle special case with Giant */ - 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); -} diff --git a/sys/dev/usb2/core/usb2_process.h b/sys/dev/usb2/core/usb2_process.h deleted file mode 100644 index 756b929..0000000 --- a/sys/dev/usb2/core/usb2_process.h +++ /dev/null @@ -1,88 +0,0 @@ -/* $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_is_gone(struct usb2_process *up); -int usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx, - const char *pmesg, uint8_t prio); -void usb2_proc_drain(struct usb2_process *up); -void usb2_proc_mwait(struct usb2_process *up, void *pm0, void *pm1); -void usb2_proc_free(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 deleted file mode 100644 index 75a14ff..0000000 --- a/sys/dev/usb2/core/usb2_request.c +++ /dev/null @@ -1,1486 +0,0 @@ -/* $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 -#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->xroot->udev->default_cv); - break; - } -} - -/*------------------------------------------------------------------------* - * 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_device *udev; - struct usb2_pipe *pipe; - struct usb2_pipe *pipe_end; - struct usb2_pipe *pipe_first; - uint8_t to = USB_EP_MAX; - - udev = xfer->xroot->udev; - - USB_BUS_LOCK(udev->bus); - - /* round robin pipe clear stall */ - - pipe = udev->pipe_curr; - pipe_end = udev->pipes + USB_EP_MAX; - pipe_first = 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; - USB_BUS_UNLOCK(udev->bus); - - usb2_start_hardware(xfer); - - USB_BUS_LOCK(udev->bus); - break; - } - pipe++; - if (--to) - goto tr_setup; - break; - - default: - if (xfer->error == USB_ERR_CANCELLED) { - break; - } - goto tr_setup; - } - - /* store current pipe */ - udev->pipe_curr = pipe; - USB_BUS_UNLOCK(udev->bus); -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_XFER_LOCK(xfer); - - 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) { - USB_XFER_UNLOCK(xfer); - err = usb2_copy_in_user(xfer->frbuffers + 1, - 0, data, temp); - USB_XFER_LOCK(xfer); - 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->xroot->xfer_mtx, - USB_MS_TO_TICKS(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(udev->default_cv, - xfer->xroot->xfer_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) { - USB_XFER_UNLOCK(xfer); - err = usb2_copy_out_user(xfer->frbuffers + 1, - 0, data, temp); - USB_XFER_LOCK(xfer); - 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); - } - USB_XFER_UNLOCK(xfer); - -done: - sx_xunlock(udev->default_sx); - - if (mtx) { - mtx_lock(mtx); - } - return ((usb2_error_t)err); -} - -/*------------------------------------------------------------------------* - * usb2_do_request_proc - factored out code - * - * This function is factored out code. It does basically the same like - * usb2_do_request_flags, except it will check the status of the - * passed process argument before doing the USB request. If the - * process is draining the USB_ERR_IOERROR code will be returned. It - * is assumed that the mutex associated with the process is locked - * when calling this function. - *------------------------------------------------------------------------*/ -usb2_error_t -usb2_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc, - struct usb2_device_request *req, void *data, uint32_t flags, - uint16_t *actlen, uint32_t timeout) -{ - usb2_error_t err; - uint16_t len; - - /* get request data length */ - len = UGETW(req->wLength); - - /* check if the device is being detached */ - if (usb2_proc_is_gone(pproc)) { - err = USB_ERR_IOERROR; - goto done; - } - - /* forward the USB request */ - err = usb2_do_request_flags(udev, pproc->up_mtx, - req, data, flags, actlen, timeout); - -done: - /* on failure we zero the data */ - /* on short packet we zero the unused data */ - if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { - if (err) - memset(data, 0, len); - else if (actlen && *actlen != len) - memset(((uint8_t *)data) + *actlen, 0, len - *actlen); - } - return (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, USB_MS_TO_TICKS(pr_poll_delay)); - n += pr_poll_delay; -#else - /* wait for the device to recover from reset */ - usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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, USB_MS_TO_TICKS(pr_recovery_delay)); -#else - /* wait for the device to recover from reset */ - usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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_flags(udev, mtx, &req, - desc, 0, NULL, 1000); - - if (err) { - if (!retries) { - goto done; - } - retries--; - - usb2_pause_mtx(mtx, hz / 5); - - 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); - } - if (string_index == 0) { - /* this is the language table */ - buf[0] = 0; - return (USB_ERR_INVAL); - } - if (udev->flags.no_strings) { - buf[0] = 0; - return (USB_ERR_STALLED); - } - err = usb2_req_get_string_desc - (udev, mtx, buf, len, udev->langid, string_index); - if (err) { - buf[0] = 0; - return (err); - } - temp = (uint8_t *)buf; - - if (temp[0] < 2) { - /* string length is too short */ - buf[0] = 0; - 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 { - /* silently skip bad character */ - continue; - } - - /* - * Filter by default - we don't allow greater and less than - * signs because they might confuse the dmesg printouts! - */ - if ((*s == '<') || (*s == '>') || (!isprint(*s))) { - /* silently skip bad character */ - continue; - } - s++; - } - *s = 0; /* zero terminate resulting string */ - 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 - * - * NOTE: After this function returns the hardware is in the - * unconfigured state! The application is responsible for setting a - * new configuration. - * - * Returns: - * 0: Success - * Else: Failure - *------------------------------------------------------------------------*/ -usb2_error_t -usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) -{ - struct usb2_device *parent_hub; - usb2_error_t err; - uint8_t old_addr; - uint8_t do_retry = 1; - - if (udev->flags.usb2_mode != USB_MODE_HOST) { - return (USB_ERR_INVAL); - } - old_addr = udev->address; - parent_hub = udev->parent_hub; - if (parent_hub == NULL) { - return (USB_ERR_INVAL); - } -retry: - 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; - - /* reset "bMaxPacketSize" */ - udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; - - /* - * 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! (ignored)\n", - old_addr); - } - /* restore device address */ - udev->address = old_addr; - - /* allow device time to set new address */ - usb2_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); - - /* get the device descriptor */ - err = usb2_req_get_desc(udev, mtx, &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; - } - /* get the full device descriptor */ - err = usb2_req_get_device_desc(udev, mtx, &udev->ddesc); - if (err) { - DPRINTFN(0, "addr=%d, getting device " - "descriptor failed!\n", old_addr); - goto done; - } -done: - if (err && do_retry) { - /* give the USB firmware some time to load */ - usb2_pause_mtx(mtx, hz / 2); - /* no more retries after this retry */ - do_retry = 0; - /* try again */ - goto retry; - } - /* restore address */ - udev->address = old_addr; - return (err); -} - -/*------------------------------------------------------------------------* - * usb2_req_clear_device_feature - * - * Returns: - * 0: Success - * Else: Failure - *------------------------------------------------------------------------*/ -usb2_error_t -usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, - uint16_t sel) -{ - struct usb2_device_request req; - - req.bmRequestType = UT_WRITE_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_device_feature - * - * Returns: - * 0: Success - * Else: Failure - *------------------------------------------------------------------------*/ -usb2_error_t -usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, - uint16_t sel) -{ - struct usb2_device_request req; - - req.bmRequestType = UT_WRITE_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)); -} diff --git a/sys/dev/usb2/core/usb2_request.h b/sys/dev/usb2/core/usb2_request.h deleted file mode 100644 index 8a360b7..0000000 --- a/sys/dev/usb2/core/usb2_request.h +++ /dev/null @@ -1,103 +0,0 @@ -/* $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_ - -struct usb2_process; - -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_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc, - 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); -usb2_error_t usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); -usb2_error_t usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); - -#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 deleted file mode 100644 index 23f89ba..0000000 --- a/sys/dev/usb2/core/usb2_sw_transfer.c +++ /dev/null @@ -1,170 +0,0 @@ -/* $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 - -/*------------------------------------------------------------------------* - * 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; - } - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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); -} diff --git a/sys/dev/usb2/core/usb2_sw_transfer.h b/sys/dev/usb2/core/usb2_sw_transfer.h deleted file mode 100644 index d2da0eb..0000000 --- a/sys/dev/usb2/core/usb2_sw_transfer.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $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 deleted file mode 100644 index 2572b25..0000000 --- a/sys/dev/usb2/core/usb2_transfer.c +++ /dev/null @@ -1,2826 +0,0 @@ -/* $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}}, - [USB_SPEED_SUPER] = {.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}}, - [USB_SPEED_SUPER] = {.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}}, - [USB_SPEED_SUPER] = {.fixed = {1024, 1024, 1024, 1024}}, - }, - - [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}}, - [USB_SPEED_SUPER] = {.range = {0, 1024}}, - }, -}; - -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 *); -static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t); -static void usb2_control_transfer_init(struct usb2_xfer *); -static uint8_t usb2_start_hardware_sub(struct usb2_xfer *); -static void usb2_callback_proc(struct usb2_proc_msg *); -static void usb2_callback_ss_done_defer(struct usb2_xfer *); -static void usb2_callback_wrapper(struct usb2_xfer_queue *); -static void usb2_dma_delay_done_cb(void *); -static void usb2_transfer_start_cb(void *); -static uint8_t usb2_callback_wrapper_sub(struct usb2_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; - } -} - -/*------------------------------------------------------------------------* - * 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 - *------------------------------------------------------------------------*/ -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->xroot->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->xroot->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; - } - switch (parm->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; - break; - default: - frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; - break; - } - - 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; - - switch (parm->speed) { - case USB_SPEED_SUPER: - case USB_SPEED_VARIABLE: - /* 125us -> 1ms */ - if (xfer->interval < 4) - xfer->interval = 1; - else if (xfer->interval > 16) - xfer->interval = (1<<(16-4)); - else - xfer->interval = - (1 << (xfer->interval-4)); - break; - case USB_SPEED_HIGH: - /* 125us -> 1ms */ - xfer->interval /= 8; - break; - default: - break; - } - if (xfer->interval == 0) { - /* - * One millisecond is the smallest - * interval we support: - */ - 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->xroot->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; - } -} - -/*------------------------------------------------------------------------* - * 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 *xfer_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 (xfer_mtx == NULL) { - DPRINTFN(6, "using global lock\n"); - xfer_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->xfer_mtx = xfer_mtx; - - usb2_dma_tag_setup(&info->dma_parent_tag, - parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, - xfer_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max); - - info->bus = udev->bus; - info->udev = udev; - - 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].xroot = info; - info->done_m[1].hdr.pm_callback = &usb2_callback_proc; - info->done_m[1].xroot = info; - - if (xfer_mtx == &Giant) - info->done_p = - &udev->bus->giant_callback_proc; - else - info->done_p = - &udev->bus->non_giant_callback_proc; - } - /* 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->address = udev->address; - xfer->priv_sc = priv_sc; - xfer->xroot = info; - info->setup_refcount++; - - usb2_callout_init_mtx(&xfer->timeout_handle, - &udev->bus->bus_mtx, 0); - } 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 - * the bus mutex before returning ! - */ - USB_BUS_LOCK(info->bus); - - /* 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; - - USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); - - /* wait for any outstanding DMA operations */ - - if (needs_delay) { - temp = usb2_get_dma_delay(info->bus); - usb2_pause_mtx(&info->bus->bus_mtx, - USB_MS_TO_TICKS(temp)); - } - - /* make sure that our done messages are not queued anywhere */ - usb2_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); - - USB_BUS_UNLOCK(info->bus); - - /* 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); -} - -/*------------------------------------------------------------------------* - * 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) { - USB_XFER_LOCK(xfer); - USB_BUS_LOCK(xfer->xroot->bus); - - /* - * 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 "xfer_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; - - USB_BUS_UNLOCK(xfer->xroot->bus); - USB_XFER_UNLOCK(xfer); - - 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->xroot) { - info = xfer->xroot; - - USB_BUS_LOCK(info->bus); - - 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 { - USB_BUS_UNLOCK(info->bus); - } - } - } - } -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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) { - USB_BUS_LOCK(xfer->xroot->bus); - - usb2_dump_pipe(xfer->pipe); - - USB_BUS_UNLOCK(xfer->xroot->bus); - } -#endif - - USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_NOTOWNED); - - /* Only open the USB transfer once! */ - if (!xfer->flags_int.open) { - xfer->flags_int.open = 1; - - DPRINTF("open\n"); - - USB_BUS_LOCK(xfer->xroot->bus); - (xfer->pipe->methods->open) (xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); - } - /* set "transferring" flag */ - xfer->flags_int.transferring = 1; - - /* increment power reference */ - usb2_transfer_power_ref(xfer, 1); - - /* - * Check if the transfer is waiting on a queue, most - * frequently the "done_q": - */ - if (xfer->wait_queue) { - USB_BUS_LOCK(xfer->xroot->bus); - usb2_transfer_dequeue(xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); - } - /* 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); - USB_BUS_LOCK(xfer->xroot->bus); - xfer->flags_int.can_cancel_immed = 1; - /* start the transfer */ - usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); - return; - } - USB_BUS_LOCK(xfer->xroot->bus); - usb2_transfer_done(xfer, USB_ERR_INVAL); - USB_BUS_UNLOCK(xfer->xroot->bus); - 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 */ - USB_BUS_LOCK(xfer->xroot->bus); - usb2_transfer_done(xfer, USB_ERR_INVAL); - USB_BUS_UNLOCK(xfer->xroot->bus); - 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)) { - USB_BUS_LOCK(xfer->xroot->bus); - usb2_transfer_done(xfer, USB_ERR_STALLED); - USB_BUS_UNLOCK(xfer->xroot->bus); - 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->xroot->dma_q, xfer); - return; - } - /* - * Enter the USB transfer into the Host Controller or - * Device Controller schedule: - */ - usb2_pipe_enter(xfer); -} - -/*------------------------------------------------------------------------* - * usb2_pipe_enter - factored out code - *------------------------------------------------------------------------*/ -void -usb2_pipe_enter(struct usb2_xfer *xfer) -{ - struct usb2_pipe *pipe; - - USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - - USB_BUS_LOCK(xfer->xroot->bus); - - 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); - USB_BUS_UNLOCK(xfer->xroot->bus); - return; - } - } else { - xfer->flags_int.can_cancel_immed = 0; - } - - /* start the transfer */ - usb2_command_wrapper(&pipe->pipe_q, xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_XFER_LOCK_ASSERT(xfer, 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; - } - USB_BUS_LOCK(xfer->xroot->bus); - /* call the USB transfer callback */ - usb2_callback_ss_done_defer(xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_XFER_LOCK_ASSERT(xfer, 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 */ - - USB_BUS_LOCK(xfer->xroot->bus); - 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); - } - } - - USB_BUS_UNLOCK(xfer->xroot->bus); -} - -/*------------------------------------------------------------------------* - * 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; - - if (xfer == NULL) { - /* transfer is gone */ - return (0); - } - USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - - if (xfer->flags_int.transferring) { - /* trivial case */ - return (1); - } - USB_BUS_LOCK(xfer->xroot->bus); - if (xfer->wait_queue) { - /* we are waiting on a queue somewhere */ - USB_BUS_UNLOCK(xfer->xroot->bus); - return (1); - } - info = xfer->xroot; - pq = &info->done_q; - - if (pq->curr == xfer) { - /* we are currently scheduled for callback */ - USB_BUS_UNLOCK(xfer->xroot->bus); - return (1); - } - /* we are not pending */ - USB_BUS_UNLOCK(xfer->xroot->bus); - 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->xroot->xfer_mtx != &Giant) { - USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); - } - USB_XFER_LOCK(xfer); - - 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->xroot->cv_drain, xfer->xroot->xfer_mtx); - } - USB_XFER_UNLOCK(xfer); -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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->xroot; - - /* Change locking order */ - USB_BUS_UNLOCK(info->bus); - - /* - * We exploit the fact that the mutex is the same for all - * callbacks that will be called from this thread: - */ - mtx_lock(info->xfer_mtx); - USB_BUS_LOCK(info->bus); - - /* Continue where we lost track */ - usb2_command_wrapper(&info->done_q, - info->done_q.curr); - - mtx_unlock(info->xfer_mtx); -} - -/*------------------------------------------------------------------------* - * 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->xroot; - struct usb2_xfer_queue *pq = &info->done_q; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - 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->xroot; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - if (!mtx_owned(xfer->xroot->xfer_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; - - USB_BUS_UNLOCK(xfer->xroot->bus); - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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 */ - USB_BUS_LOCK(xfer->xroot->bus); - goto done; - } - } else { - - if (usb2_callback_wrapper_sub(xfer)) { - /* the callback has been deferred */ - USB_BUS_LOCK(xfer->xroot->bus); - goto done; - } - /* decrement power reference */ - usb2_transfer_power_ref(xfer, -1); - - 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 */ - USB_BUS_LOCK(xfer->xroot->bus); - - /* - * Check if we got started after that we got cancelled, but - * before we managed to do the callback. - */ - 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; - } - -done: - /* - * Check if we are draining. - */ - 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->xroot->cv_drain); - } - - /* do the next callback, if any */ - usb2_command_wrapper(&info->done_q, - info->done_q.curr); -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - DPRINTFN(3, "Completed %p\n", xfer); - - /* queue callback for execution, again */ - usb2_transfer_done(xfer, 0); -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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); - } -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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->xroot->xfer_mtx)) { - /* - * If the private USB lock is not locked, then we assume - * that the BUS-DMA load stage has been passed: - */ - pq = &xfer->xroot->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->xroot->bus->stats_err.uds_requests - [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; - } else { - xfer->xroot->bus->stats_ok.uds_requests - [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; - } - - /* call the USB transfer callback */ - usb2_callback_ss_done_defer(xfer); -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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; - } -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - - /* avoid any races by locking the USB mutex */ - USB_BUS_LOCK(xfer->xroot->bus); - - xfer->flags.stall_pipe = 1; - - USB_BUS_UNLOCK(xfer->xroot->bus); -} - -/*------------------------------------------------------------------------* - * 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; - } - USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - - /* avoid any races by locking the USB mutex */ - USB_BUS_LOCK(xfer->xroot->bus); - - xfer->flags.stall_pipe = 0; - - USB_BUS_UNLOCK(xfer->xroot->bus); -} - -/*------------------------------------------------------------------------* - * 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; - - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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; - - info = xfer->xroot; - udev = info->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]->xroot; - if (usb2_proc_msignal( - &info->bus->non_giant_callback_proc, - &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; - } -} - -/*------------------------------------------------------------------------* - * 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) -{ - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - - /* defer delay */ - usb2_callout_reset(&xfer->timeout_handle, - USB_MS_TO_TICKS(ms), cb, xfer); -} - -/*------------------------------------------------------------------------* - * 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"); - USB_BUS_LOCK(xfer->xroot->bus); - (xfer->pipe->methods->close) (xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); - /* 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->xroot->bus); - - DPRINTFN(3, "DMA delay, %u ms, " - "on %p\n", temp, xfer); - - if (temp != 0) { - USB_BUS_LOCK(xfer->xroot->bus); - usb2_transfer_timeout_ms(xfer, - &usb2_dma_delay_done_cb, temp); - USB_BUS_UNLOCK(xfer->xroot->bus); - 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: - */ - USB_BUS_LOCK(xfer->xroot->bus); - 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; - } - } - USB_BUS_UNLOCK(xfer->xroot->bus); -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; - } -} - -/*------------------------------------------------------------------------* - * 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) { - USB_XFER_LOCK(xfer); - 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); - } - } - USB_XFER_UNLOCK(xfer); - } 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; - } -} - -/*------------------------------------------------------------------------* - * 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); - - USB_BUS_LOCK(udev->bus); - pipe->toggle_next = 0; - USB_BUS_UNLOCK(udev->bus); -} - -/*------------------------------------------------------------------------* - * 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); - } - USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); - USB_XFER_LOCK_ASSERT(xfer2, 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->xroot->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); - } - } - mtx_unlock(mtx); -} - - -/*------------------------------------------------------------------------* - * 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 *xroot; - 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) { - xroot = xfer->xroot; - udev = xroot->udev; - - /* - * Poll hardware - signal that we are polling by - * locking the private mutex: - */ - USB_XFER_LOCK(xfer); - (udev->bus->methods->do_poll) (udev->bus); - USB_XFER_UNLOCK(xfer); - - /* poll clear stall start */ - USB_BUS_LOCK(xfer->xroot->bus); - pm = &udev->cs_msg[0].hdr; - (pm->pm_callback) (pm); - USB_BUS_UNLOCK(xfer->xroot->bus); - - if (udev->default_xfer[1]) { - - /* poll timeout */ - usb2_callout_poll(udev->default_xfer[1]); - - /* poll clear stall done thread */ - USB_BUS_LOCK(xfer->xroot->bus); - pm = &udev->default_xfer[1]-> - xroot->done_m[0].hdr; - (pm->pm_callback) (pm); - USB_BUS_UNLOCK(xfer->xroot->bus); - } - /* poll timeout */ - usb2_callout_poll(xfer); - - /* poll done thread */ - USB_BUS_LOCK(xfer->xroot->bus); - pm = &xroot->done_m[0].hdr; - (pm->pm_callback) (pm); - USB_BUS_UNLOCK(xfer->xroot->bus); - } - } -} - -#else - -void -usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) -{ - /* polling not supported */ -} - -#endif diff --git a/sys/dev/usb2/core/usb2_transfer.h b/sys/dev/usb2/core/usb2_transfer.h deleted file mode 100644 index 34124c5..0000000 --- a/sys/dev/usb2/core/usb2_transfer.h +++ /dev/null @@ -1,129 +0,0 @@ -/* $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 *xroot; -}; - -/* - * 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; /* pointer to callback process */ - void *memory_base; - struct mtx *xfer_mtx; /* cannot be changed during operation */ - 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; /* pointer to USB bus (cached) */ - struct usb2_device *udev; /* pointer to USB device */ - - 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_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); -uint32_t usb2_get_dma_delay(struct usb2_bus *bus); -void usb2_transfer_power_ref(struct usb2_xfer *xfer, int val); - -#endif /* _USB2_TRANSFER_H_ */ diff --git a/sys/dev/usb2/core/usb2_util.c b/sys/dev/usb2/core/usb2_util.c deleted file mode 100644 index 5dca332..0000000 --- a/sys/dev/usb2/core/usb2_util.c +++ /dev/null @@ -1,346 +0,0 @@ -/* $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 happen 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)); -} - -/*------------------------------------------------------------------------* - * usb2_pause_mtx - factored out code - * - * This function will delay the code by the passed number of system - * ticks. The passed mutex "mtx" will be dropped while waiting, if - * "mtx" is not NULL. - *------------------------------------------------------------------------*/ -void -usb2_pause_mtx(struct mtx *mtx, int _ticks) -{ - if (mtx != NULL) - mtx_unlock(mtx); - - if (cold) { - /* convert to milliseconds */ - _ticks = (_ticks * 1000) / hz; - /* convert to microseconds, rounded up */ - _ticks = (_ticks + 1) * 1000; - DELAY(_ticks); - - } else { - - /* - * Add one to the number of ticks so that we don't return - * too early! - */ - _ticks++; - - if (pause("USBWAIT", _ticks)) { - /* ignore */ - } - } - if (mtx != NULL) - mtx_lock(mtx); -} - -/*------------------------------------------------------------------------* - * 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 */ - } -} - -/*------------------------------------------------------------------------* - * 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 */ -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * usb2_cv_destroy - wrapper function - *------------------------------------------------------------------------*/ -void -usb2_cv_destroy(struct cv *cv) -{ - cv_destroy(cv); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * 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); -} - -/*------------------------------------------------------------------------* - * usb2_cv_broadcast - wrapper function - *------------------------------------------------------------------------*/ -void -usb2_cv_broadcast(struct cv *cv) -{ - wakeup(cv); -} - -/*------------------------------------------------------------------------* - * 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 deleted file mode 100644 index e081c31..0000000 --- a/sys/dev/usb2/core/usb2_util.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $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, int _ticks); -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/ethernet/if_aue2.c b/sys/dev/usb2/ethernet/if_aue2.c deleted file mode 100644 index c280e5e..0000000 --- a/sys/dev/usb2/ethernet/if_aue2.c +++ /dev/null @@ -1,1054 +0,0 @@ -/*- - * Copyright (c) 1997, 1998, 1999, 2000 - * Bill Paul . All rights reserved. - * - * Copyright (c) 2006 - * Alfred Perlstein . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must 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 - * - * SMP locking by Alfred Perlstein . - * RED Inc. - */ - -/* - * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet - * support: the control endpoint for reading/writing registers, burst - * read endpoint for packet reception, burst write for packet transmission - * and one for "interrupts." The chip uses the same RX filter scheme - * as the other ADMtek ethernet parts: one perfect filter entry for the - * the station address and a 64-bit multicast hash table. The chip supports - * both MII and HomePNA attachments. - * - * Since the maximum data transfer speed of USB is supposed to be 12Mbps, - * you're never really going to get 100Mbps speeds from this device. I - * think the idea is to allow the device to connect to 10 or 100Mbps - * networks, not necessarily to provide 100Mbps performance. Also, since - * the controller uses an external PHY chip, it's possible that board - * designers might simply choose a 10Mbps PHY. - * - * Registers are accessed using usb2_ether_do_request(). Packet - * transfers are done using usb2_transfer() and friends. - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR aue_debug - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#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 miibus_readreg_t aue_miibus_readreg; -static miibus_writereg_t aue_miibus_writereg; -static miibus_statchg_t aue_miibus_statchg; - -static usb2_callback_t aue_intr_callback; -static usb2_callback_t aue_bulk_read_callback; -static usb2_callback_t aue_bulk_write_callback; - -static usb2_ether_fn_t aue_attach_post; -static usb2_ether_fn_t aue_init; -static usb2_ether_fn_t aue_stop; -static usb2_ether_fn_t aue_start; -static usb2_ether_fn_t aue_tick; -static usb2_ether_fn_t aue_setmulti; -static usb2_ether_fn_t aue_setpromisc; - -static uint8_t aue_csr_read_1(struct aue_softc *, uint16_t); -static uint16_t aue_csr_read_2(struct aue_softc *, uint16_t); -static void aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t); -static void aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t); -static void aue_eeprom_getword(struct aue_softc *, int, uint16_t *); -static void aue_read_eeprom(struct aue_softc *, uint8_t *, uint16_t, - uint16_t); -static void aue_reset(struct aue_softc *); -static void aue_reset_pegasus_II(struct aue_softc *); - -static int aue_ifmedia_upd(struct ifnet *); -static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *); - -static const struct usb2_config aue_config[AUE_N_TRANSFER] = { - - [AUE_BULK_DT_WR] = { - .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 */ - }, - - [AUE_BULK_DT_RD] = { - .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, - }, - - [AUE_INTR_DT_RD] = { - .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, - }, -}; - -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_miibus_readreg), - DEVMETHOD(miibus_writereg, aue_miibus_writereg), - DEVMETHOD(miibus_statchg, aue_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); -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); - -static const struct usb2_ether_methods aue_ue_methods = { - .ue_attach_post = aue_attach_post, - .ue_start = aue_start, - .ue_init = aue_init, - .ue_stop = aue_stop, - .ue_tick = aue_tick, - .ue_setmulti = aue_setmulti, - .ue_setpromisc = aue_setpromisc, - .ue_mii_upd = aue_ifmedia_upd, - .ue_mii_sts = aue_ifmedia_sts, -}; - -#define AUE_SETBIT(sc, reg, x) \ - aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x)) - -#define AUE_CLRBIT(sc, reg, x) \ - aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x)) - -static uint8_t -aue_csr_read_1(struct aue_softc *sc, uint16_t reg) -{ - struct usb2_device_request req; - usb2_error_t err; - 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); - - err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); - if (err) - return (0); - return (val); -} - -static uint16_t -aue_csr_read_2(struct aue_softc *sc, uint16_t reg) -{ - struct usb2_device_request req; - usb2_error_t err; - 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); - - err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); - if (err) - return (0); - return (le16toh(val)); -} - -static void -aue_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); - - if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { - /* error ignored */ - } -} - -static void -aue_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); - - if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { - /* error ignored */ - } -} - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void -aue_eeprom_getword(struct aue_softc *sc, int addr, uint16_t *dest) -{ - int i; - uint16_t word = 0; - - aue_csr_write_1(sc, AUE_EE_REG, addr); - aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); - - for (i = 0; i != AUE_TIMEOUT; i++) { - if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) - break; - if (usb2_ether_pause(&sc->sc_ue, hz / 100)) - break; - } - - if (i == AUE_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n"); - - word = aue_csr_read_2(sc, AUE_EE_DATA); - *dest = word; -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void -aue_read_eeprom(struct aue_softc *sc, uint8_t *dest, - uint16_t off, uint16_t len) -{ - uint16_t *ptr = (uint16_t *)dest; - int i; - - for (i = 0; i != len; i++, ptr++) - aue_eeprom_getword(sc, off + i, ptr); -} - -static int -aue_miibus_readreg(device_t dev, int phy, int reg) -{ - struct aue_softc *sc = device_get_softc(dev); - int i, locked; - uint16_t val = 0; - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AUE_LOCK(sc); - - /* - * 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) - goto done; -#if 0 - if (phy != 1) - goto done; -#endif - } - aue_csr_write_1(sc, AUE_PHY_ADDR, phy); - aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); - - for (i = 0; i != AUE_TIMEOUT; i++) { - if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) - break; - if (usb2_ether_pause(&sc->sc_ue, hz / 100)) - break; - } - - if (i == AUE_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); - - val = aue_csr_read_2(sc, AUE_PHY_DATA); - -done: - if (!locked) - AUE_UNLOCK(sc); - return (val); -} - -static int -aue_miibus_writereg(device_t dev, int phy, int reg, int data) -{ - struct aue_softc *sc = device_get_softc(dev); - int i; - int locked; - - if (phy == 3) - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AUE_LOCK(sc); - - aue_csr_write_2(sc, AUE_PHY_DATA, data); - aue_csr_write_1(sc, AUE_PHY_ADDR, phy); - aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); - - for (i = 0; i != AUE_TIMEOUT; i++) { - if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) - break; - if (usb2_ether_pause(&sc->sc_ue, hz / 100)) - break; - } - - if (i == AUE_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); - - if (!locked) - AUE_UNLOCK(sc); - return (0); -} - -static void -aue_miibus_statchg(device_t dev) -{ - struct aue_softc *sc = device_get_softc(dev); - struct mii_data *mii = GET_MII(sc); - int locked; - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AUE_LOCK(sc); - - AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); - if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) - AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); - else - AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); - - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) - AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); - else - AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); - - AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); - - /* - * Set the LED modes on the LinkSys adapter. - * This turns on the 'dual link LED' bin in the auxmode - * register of the Broadcom PHY. - */ - if (sc->sc_flags & AUE_FLAG_LSYS) { - uint16_t auxmode; - - auxmode = aue_miibus_readreg(dev, 0, 0x1b); - aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); - } - if (!locked) - AUE_UNLOCK(sc); -} - -#define AUE_BITS 6 -static void -aue_setmulti(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - struct ifmultiaddr *ifma; - uint32_t h = 0; - uint32_t i; - uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); - return; - } - - AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); - - /* now program new ones */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ether_crc32_le(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1); - hashtbl[(h >> 3)] |= 1 << (h & 0x7); - } - IF_ADDR_UNLOCK(ifp); - - /* write the hashtable */ - for (i = 0; i != 8; i++) - aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]); -} - -static void -aue_reset_pegasus_II(struct aue_softc *sc) -{ - /* Magic constants taken from Linux driver. */ - aue_csr_write_1(sc, AUE_REG_1D, 0); - aue_csr_write_1(sc, AUE_REG_7B, 2); -#if 0 - if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) - aue_csr_write_1(sc, AUE_REG_81, 6); - else -#endif - aue_csr_write_1(sc, AUE_REG_81, 2); -} - -static void -aue_reset(struct aue_softc *sc) -{ - int i; - - AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); - - for (i = 0; i != AUE_TIMEOUT; i++) { - if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) - break; - if (usb2_ether_pause(&sc->sc_ue, hz / 100)) - break; - } - - if (i == AUE_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "reset failed\n"); - - /* - * The PHY(s) attached to the Pegasus chip may be held - * in reset until we flip on the GPIO outputs. Make sure - * to set the GPIO pins high so that the PHY(s) will - * be enabled. - * - * Note: We force all of the GPIO pins low first, *then* - * enable the ones we want. - */ - aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0); - aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1); - - if (sc->sc_flags & AUE_FLAG_LSYS) { - /* Grrr. LinkSys has to be different from everyone else. */ - aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1); - aue_csr_write_1(sc, AUE_GPIO0, - AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0); - } - if (sc->sc_flags & AUE_FLAG_PII) - aue_reset_pegasus_II(sc); - - /* Wait a little while for the chip to get its brains in order: */ - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -static void -aue_attach_post(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - - /* reset the adapter */ - aue_reset(sc); - - /* get station address from the EEPROM */ - aue_read_eeprom(sc, ue->ue_eaddr, 0, 3); -} - -/* - * 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); - /* - * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict - * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of - * the devices that look like Bluetooth adapters. - */ - if (uaa->info.idVendor == USB_VENDOR_BELKIN && - uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 && - uaa->info.bcdDevice == 0x0413) - 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); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - sc->sc_flags = USB_GET_DRIVER_INFO(uaa); - - if (uaa->info.bcdDevice >= 0x0201) { - /* XXX currently undocumented */ - sc->sc_flags |= AUE_FLAG_VER_2; - } - - device_set_usb2_desc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = AUE_IFACE_IDX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, aue_config, AUE_N_TRANSFER, - sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &aue_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - return (0); /* success */ - -detach: - aue_detach(dev); - return (ENXIO); /* failure */ -} - -static int -aue_detach(device_t dev) -{ - struct aue_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static void -aue_intr_callback(struct usb2_xfer *xfer) -{ - struct aue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct aue_intrpkt pkt; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - if ((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++; - } - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -aue_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct aue_softc *sc = xfer->priv_sc; - struct usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - struct aue_rxpkt stat; - - 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 <= (sizeof(stat) + ETHER_CRC_LEN)) { - ifp->if_ierrors++; - goto tr_setup; - } - usb2_copy_out(xfer->frbuffers, - xfer->actlen - sizeof(stat), &stat, sizeof(stat)); - - /* - * turn off all the non-error bits in the rx status - * word: - */ - stat.aue_rxstat &= AUE_RXSTAT_MASK; - if (stat.aue_rxstat) { - ifp->if_ierrors++; - goto tr_setup; - } - /* No errors; receive the packet. */ - xfer->actlen -= (sizeof(stat) + ETHER_CRC_LEN); - } - usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen); - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - return; - } -} - -static void -aue_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct aue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - 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++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - if ((sc->sc_flags & AUE_FLAG_LINK) == 0) { - /* - * don't send anything if there is no link ! - */ - return; - } - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) - return; - 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); - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -aue_tick(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - struct mii_data *mii = GET_MII(sc); - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - mii_tick(mii); - if ((sc->sc_flags & AUE_FLAG_LINK) == 0 - && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->sc_flags |= AUE_FLAG_LINK; - aue_start(ue); - } -} - -static void -aue_start(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]); - usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]); -} - -static void -aue_init(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - int i; - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - /* - * Cancel pending I/O - */ - aue_reset(sc); - - /* Set MAC address */ - for (i = 0; i != ETHER_ADDR_LEN; i++) - aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]); - - /* update promiscuous setting */ - aue_setpromisc(ue); - - /* Load the multicast filter. */ - aue_setmulti(ue); - - /* Enable RX and TX */ - aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB); - AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); - AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); - - usb2_transfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - aue_start(ue); -} - -static void -aue_setpromisc(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - /* if we want promiscuous mode, set the allframes bit: */ - if (ifp->if_flags & IFF_PROMISC) - AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); - else - AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); -} - -/* - * Set media options. - */ -static int -aue_ifmedia_upd(struct ifnet *ifp) -{ - struct aue_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - sc->sc_flags &= ~AUE_FLAG_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 (0); -} - -/* - * Report current media status. - */ -static void -aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct aue_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - AUE_LOCK(sc); - mii_pollstat(mii); - AUE_UNLOCK(sc); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void -aue_stop(struct usb2_ether *ue) -{ - struct aue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - AUE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - sc->sc_flags &= ~AUE_FLAG_LINK; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]); - usb2_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]); - - aue_csr_write_1(sc, AUE_CTL0, 0); - aue_csr_write_1(sc, AUE_CTL1, 0); - aue_reset(sc); -} - -/* - * 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); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_auereg.h b/sys/dev/usb2/ethernet/if_auereg.h deleted file mode 100644 index 249c913..0000000 --- a/sys/dev/usb2/ethernet/if_auereg.h +++ /dev/null @@ -1,220 +0,0 @@ -/*- - * 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. - */ -enum { - AUE_BULK_DT_WR, - AUE_BULK_DT_RD, - AUE_INTR_DT_RD, - AUE_N_TRANSFER, -}; - -#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) usb2_ether_getmii(&(sc)->sc_ue) - -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; - uint8_t pad; -} __packed; - -struct aue_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[AUE_N_TRANSFER]; - - int 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_LINK 0x0008 /* wait for link to come up */ -#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */ -#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */ -}; - -#define AUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define AUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/if_axe2.c b/sys/dev/usb2/ethernet/if_axe2.c deleted file mode 100644 index b578be9..0000000 --- a/sys/dev/usb2/ethernet/if_axe2.c +++ /dev/null @@ -1,1076 +0,0 @@ -/*- - * 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 - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR axe_debug - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * AXE_178_MAX_FRAME_BURST - * max frame burst size for Ax88178 and Ax88772 - * 0 2048 bytes - * 1 4096 bytes - * 2 8192 bytes - * 3 16384 bytes - * use the largest your system can handle without USB stalling. - * - * NB: 88772 parts appear to generate lots of input errors with - * a 2K rx buffer and 8K is only slightly faster than 4K on an - * EHCI port on a T42 so change at your own risk. - */ -#define AXE_178_MAX_FRAME_BURST 1 - -#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_callback; -static usb2_callback_t axe_bulk_read_callback; -static usb2_callback_t axe_bulk_write_callback; - -static miibus_readreg_t axe_miibus_readreg; -static miibus_writereg_t axe_miibus_writereg; -static miibus_statchg_t axe_miibus_statchg; - -static usb2_ether_fn_t axe_attach_post; -static usb2_ether_fn_t axe_init; -static usb2_ether_fn_t axe_stop; -static usb2_ether_fn_t axe_start; -static usb2_ether_fn_t axe_tick; -static usb2_ether_fn_t axe_setmulti; -static usb2_ether_fn_t axe_setpromisc; - -static int axe_ifmedia_upd(struct ifnet *); -static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); -static int axe_cmd(struct axe_softc *, int, int, int, void *); -static void axe_ax88178_init(struct axe_softc *); -static void axe_ax88772_init(struct axe_softc *); -static int axe_get_phyno(struct axe_softc *, int); - -static const struct usb2_config axe_config[AXE_N_TRANSFER] = { - - [AXE_BULK_DT_WR] = { - .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 */ - }, - - [AXE_BULK_DT_RD] = { - .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 */ - }, - - [AXE_INTR_DT_RD] = { - .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, - }, -}; - -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_miibus_readreg), - DEVMETHOD(miibus_writereg, axe_miibus_writereg), - DEVMETHOD(miibus_statchg, axe_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); -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); - -static const struct usb2_ether_methods axe_ue_methods = { - .ue_attach_post = axe_attach_post, - .ue_start = axe_start, - .ue_init = axe_init, - .ue_stop = axe_stop, - .ue_tick = axe_tick, - .ue_setmulti = axe_setmulti, - .ue_setpromisc = axe_setpromisc, - .ue_mii_upd = axe_ifmedia_upd, - .ue_mii_sts = axe_ifmedia_sts, -}; - -static int -axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) -{ - struct usb2_device_request req; - usb2_error_t err; - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - 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, AXE_CMD_LEN(cmd)); - - err = usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000); - - return (err); -} - -static int -axe_miibus_readreg(device_t dev, int phy, int reg) -{ - struct axe_softc *sc = device_get_softc(dev); - uint16_t val; - int locked; - - if (sc->sc_phyno != phy) - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AXE_LOCK(sc); - - axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); - axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); - axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); - - val = le16toh(val); - if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) { - /* - * BMSR of AX88772 indicates that it supports extended - * capability but the extended status register is - * revered for embedded ethernet PHY. So clear the - * extended capability bit of BMSR. - */ - val &= ~BMSR_EXTCAP; - } - - if (!locked) - AXE_UNLOCK(sc); - return (val); -} - -static int -axe_miibus_writereg(device_t dev, int phy, int reg, int val) -{ - struct axe_softc *sc = device_get_softc(dev); - int locked; - - val = htole16(val); - - if (sc->sc_phyno != phy) - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AXE_LOCK(sc); - - axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); - axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); - axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); - - if (!locked) - AXE_UNLOCK(sc); - return (0); -} - -static void -axe_miibus_statchg(device_t dev) -{ - struct axe_softc *sc = device_get_softc(dev); - struct mii_data *mii = GET_MII(sc); - struct ifnet *ifp; - uint16_t val; - int err, locked; - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - AXE_LOCK(sc); - - ifp = usb2_ether_getifp(&sc->sc_ue); - if (mii == NULL || ifp == NULL || - (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - goto done; - - sc->sc_flags &= ~AXE_FLAG_LINK; - if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID)) { - switch (IFM_SUBTYPE(mii->mii_media_active)) { - case IFM_10_T: - case IFM_100_TX: - sc->sc_flags |= AXE_FLAG_LINK; - break; - case IFM_1000_T: - if ((sc->sc_flags & AXE_FLAG_178) == 0) - break; - sc->sc_flags |= AXE_FLAG_LINK; - break; - default: - break; - } - } - - /* Lost link, do nothing. */ - if ((sc->sc_flags & AXE_FLAG_LINK) == 0) - goto done; - - val = 0; - if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) - val |= AXE_MEDIA_FULL_DUPLEX; - if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { - val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; - if ((sc->sc_flags & AXE_FLAG_178) != 0) - val |= AXE_178_MEDIA_ENCK; - switch (IFM_SUBTYPE(mii->mii_media_active)) { - case IFM_1000_T: - val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; - break; - case IFM_100_TX: - val |= AXE_178_MEDIA_100TX; - break; - case IFM_10_T: - /* doesn't need to be handled */ - break; - } - } - err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); - if (err) - device_printf(dev, "media change failed, error %d\n", err); -done: - if (!locked) - AXE_UNLOCK(sc); -} - -/* - * Set media options. - */ -static int -axe_ifmedia_upd(struct ifnet *ifp) -{ - struct axe_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - int error; - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - if (mii->mii_instance) { - struct mii_softc *miisc; - - LIST_FOREACH(miisc, &mii->mii_phys, mii_list) - mii_phy_reset(miisc); - } - error = mii_mediachg(mii); - return (error); -} - -/* - * Report current media status. - */ -static void -axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct axe_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - AXE_LOCK(sc); - mii_pollstat(mii); - AXE_UNLOCK(sc); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; -} - -static void -axe_setmulti(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - struct ifmultiaddr *ifma; - uint32_t h = 0; - uint16_t rxmode; - uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); - rxmode = le16toh(rxmode); - - if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { - rxmode |= AXE_RXCMD_ALLMULTI; - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); - return; - } - rxmode &= ~AXE_RXCMD_ALLMULTI; - - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) - { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ether_crc32_be(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; - hashtbl[h / 8] |= 1 << (h % 8); - } - IF_ADDR_UNLOCK(ifp); - - axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); -} - -static int -axe_get_phyno(struct axe_softc *sc, int sel) -{ - int phyno; - - switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) { - case PHY_TYPE_100_HOME: - case PHY_TYPE_GIG: - phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]); - break; - case PHY_TYPE_SPECIAL: - /* FALLTHROUGH */ - case PHY_TYPE_RSVD: - /* FALLTHROUGH */ - case PHY_TYPE_NON_SUP: - /* FALLTHROUGH */ - default: - phyno = -1; - break; - } - - return (phyno); -} - -static void -axe_ax88178_init(struct axe_softc *sc) -{ - int gpio0 = 0, phymode = 0; - uint16_t eeprom; - - axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); - /* XXX magic */ - axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); - eeprom = le16toh(eeprom); - axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); - - /* if EEPROM is invalid we have to use to GPIO0 */ - if (eeprom == 0xffff) { - phymode = 0; - gpio0 = 1; - } else { - phymode = eeprom & 7; - gpio0 = (eeprom & 0x80) ? 0 : 1; - } - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 16); - - if ((eeprom >> 8) != 0x01) { - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 32); - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 3); - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 32); - } else { - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 32); - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 32); - } - - /* soft reset */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 4); - - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, - AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 4); - /* Enable MII/GMII/RGMII interface to work with external PHY. */ - axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 4); - - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); -} - -static void -axe_ax88772_init(struct axe_softc *sc) -{ - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 16); - - if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { - /* ask for the embedded PHY */ - axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 64); - - /* power down and reset state, pin reset state */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, - AXE_SW_RESET_CLEAR, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 16); - - /* power down/reset state, pin operating state */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, - AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 4); - - /* power up, reset */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); - - /* power up, operating */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, - AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); - } else { - /* ask for external PHY */ - axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); - usb2_ether_pause(&sc->sc_ue, hz / 64); - - /* power down internal PHY */ - axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, - AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); - } - - usb2_ether_pause(&sc->sc_ue, hz / 4); - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); -} - -static void -axe_reset(struct axe_softc *sc) -{ - struct usb2_config_descriptor *cd; - usb2_error_t err; - - cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev); - - err = usb2_req_set_config(sc->sc_ue.ue_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. */ - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -static void -axe_attach_post(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - - /* - * Load PHY indexes first. Needed by axe_xxx_init(). - */ - axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); -#if 1 - device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n", - sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); -#endif - sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); - if (sc->sc_phyno == -1) - sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); - if (sc->sc_phyno == -1) { - device_printf(sc->sc_ue.ue_dev, - "no valid PHY address found, assuming PHY address 0\n"); - sc->sc_phyno = 0; - } - - if (sc->sc_flags & AXE_FLAG_178) - axe_ax88178_init(sc); - else if (sc->sc_flags & AXE_FLAG_772) - axe_ax88772_init(sc); - - /* - * Get station address. - */ - if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) - axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); - else - axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); - - /* - * Fetch IPG values. - */ - axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); -} - -/* - * 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); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - sc->sc_flags = USB_GET_DRIVER_INFO(uaa); - - device_set_usb2_desc(dev); - - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = AXE_IFACE_IDX; - error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, - axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &axe_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - return (0); /* success */ - -detach: - axe_detach(dev); - return (ENXIO); /* failure */ -} - -static int -axe_detach(device_t dev) -{ - struct axe_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static void -axe_intr_callback(struct usb2_xfer *xfer) -{ - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - case USB_ST_SETUP: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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 usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - struct axe_sframe_hdr hdr; - int error, pos, len, 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; - } - error = usb2_ether_rxbuf(ue, xfer->frbuffers, pos, len); - if (error) - break; - - 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++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - 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 = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - int pos; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete\n"); - ifp->if_opackets++; - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { - /* - * don't send anything if there is no link ! - */ - return; - } - pos = 0; - - while (1) { - - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) { - if (pos > 0) - break; /* send out data */ - return; - } - 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); - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - - } -} - -static void -axe_tick(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - struct mii_data *mii = GET_MII(sc); - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - mii_tick(mii); - if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { - axe_miibus_statchg(ue->ue_dev); - if ((sc->sc_flags & AXE_FLAG_LINK) != 0) - axe_start(ue); - } -} - -static void -axe_start(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[AXE_INTR_DT_RD]); - usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]); -} - -static void -axe_init(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - uint16_t rxmode; - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - /* Cancel pending I/O */ - axe_stop(ue); - -#ifdef notdef - /* Set MAC address */ - axe_mac(sc, IF_LLADDR(ifp), 1); -#endif - - /* Set transmitter IPG values */ - if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { - axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], - (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); - } else { - axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); - axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); - axe_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 (ifp->if_flags & IFF_PROMISC) - rxmode |= AXE_RXCMD_PROMISC; - - if (ifp->if_flags & IFF_BROADCAST) - rxmode |= AXE_RXCMD_BROADCAST; - - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); - - /* Load the multicast filter. */ - axe_setmulti(ue); - - usb2_transfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - axe_start(ue); -} - -static void -axe_setpromisc(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - uint16_t rxmode; - - axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); - - rxmode = le16toh(rxmode); - - if (ifp->if_flags & IFF_PROMISC) { - rxmode |= AXE_RXCMD_PROMISC; - } else { - rxmode &= ~AXE_RXCMD_PROMISC; - } - - axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); - - axe_setmulti(ue); -} - -static void -axe_stop(struct usb2_ether *ue) -{ - struct axe_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - AXE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - sc->sc_flags &= ~AXE_FLAG_LINK; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]); - usb2_transfer_stop(sc->sc_xfer[AXE_INTR_DT_RD]); - - axe_reset(sc); -} - -/* - * 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); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_axereg.h b/sys/dev/usb2/ethernet/if_axereg.h deleted file mode 100644 index dc063e3..0000000 --- a/sys/dev/usb2/ethernet/if_axereg.h +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * 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_PHY_SEL_PRI 1 -#define AXE_PHY_SEL_SEC 0 -#define AXE_PHY_TYPE_MASK 0xE0 -#define AXE_PHY_TYPE_SHIFT 5 -#define AXE_PHY_TYPE(x) \ - (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT) - -#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */ -#define PHY_TYPE_GIG 1 /* Gigabit PHY */ -#define PHY_TYPE_SPECIAL 4 /* Special case */ -#define PHY_TYPE_RSVD 5 /* Reserved */ -#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */ - -#define AXE_PHY_NO_MASK 0x1F -#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK) - -#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ - -#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 - -struct axe_sframe_hdr { - uint16_t len; - uint16_t ilen; -} __packed; - -#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue) - -/* The interrupt endpoint is currently unused by the ASIX part. */ -enum { - AXE_BULK_DT_WR, - AXE_BULK_DT_RD, - AXE_INTR_DT_RD, - AXE_N_TRANSFER, -}; - -struct axe_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[AXE_N_TRANSFER]; - int sc_phyno; - - int sc_flags; -#define AXE_FLAG_LINK 0x0001 -#define AXE_FLAG_772 0x1000 /* AX88772 */ -#define AXE_FLAG_178 0x2000 /* AX88178 */ - - uint8_t sc_ipgs[3]; - uint8_t sc_phyaddrs[2]; -}; - -#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/if_cdce2.c b/sys/dev/usb2/ethernet/if_cdce2.c deleted file mode 100644 index 78b54cf..0000000 --- a/sys/dev/usb2/ethernet/if_cdce2.c +++ /dev/null @@ -1,771 +0,0 @@ -/* $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 - * Copyright (c) 2009 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. - * 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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR cdce_debug - -#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 usb2_ether_fn_t cdce_attach_post; -static usb2_ether_fn_t cdce_init; -static usb2_ether_fn_t cdce_stop; -static usb2_ether_fn_t cdce_start; -static usb2_ether_fn_t cdce_setmulti; -static usb2_ether_fn_t cdce_setpromisc; - -static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t); - -#if USB_DEBUG -static int cdce_debug = 0; - -SYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet"); -SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0, - "Debug level"); -#endif - -static const struct usb2_config cdce_config[CDCE_N_TRANSFER] = { - - [CDCE_BULK_A] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .if_index = 0, - /* Host Mode */ - .mh.frames = CDCE_FRAMES_MAX, - .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), - .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_FRAMES_MAX, - .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), - .md.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, - .md.callback = cdce_bulk_read_callback, - .md.timeout = 0, /* no timeout */ - }, - - [CDCE_BULK_B] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 0, - /* Host Mode */ - .mh.frames = CDCE_FRAMES_MAX, - .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), - .mh.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, - .mh.callback = cdce_bulk_read_callback, - .mh.timeout = 0, /* no timeout */ - /* Device Mode */ - .md.frames = CDCE_FRAMES_MAX, - .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES), - .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, - .md.callback = cdce_bulk_write_callback, - .md.timeout = 10000, /* 10 seconds */ - }, - - [CDCE_INTR] = { - .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_ether_methods cdce_ue_methods = { - .ue_attach_post = cdce_attach_post, - .ue_start = cdce_start, - .ue_init = cdce_init, - .ue_stop = cdce_stop, - .ue_setmulti = cdce_setmulti, - .ue_setpromisc = cdce_setpromisc, -}; - -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 void -cdce_attach_post(struct usb2_ether *ue) -{ - /* no-op */ - return; -} - -static int -cdce_attach(device_t dev) -{ - struct cdce_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct usb2_interface *iface; - const struct usb2_cdc_union_descriptor *ud; - const struct usb2_interface_descriptor *id; - const struct usb2_cdc_ethernet_descriptor *ued; - int error; - uint8_t i; - char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ - - sc->sc_flags = USB_GET_DRIVER_INFO(uaa); - - device_set_usb2_desc(dev); - - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - 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; - } - } - - ued = usb2_find_descriptor - (uaa->device, NULL, uaa->info.bIfaceIndex, - UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1); - - if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { - error = USB_ERR_INVAL; - } else { - error = usb2_req_get_string_any(uaa->device, NULL, - eaddr_str, sizeof(eaddr_str), ued->iMacAddress); - } - - if (error) { - - /* fake MAC address */ - - device_printf(dev, "faking MAC address\n"); - sc->sc_ue.ue_eaddr[0] = 0x2a; - memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t)); - sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); - - } else { - - bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr)); - - for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { - - char c = eaddr_str[i]; - - if ('0' <= c && c <= '9') - c -= '0'; - else if (c != 0) - c -= 'A' - 10; - else - break; - - c &= 0xf; - - if ((i & 1) == 0) - c <<= 4; - sc->sc_ue.ue_eaddr[i / 2] |= c; - } - - if (uaa->usb2_mode == USB_MODE_DEVICE) { - /* - * Do not use the same MAC address like the peer ! - */ - sc->sc_ue.ue_eaddr[5] ^= 0xFF; - } - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &cdce_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - 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 usb2_ether *ue = &sc->sc_ue; - - /* stop all USB transfers first */ - usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static void -cdce_start(struct usb2_ether *ue) -{ - struct cdce_softc *sc = usb2_ether_getsc(ue); - - /* - * Start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[CDCE_BULK_B]); - usb2_transfer_start(sc->sc_xfer[CDCE_BULK_A]); -} - -static void -cdce_free_queue(struct mbuf **ppm, uint8_t n) -{ - uint8_t x; - for (x = 0; x != n; x++) { - if (ppm[x] != NULL) { - m_freem(ppm[x]); - ppm[x] = NULL; - } - } -} - -static void -cdce_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct cdce_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - struct mbuf *mt; - uint32_t crc; - uint8_t x; - - 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 TX buffers */ - cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - for (x = 0; x != CDCE_FRAMES_MAX; x++) { - - 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++; - continue; - } - } - if (m->m_len != m->m_pkthdr.len) { - mt = m_defrag(m, M_DONTWAIT); - if (mt == NULL) { - m_freem(m); - ifp->if_oerrors++; - continue; - } - m = mt; - } - if (m->m_pkthdr.len > MCLBYTES) { - m->m_pkthdr.len = MCLBYTES; - } - sc->sc_tx_buf[x] = m; - xfer->frlengths[x] = m->m_len; - usb2_set_frame_data(xfer, m->m_data, x); - - /* - * If there's a BPF listener, bounce a copy of - * this frame to him: - */ - BPF_MTAP(ifp, m); - } - if (x != 0) { - xfer->nframes = x; - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - /* free all previous TX buffers */ - cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); - - /* count output errors */ - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } -} - -static int32_t -cdce_m_crc32_cb(void *arg, void *src, uint32_t count) -{ - 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) -{ - uint32_t crc = 0xFFFFFFFF; - int error; - - error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc); - return (crc ^ 0xFFFFFFFF); -} - -static void -cdce_init(struct usb2_ether *ue) -{ - struct cdce_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - CDCE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - - /* start interrupt transfer */ - usb2_transfer_start(sc->sc_xfer[CDCE_INTR]); - - /* stall data write direction, which depends on USB mode */ - if (usb2_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) - usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_A]); - else - usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_B]); - - /* start data transfers */ - cdce_start(ue); -} - -static void -cdce_stop(struct usb2_ether *ue) -{ - struct cdce_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - CDCE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_A]); - usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_B]); - usb2_transfer_stop(sc->sc_xfer[CDCE_INTR]); -} - -static void -cdce_setmulti(struct usb2_ether *ue) -{ - /* no-op */ - return; -} - -static void -cdce_setpromisc(struct usb2_ether *ue) -{ - /* no-op */ - return; -} - -static int -cdce_shutdown(device_t dev) -{ - struct cdce_softc *sc = device_get_softc(dev); - - usb2_ether_ifshutdown(&sc->sc_ue); - - 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 void -cdce_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct cdce_softc *sc = xfer->priv_sc; - struct mbuf *m; - uint8_t x; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - DPRINTF("received %u bytes in %u frames\n", - xfer->actlen, xfer->aframes); - - for (x = 0; x != xfer->aframes; x++) { - - m = sc->sc_rx_buf[x]; - sc->sc_rx_buf[x] = NULL; - - /* Strip off CRC added by Zaurus, if any */ - if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && - (xfer->frlengths[x] >= 14)) - xfer->frlengths[x] -= 4; - - if (xfer->frlengths[x] < sizeof(struct ether_header)) { - m_freem(m); - continue; - } - /* queue up mbuf */ - usb2_ether_rxmbuf(&sc->sc_ue, m, xfer->frlengths[x]); - } - - /* FALLTHROUGH */ - case USB_ST_SETUP: - /* - * TODO: Implement support for multi frame transfers, - * when the USB hardware supports it. - */ - for (x = 0; x != 1; x++) { - if (sc->sc_rx_buf[x] == NULL) { - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) - goto tr_stall; - sc->sc_rx_buf[x] = m; - /* adjust for ethernet */ - m->m_len = m->m_pkthdr.len = MCLBYTES; - m_adj(m, ETHER_ALIGN); - } else { - m = sc->sc_rx_buf[x]; - } - - usb2_set_frame_data(xfer, m->m_data, x); - xfer->frlengths[x] = m->m_len; - } - /* set number of frames and start hardware */ - xfer->nframes = x; - usb2_start_hardware(xfer); - /* flush any received frames */ - usb2_ether_rxflush(&sc->sc_ue); - break; - - default: /* Error */ - DPRINTF("error = %s\n", - usb2_errstr(xfer->error)); - - if (xfer->error != USB_ERR_CANCELLED) { -tr_stall: - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - xfer->nframes = 0; - usb2_start_hardware(xfer); - break; - } - - /* need to free the RX-mbufs when we are cancelled */ - cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX); - break; - } -} - -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 */ - - /* FALLTHROUGH */ - 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; - } -} - -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); - - /* FALLTHROUGH */ - 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; - } -} - -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_cdcereg.h b/sys/dev/usb2/ethernet/if_cdcereg.h deleted file mode 100644 index dac5121..0000000 --- a/sys/dev/usb2/ethernet/if_cdcereg.h +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * 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_FRAMES_MAX 8 /* units */ -#define CDCE_IND_SIZE_MAX 32 /* bytes */ - -enum { - CDCE_BULK_A, - CDCE_BULK_B, - CDCE_INTR, - CDCE_N_TRANSFER, -}; - -struct cdce_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[CDCE_N_TRANSFER]; - struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX]; - struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX]; - - int sc_flags; -#define CDCE_FLAG_ZAURUS 0x0001 -#define CDCE_FLAG_NO_UNION 0x0002 -#define CDCE_FLAG_RX_DATA 0x0010 - - uint8_t sc_eaddr_str_index; - uint8_t sc_data_iface_no; - uint8_t sc_ifaces_index[2]; -}; - -#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define CDCE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) -#endif /* _USB_IF_CDCEREG_H_ */ diff --git a/sys/dev/usb2/ethernet/if_cue2.c b/sys/dev/usb2/ethernet/if_cue2.c deleted file mode 100644 index 630a210..0000000 --- a/sys/dev/usb2/ethernet/if_cue2.c +++ /dev/null @@ -1,645 +0,0 @@ -/*- - * 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. - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR cue_debug - -#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_callback; -static usb2_callback_t cue_bulk_write_callback; - -static usb2_ether_fn_t cue_attach_post; -static usb2_ether_fn_t cue_init; -static usb2_ether_fn_t cue_stop; -static usb2_ether_fn_t cue_start; -static usb2_ether_fn_t cue_tick; -static usb2_ether_fn_t cue_setmulti; -static usb2_ether_fn_t cue_setpromisc; - -static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t); -static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t); -static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t); -static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int); -static int cue_getmac(struct cue_softc *, void *); -static uint32_t cue_mchash(const uint8_t *); -static void cue_reset(struct cue_softc *); - -#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_N_TRANSFER] = { - - [CUE_BULK_DT_WR] = { - .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 */ - }, - - [CUE_BULK_DT_RD] = { - .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, - }, -}; - -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 const struct usb2_ether_methods cue_ue_methods = { - .ue_attach_post = cue_attach_post, - .ue_start = cue_start, - .ue_init = cue_init, - .ue_stop = cue_stop, - .ue_tick = cue_tick, - .ue_setmulti = cue_setmulti, - .ue_setpromisc = cue_setpromisc, -}; - -#define CUE_SETBIT(sc, reg, x) \ - cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) - -#define CUE_CLRBIT(sc, reg, x) \ - cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) - -static uint8_t -cue_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); - - if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) { - /* ignore any errors */ - } - return (val); -} - -static uint16_t -cue_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); - - (void)usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000); - return (le16toh(val)); -} - -static int -cue_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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); -} - -static int -cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static int -cue_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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -#define CUE_BITS 9 - -static uint32_t -cue_mchash(const uint8_t *addr) -{ - uint32_t crc; - - /* Compute CRC for the address value. */ - crc = ether_crc32_le(addr, ETHER_ADDR_LEN); - - return (crc & ((1 << CUE_BITS) - 1)); -} - -static void -cue_setpromisc(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - CUE_LOCK_ASSERT(sc, MA_OWNED); - - /* if we want promiscuous mode, set the allframes bit */ - if (ifp->if_flags & IFF_PROMISC) - CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); - else - CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); - - /* write multicast hash-bits */ - cue_setmulti(ue); -} - -static void -cue_setmulti(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - struct ifmultiaddr *ifma; - uint32_t h = 0, i; - uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - CUE_LOCK_ASSERT(sc, MA_OWNED); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - for (i = 0; i < 8; i++) - hashtbl[i] = 0xff; - cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, - &hashtbl, 8); - return; - } - - /* now program new ones */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) - { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - hashtbl[h >> 3] |= 1 << (h & 0x7); - } - IF_ADDR_UNLOCK(ifp); - - /* - * Also include the broadcast address in the filter - * so we can receive broadcast frames. - */ - if (ifp->if_flags & IFF_BROADCAST) { - h = cue_mchash(ifp->if_broadcastaddr); - hashtbl[h >> 3] |= 1 << (h & 0x7); - } - - cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8); -} - -static void -cue_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); - - if (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)) { - /* ignore any errors */ - } - - /* - * wait a little while for the chip to get its brains in order: - */ - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -static void -cue_attach_post(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - - cue_getmac(sc, ue->ue_eaddr); -} - -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)); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int -cue_attach(device_t dev) -{ - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct cue_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - device_set_usb2_desc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = CUE_IFACE_IDX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &cue_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - return (0); /* success */ - -detach: - cue_detach(dev); - return (ENXIO); /* failure */ -} - -static int -cue_detach(device_t dev) -{ - struct cue_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static void -cue_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct cue_softc *sc = xfer->priv_sc; - struct usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - uint8_t buf[2]; - int 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); - xfer->actlen -= 2; - len = buf[0] | (buf[1] << 8); - len = min(xfer->actlen, len); - - usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len); - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - return; - - } -} - -static void -cue_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct cue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - uint8_t buf[2]; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete\n"); - ifp->if_opackets++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) - return; - 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); - - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -cue_tick(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - CUE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL); - ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL); - ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL); - - if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) - ifp->if_ierrors++; -} - -static void -cue_start(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]); -} - -static void -cue_init(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - int i; - - CUE_LOCK_ASSERT(sc, MA_OWNED); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - cue_stop(ue); -#if 0 - cue_reset(sc); -#endif - /* Set MAC address */ - for (i = 0; i < ETHER_ADDR_LEN; i++) - cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]); - - /* Enable RX logic. */ - cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); - - /* Load the multicast filter */ - cue_setpromisc(ue); - - /* - * Set the number of RX and TX buffers that we want - * to reserve inside the ASIC. - */ - cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); - cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); - - /* Set advanced operation modes. */ - cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, - CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ - - /* Program the LED operation. */ - cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); - - usb2_transfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - cue_start(ue); -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void -cue_stop(struct usb2_ether *ue) -{ - struct cue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - CUE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]); - - cue_csr_write_1(sc, CUE_ETHCTL, 0); - cue_reset(sc); -} - -/* - * 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); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_cuereg.h b/sys/dev/usb2/ethernet/if_cuereg.h deleted file mode 100644 index ca3a816..0000000 --- a/sys/dev/usb2/ethernet/if_cuereg.h +++ /dev/null @@ -1,132 +0,0 @@ -/*- - * 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_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. */ -enum { - CUE_BULK_DT_WR, - CUE_BULK_DT_RD, - CUE_N_TRANSFER, -}; - -struct cue_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[CUE_N_TRANSFER]; - - int sc_flags; -#define CUE_FLAG_LINK 0x0001 /* got a link */ -}; - -#define CUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define CUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/if_kue2.c b/sys/dev/usb2/ethernet/if_kue2.c deleted file mode 100644 index b99f363..0000000 --- a/sys/dev/usb2/ethernet/if_kue2.c +++ /dev/null @@ -1,704 +0,0 @@ -/*- - * 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. - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR kue_debug - -#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_callback; -static usb2_callback_t kue_bulk_write_callback; - -static usb2_ether_fn_t kue_attach_post; -static usb2_ether_fn_t kue_init; -static usb2_ether_fn_t kue_stop; -static usb2_ether_fn_t kue_start; -static usb2_ether_fn_t kue_setmulti; -static usb2_ether_fn_t kue_setpromisc; - -static int kue_do_request(struct kue_softc *, - struct usb2_device_request *, void *); -static int kue_setword(struct kue_softc *, uint8_t, uint16_t); -static int kue_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t, - void *, int); -static int kue_load_fw(struct kue_softc *); -static void kue_reset(struct kue_softc *); - -#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_N_TRANSFER] = { - - [KUE_BULK_DT_WR] = { - .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 */ - }, - - [KUE_BULK_DT_RD] = { - .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 */ - }, -}; - -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); - -static const struct usb2_ether_methods kue_ue_methods = { - .ue_attach_post = kue_attach_post, - .ue_start = kue_start, - .ue_init = kue_init, - .ue_stop = kue_stop, - .ue_setmulti = kue_setmulti, - .ue_setpromisc = kue_setpromisc, -}; - -/* - * 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 int -kue_do_request(struct kue_softc *sc, struct usb2_device_request *req, - void *data) -{ - usb2_error_t err; - - err = usb2_ether_do_request(&sc->sc_ue, req, data, 60000); - - return (err); -} - -static int -kue_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); - - return (kue_do_request(sc, &req, NULL)); -} - -static int -kue_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, - uint16_t val, void *data, int 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); - - return (kue_do_request(sc, &req, data)); -} - -static int -kue_load_fw(struct kue_softc *sc) -{ - struct usb2_device_descriptor *dd; - uint16_t hwrev; - usb2_error_t err; - - dd = usb2_get_device_descriptor(sc->sc_ue.ue_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(0); - - /* Load code segment */ - err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, - 0, kue_code_seg, sizeof(kue_code_seg)); - if (err) { - device_printf(sc->sc_ue.ue_dev, "failed to load code segment: %s\n", - usb2_errstr(err)); - return(ENXIO); - } - - /* Load fixup segment */ - err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, - 0, kue_fix_seg, sizeof(kue_fix_seg)); - if (err) { - device_printf(sc->sc_ue.ue_dev, "failed to load fixup segment: %s\n", - usb2_errstr(err)); - return(ENXIO); - } - - /* Send trigger command. */ - err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, - 0, kue_trig_seg, sizeof(kue_trig_seg)); - if (err) { - device_printf(sc->sc_ue.ue_dev, "failed to load trigger segment: %s\n", - usb2_errstr(err)); - return(ENXIO); - } - - return (0); -} - -static void -kue_setpromisc(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - KUE_LOCK_ASSERT(sc, MA_OWNED); - - if (ifp->if_flags & IFF_PROMISC) - sc->sc_rxfilt |= KUE_RXFILT_PROMISC; - else - sc->sc_rxfilt &= ~KUE_RXFILT_PROMISC; - - kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); -} - -static void -kue_setmulti(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - struct ifmultiaddr *ifma; - int i = 0; - - KUE_LOCK_ASSERT(sc, MA_OWNED); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; - sc->sc_rxfilt &= ~KUE_RXFILT_MULTICAST; - kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); - return; - } - - sc->sc_rxfilt &= ~KUE_RXFILT_ALLMULTI; - - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) - { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - /* - * If there are too many addresses for the - * internal filter, switch over to allmulti mode. - */ - if (i == KUE_MCFILTCNT(sc)) - break; - bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), - KUE_MCFILT(sc, i), ETHER_ADDR_LEN); - i++; - } - IF_ADDR_UNLOCK(ifp); - - if (i == KUE_MCFILTCNT(sc)) - sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; - else { - sc->sc_rxfilt |= KUE_RXFILT_MULTICAST; - kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, - i, sc->sc_mcfilters, i * ETHER_ADDR_LEN); - } - - kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); -} - -/* - * Issue a SET_CONFIGURATION command to reset the MAC. This should be - * done after the firmware is loaded into the adapter in order to - * bring it into proper operation. - */ -static void -kue_reset(struct kue_softc *sc) -{ - struct usb2_config_descriptor *cd; - usb2_error_t err; - - cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev); - - err = usb2_req_set_config(sc->sc_ue.ue_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 */ - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -static void -kue_attach_post(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - int error; - - /* load the firmware into the NIC */ - error = kue_load_fw(sc); - if (error) { - device_printf(sc->sc_ue.ue_dev, "could not load firmware\n"); - /* ignore the error */ - } - - /* reset the adapter */ - kue_reset(sc); - - /* read ethernet descriptor */ - kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, - 0, &sc->sc_desc, sizeof(sc->sc_desc)); - - /* copy in ethernet address */ - memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr)); -} - -/* - * 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); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - device_set_usb2_desc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = KUE_IFACE_IDX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, kue_config, KUE_N_TRANSFER, sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - sc->sc_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, - M_USBDEV, M_WAITOK); - if (sc->sc_mcfilters == NULL) { - device_printf(dev, "failed allocating USB memory!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &kue_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - return (0); /* success */ - -detach: - kue_detach(dev); - return (ENXIO); /* failure */ -} - -static int -kue_detach(device_t dev) -{ - struct kue_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - free(sc->sc_mcfilters, M_USBDEV); - - return (0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void -kue_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct kue_softc *sc = xfer->priv_sc; - struct usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - uint8_t buf[2]; - int 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); - xfer->actlen -= 2; - len = buf[0] | (buf[1] << 8); - len = min(xfer->actlen, len); - - usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len); - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - return; - - } -} - -static void -kue_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct kue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - int total_len; - int temp_len; - uint8_t buf[2]; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete\n"); - ifp->if_opackets++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) - return; - 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); - - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - - } -} - -static void -kue_start(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]); -} - -static void -kue_init(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - KUE_LOCK_ASSERT(sc, MA_OWNED); - - /* set MAC address */ - kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, - 0, IF_LLADDR(ifp), 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_setword(sc, KUE_CMD_SET_SOFS, 1); -#endif - kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64); - - /* load the multicast filter */ - kue_setpromisc(ue); - - usb2_transfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - kue_start(ue); -} - -static void -kue_stop(struct usb2_ether *ue) -{ - struct kue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - KUE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]); -} - -/* - * 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); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_kuefw.h b/sys/dev/usb2/ethernet/if_kuefw.h deleted file mode 100644 index 2b055a9..0000000 --- a/sys/dev/usb2/ethernet/if_kuefw.h +++ /dev/null @@ -1,685 +0,0 @@ -/*- - * 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_kuereg.h b/sys/dev/usb2/ethernet/if_kuereg.h deleted file mode 100644 index 8650687..0000000 --- a/sys/dev/usb2/ethernet/if_kuereg.h +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * 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(x, y) \ - (char *)&(sc->sc_mcfilters[y * ETHER_ADDR_LEN]) - -#define KUE_STAT_TX_OK 0x00000001 -#define KUE_STAT_RX_OK 0x00000002 -#define KUE_STAT_TX_ERR 0x00000004 -#define KUE_STAT_RX_ERR 0x00000008 -#define KUE_STAT_RX_NOBUF 0x00000010 -#define KUE_STAT_TX_UCAST_BYTES 0x00000020 -#define KUE_STAT_TX_UCAST_FRAMES 0x00000040 -#define KUE_STAT_TX_MCAST_BYTES 0x00000080 -#define KUE_STAT_TX_MCAST_FRAMES 0x00000100 -#define KUE_STAT_TX_BCAST_BYTES 0x00000200 -#define KUE_STAT_TX_BCAST_FRAMES 0x00000400 -#define KUE_STAT_RX_UCAST_BYTES 0x00000800 -#define KUE_STAT_RX_UCAST_FRAMES 0x00001000 -#define KUE_STAT_RX_MCAST_BYTES 0x00002000 -#define KUE_STAT_RX_MCAST_FRAMES 0x00004000 -#define KUE_STAT_RX_BCAST_BYTES 0x00008000 -#define KUE_STAT_RX_BCAST_FRAMES 0x00010000 -#define KUE_STAT_RX_CRCERR 0x00020000 -#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 -#define KUE_STAT_RX_ALIGNERR 0x00080000 -#define KUE_STAT_TX_SINGLECOLL 0x00100000 -#define KUE_STAT_TX_MULTICOLL 0x00200000 -#define KUE_STAT_TX_DEFERRED 0x00400000 -#define KUE_STAT_TX_MAXCOLLS 0x00800000 -#define KUE_STAT_RX_OVERRUN 0x01000000 -#define KUE_STAT_TX_UNDERRUN 0x02000000 -#define KUE_STAT_TX_SQE_ERR 0x04000000 -#define KUE_STAT_TX_CARRLOSS 0x08000000 -#define KUE_STAT_RX_LATECOLL 0x10000000 - -#define KUE_RXFILT_PROMISC 0x0001 -#define KUE_RXFILT_ALLMULTI 0x0002 -#define KUE_RXFILT_UNICAST 0x0004 -#define KUE_RXFILT_BROADCAST 0x0008 -#define KUE_RXFILT_MULTICAST 0x0010 - -#define KUE_TIMEOUT 1000 -#define KUE_MIN_FRAMELEN 60 - -#define KUE_CTL_READ 0x01 -#define KUE_CTL_WRITE 0x02 - -#define KUE_CONFIG_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 -enum { - KUE_BULK_DT_WR, - KUE_BULK_DT_RD, - KUE_N_TRANSFER, -}; - -struct kue_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct kue_ether_desc sc_desc; - struct usb2_xfer *sc_xfer[KUE_N_TRANSFER]; - uint8_t *sc_mcfilters; - - int sc_flags; -#define KUE_FLAG_LINK 0x0001 - - uint16_t sc_rxfilt; -}; - -#define KUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define KUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/if_rue2.c b/sys/dev/usb2/ethernet/if_rue2.c deleted file mode 100644 index 690092f..0000000 --- a/sys/dev/usb2/ethernet/if_rue2.c +++ /dev/null @@ -1,913 +0,0 @@ -/*- - * 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/. - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR rue_debug - -#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 miibus_readreg_t rue_miibus_readreg; -static miibus_writereg_t rue_miibus_writereg; -static miibus_statchg_t rue_miibus_statchg; - -static usb2_callback_t rue_intr_callback; -static usb2_callback_t rue_bulk_read_callback; -static usb2_callback_t rue_bulk_write_callback; - -static usb2_ether_fn_t rue_attach_post; -static usb2_ether_fn_t rue_init; -static usb2_ether_fn_t rue_stop; -static usb2_ether_fn_t rue_start; -static usb2_ether_fn_t rue_tick; -static usb2_ether_fn_t rue_setmulti; -static usb2_ether_fn_t rue_setpromisc; - -static int rue_read_mem(struct rue_softc *, uint16_t, void *, int); -static int rue_write_mem(struct rue_softc *, uint16_t, void *, int); -static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t); -static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t); -static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t); -static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t); -static int rue_csr_write_4(struct rue_softc *, int, uint32_t); - -static void rue_reset(struct rue_softc *); -static int rue_ifmedia_upd(struct ifnet *); -static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *); - -static const struct usb2_config rue_config[RUE_N_TRANSFER] = { - - [RUE_BULK_DT_WR] = { - .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 */ - }, - - [RUE_BULK_DT_RD] = { - .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 */ - }, - - [RUE_INTR_DT_RD] = { - .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, - }, -}; - -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_miibus_readreg), - DEVMETHOD(miibus_writereg, rue_miibus_writereg), - DEVMETHOD(miibus_statchg, rue_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 const struct usb2_ether_methods rue_ue_methods = { - .ue_attach_post = rue_attach_post, - .ue_start = rue_start, - .ue_init = rue_init, - .ue_stop = rue_stop, - .ue_tick = rue_tick, - .ue_setmulti = rue_setmulti, - .ue_setpromisc = rue_setpromisc, - .ue_mii_upd = rue_ifmedia_upd, - .ue_mii_sts = rue_ifmedia_sts, -}; - -#define RUE_SETBIT(sc, reg, x) \ - rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x)) - -#define RUE_CLRBIT(sc, reg, x) \ - rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x)) - -static int -rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static int -rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static uint8_t -rue_csr_read_1(struct rue_softc *sc, uint16_t reg) -{ - uint8_t val; - - rue_read_mem(sc, reg, &val, 1); - return (val); -} - -static uint16_t -rue_csr_read_2(struct rue_softc *sc, uint16_t reg) -{ - uint8_t val[2]; - - rue_read_mem(sc, reg, &val, 2); - return (UGETW(val)); -} - -static int -rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) -{ - return (rue_write_mem(sc, reg, &val, 1)); -} - -static int -rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) -{ - uint8_t temp[2]; - - USETW(temp, val); - return (rue_write_mem(sc, reg, &temp, 2)); -} - -static int -rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) -{ - uint8_t temp[4]; - - USETDW(temp, val); - return (rue_write_mem(sc, reg, &temp, 4)); -} - -static int -rue_miibus_readreg(device_t dev, int phy, int reg) -{ - struct rue_softc *sc = device_get_softc(dev); - uint16_t rval; - uint16_t ruereg; - int locked; - - if (phy != 0) /* RTL8150 supports PHY == 0, only */ - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - RUE_LOCK(sc); - - 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_csr_read_1(sc, reg); - goto done; - } - device_printf(sc->sc_ue.ue_dev, "bad phy register\n"); - rval = 0; - goto done; - } - - rval = rue_csr_read_2(sc, ruereg); -done: - if (!locked) - RUE_UNLOCK(sc); - return (rval); -} - -static int -rue_miibus_writereg(device_t dev, int phy, int reg, int data) -{ - struct rue_softc *sc = device_get_softc(dev); - uint16_t ruereg; - int locked; - - if (phy != 0) /* RTL8150 supports PHY == 0, only */ - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - RUE_LOCK(sc); - - 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_csr_write_1(sc, reg, data); - goto done; - } - device_printf(sc->sc_ue.ue_dev, " bad phy register\n"); - goto done; - } - rue_csr_write_2(sc, ruereg, data); -done: - if (!locked) - RUE_UNLOCK(sc); - return (0); -} - -static void -rue_miibus_statchg(device_t dev) -{ - /* - * When the code below is enabled the card starts doing weird - * things after link going from UP to DOWN and back UP. - * - * Looks like some of register writes below messes up PHY - * interface. - * - * No visible regressions were found after commenting this code - * out, so that disable it for good. - */ -#if 0 - struct rue_softc *sc = device_get_softc(dev); - struct mii_data *mii = GET_MII(sc); - uint16_t bmcr; - int locked; - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - RUE_LOCK(sc); - - RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); - - bmcr = rue_csr_read_2(sc, RUE_BMCR); - - if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) - bmcr |= RUE_BMCR_SPD_SET; - else - bmcr &= ~RUE_BMCR_SPD_SET; - - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) - bmcr |= RUE_BMCR_DUPLEX; - else - bmcr &= ~RUE_BMCR_DUPLEX; - - rue_csr_write_2(sc, RUE_BMCR, bmcr); - - RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); - - if (!locked) - RUE_UNLOCK(sc); -#endif -} - -static void -rue_setpromisc(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) - RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP); - else - RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP); -} - -/* - * Program the 64-bit multicast hash filter. - */ -static void -rue_setmulti(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - uint16_t rxcfg; - int h = 0; - uint32_t hashes[2] = { 0, 0 }; - struct ifmultiaddr *ifma; - int mcnt = 0; - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - rxcfg = rue_csr_read_2(sc, RUE_RCR); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); - rxcfg &= ~RUE_RCR_AM; - rue_csr_write_2(sc, RUE_RCR, rxcfg); - rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); - rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); - return; - } - - /* first, zot all the existing hash bits */ - rue_csr_write_4(sc, RUE_MAR0, 0); - rue_csr_write_4(sc, RUE_MAR4, 0); - - /* now program new ones */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) - { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ether_crc32_be(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; - if (h < 32) - hashes[0] |= (1 << h); - else - hashes[1] |= (1 << (h - 32)); - mcnt++; - } - IF_ADDR_UNLOCK(ifp); - - if (mcnt) - rxcfg |= RUE_RCR_AM; - else - rxcfg &= ~RUE_RCR_AM; - - rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); - - rue_csr_write_2(sc, RUE_RCR, rxcfg); - rue_csr_write_4(sc, RUE_MAR0, hashes[0]); - rue_csr_write_4(sc, RUE_MAR4, hashes[1]); -} - -static void -rue_reset(struct rue_softc *sc) -{ - int i; - - rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); - - for (i = 0; i != RUE_TIMEOUT; i++) { - if (usb2_ether_pause(&sc->sc_ue, hz / 1000)) - break; - if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) - break; - } - if (i == RUE_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "reset never completed!\n"); - - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -static void -rue_attach_post(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - - /* reset the adapter */ - rue_reset(sc); - - /* get station address from the EEPROM */ - rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN); -} - -/* - * 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); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - device_set_usb2_desc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = RUE_IFACE_IDX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, rue_config, RUE_N_TRANSFER, - sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &rue_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - return (0); /* success */ - -detach: - rue_detach(dev); - return (ENXIO); /* failure */ -} - -static int -rue_detach(device_t dev) -{ - struct rue_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static void -rue_intr_callback(struct usb2_xfer *xfer) -{ - struct rue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - 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; - } - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -rue_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct rue_softc *sc = xfer->priv_sc; - struct usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - uint16_t status; - - 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)); - xfer->actlen -= 4; - - /* check recieve packet was valid or not */ - status = le16toh(status); - if ((status & RUE_RXSTAT_VALID) == 0) { - ifp->if_ierrors++; - goto tr_setup; - } - usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen); - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - return; - } -} - -static void -rue_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct rue_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - int temp_len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete\n"); - ifp->if_opackets++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - if ((sc->sc_flags & RUE_FLAG_LINK) == 0) { - /* - * don't send anything if there is no link ! - */ - return; - } - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) - return; - 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); - - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -rue_tick(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - struct mii_data *mii = GET_MII(sc); - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - mii_tick(mii); - if ((sc->sc_flags & RUE_FLAG_LINK) == 0 - && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->sc_flags |= RUE_FLAG_LINK; - rue_start(ue); - } -} - -static void -rue_start(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]); - usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]); -} - -static void -rue_init(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - /* - * Cancel pending I/O - */ - rue_reset(sc); - - /* Set MAC address */ - rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN); - - rue_stop(ue); - - /* - * Set the initial TX and RX configuration. - */ - rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); - rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB); - - /* Load the multicast filter */ - rue_setpromisc(ue); - /* Load the multicast filter. */ - rue_setmulti(ue); - - /* Enable RX and TX */ - rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); - - usb2_transfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - rue_start(ue); -} - -/* - * Set media options. - */ -static int -rue_ifmedia_upd(struct ifnet *ifp) -{ - struct rue_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - sc->sc_flags &= ~RUE_FLAG_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 (0); -} - -/* - * Report current media status. - */ -static void -rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct rue_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - RUE_LOCK(sc); - mii_pollstat(mii); - RUE_UNLOCK(sc); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; -} - -static void -rue_stop(struct usb2_ether *ue) -{ - struct rue_softc *sc = usb2_ether_getsc(ue); - struct ifnet *ifp = usb2_ether_getifp(ue); - - RUE_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - sc->sc_flags &= ~RUE_FLAG_LINK; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]); - usb2_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]); - - rue_csr_write_1(sc, RUE_CR, 0x00); - - rue_reset(sc); -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static int -rue_shutdown(device_t dev) -{ - struct rue_softc *sc = device_get_softc(dev); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_ruereg.h b/sys/dev/usb2/ethernet/if_ruereg.h deleted file mode 100644 index a94d45a..0000000 --- a/sys/dev/usb2/ethernet/if_ruereg.h +++ /dev/null @@ -1,183 +0,0 @@ -/*- - * 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_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) usb2_ether_getmii(&(sc)->sc_ue) - -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; -}; - -enum { - RUE_BULK_DT_WR, - RUE_BULK_DT_RD, - RUE_INTR_DT_RD, - RUE_N_TRANSFER, -}; - -struct rue_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[RUE_N_TRANSFER]; - - int sc_flags; -#define RUE_FLAG_LINK 0x0001 -}; - -#define RUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define RUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/if_udav2.c b/sys/dev/usb2/ethernet/if_udav2.c deleted file mode 100644 index 3bec08a..0000000 --- a/sys/dev/usb2/ethernet/if_udav2.c +++ /dev/null @@ -1,856 +0,0 @@ -/* $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 - */ - -/* - * TODO: - * Interrupt Endpoint support - * External PHYs - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR udav_debug - -#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_callback; -static usb2_callback_t udav_bulk_read_callback; -static usb2_callback_t udav_intr_callback; - -static usb2_ether_fn_t udav_attach_post; -static usb2_ether_fn_t udav_init; -static usb2_ether_fn_t udav_stop; -static usb2_ether_fn_t udav_start; -static usb2_ether_fn_t udav_tick; -static usb2_ether_fn_t udav_setmulti; -static usb2_ether_fn_t udav_setpromisc; - -static int udav_csr_read(struct udav_softc *, uint16_t, void *, int); -static int udav_csr_write(struct udav_softc *, uint16_t, void *, int); -static uint8_t udav_csr_read1(struct udav_softc *, uint16_t); -static int udav_csr_write1(struct udav_softc *, uint16_t, uint8_t); -static void udav_reset(struct udav_softc *); -static int udav_ifmedia_upd(struct ifnet *); -static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *); - -static miibus_readreg_t udav_miibus_readreg; -static miibus_writereg_t udav_miibus_writereg; -static miibus_statchg_t udav_miibus_statchg; - -static const struct usb2_config udav_config[UDAV_N_TRANSFER] = { - - [UDAV_BULK_DT_WR] = { - .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 */ - }, - - [UDAV_BULK_DT_RD] = { - .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 */ - }, - - [UDAV_INTR_DT_RD] = { - .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, - }, -}; - -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_miibus_readreg), - DEVMETHOD(miibus_writereg, udav_miibus_writereg), - DEVMETHOD(miibus_statchg, udav_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); - -static const struct usb2_ether_methods udav_ue_methods = { - .ue_attach_post = udav_attach_post, - .ue_start = udav_start, - .ue_init = udav_init, - .ue_stop = udav_stop, - .ue_tick = udav_tick, - .ue_setmulti = udav_setmulti, - .ue_setpromisc = udav_setpromisc, - .ue_mii_upd = udav_ifmedia_upd, - .ue_mii_sts = udav_ifmedia_status, -}; - -#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_SETBIT(sc, reg, x) \ - udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x)) - -#define UDAV_CLRBIT(sc, reg, x) \ - udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x)) - -static const struct 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 void -udav_attach_post(struct usb2_ether *ue) -{ - struct udav_softc *sc = usb2_ether_getsc(ue); - - /* reset the adapter */ - udav_reset(sc); - - /* Get Ethernet Address */ - udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN); -} - -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); - struct usb2_ether *ue = &sc->sc_ue; - uint8_t iface_index; - int error; - - sc->sc_flags = USB_GET_DRIVER_INFO(uaa); - - device_set_usb2_desc(dev); - - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - - iface_index = UDAV_IFACE_INDEX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "allocating USB transfers failed!\n"); - goto detach; - } - - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &udav_ue_methods; - - error = usb2_ether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); - goto detach; - } - - return (0); /* success */ - -detach: - udav_detach(dev); - return (ENXIO); /* failure */ -} - -static int -udav_detach(device_t dev) -{ - struct udav_softc *sc = device_get_softc(dev); - struct usb2_ether *ue = &sc->sc_ue; - - usb2_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER); - usb2_ether_ifdetach(ue); - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -#if 0 -static int -udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf, - int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static int -udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf, - int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static int -udav_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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); -} -#endif - -static int -udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static int -udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int 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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000)); -} - -static uint8_t -udav_csr_read1(struct udav_softc *sc, uint16_t offset) -{ - uint8_t val; - - udav_csr_read(sc, offset, &val, 1); - return (val); -} - -static int -udav_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); - - return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)); -} - -static void -udav_init(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - - UDAV_LOCK_ASSERT(sc, MA_OWNED); - - /* - * Cancel pending I/O - */ - udav_stop(ue); - - /* set MAC address */ - udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN); - - /* initialize network control register */ - - /* disable loopback */ - UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); - - /* Initialize RX control register */ - UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); - - /* load multicast filter and update promiscious mode bit */ - udav_setpromisc(ue); - - /* enable RX */ - UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); - - /* clear POWER_DOWN state of internal PHY */ - UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); - UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); - - usb2_transfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - udav_start(ue); -} - -static void -udav_reset(struct udav_softc *sc) -{ - int i; - - /* Select PHY */ -#if 1 - /* - * XXX: force select internal phy. - * external phy routines are not tested. - */ - UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); -#else - if (sc->sc_flags & UDAV_EXT_PHY) - UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); - else - UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); -#endif - - UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); - - for (i = 0; i < UDAV_TX_TIMEOUT; i++) { - if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) - break; - if (usb2_ether_pause(&sc->sc_ue, hz / 100)) - break; - } - - usb2_ether_pause(&sc->sc_ue, hz / 100); -} - -#define UDAV_BITS 6 -static void -udav_setmulti(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct ifmultiaddr *ifma; - uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int h = 0; - - UDAV_LOCK_ASSERT(sc, MA_OWNED); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); - return; - } - - /* first, zot all the existing hash bits */ - memset(hashtbl, 0x00, sizeof(hashtbl)); - hashtbl[7] |= 0x80; /* broadcast address */ - udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); - - /* now program new ones */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) - { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ether_crc32_be(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; - hashtbl[h / 8] |= 1 << (h % 8); - } - IF_ADDR_UNLOCK(ifp); - - /* disable all multicast */ - UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL); - - /* write hash value to the register */ - udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); -} - -static void -udav_setpromisc(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - uint8_t rxmode; - - rxmode = udav_csr_read1(sc, UDAV_RCR); - rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC); - - if (ifp->if_flags & IFF_PROMISC) - rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC; - else if (ifp->if_flags & IFF_ALLMULTI) - rxmode |= UDAV_RCR_ALL; - - /* write new mode bits */ - udav_csr_write1(sc, UDAV_RCR, rxmode); -} - -static void -udav_start(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - - /* - * start the USB transfers, if not already started: - */ - usb2_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]); - usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]); - usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]); -} - -static void -udav_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct udav_softc *sc = xfer->priv_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - struct mbuf *m; - int extra_len; - int temp_len; - uint8_t buf[2]; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete\n"); - ifp->if_opackets++; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) { - /* - * don't send anything if there is no link ! - */ - return; - } - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - - if (m == NULL) - return; - 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); - return; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -udav_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct udav_softc *sc = xfer->priv_sc; - struct usb2_ether *ue = &sc->sc_ue; - struct ifnet *ifp = usb2_ether_getifp(ue); - struct udav_rxpkt stat; - int len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - if (xfer->actlen < sizeof(stat) + ETHER_CRC_LEN) { - ifp->if_ierrors++; - goto tr_setup; - } - usb2_copy_out(xfer->frbuffers, 0, &stat, sizeof(stat)); - xfer->actlen -= sizeof(stat); - len = min(xfer->actlen, le16toh(stat.pktlen)); - len -= ETHER_CRC_LEN; - - if (stat.rxstat & UDAV_RSR_LCS) { - ifp->if_collisions++; - goto tr_setup; - } - if (stat.rxstat & UDAV_RSR_ERR) { - ifp->if_ierrors++; - goto tr_setup; - } - usb2_ether_rxbuf(ue, xfer->frbuffers, sizeof(stat), len); - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - usb2_ether_rxflush(ue); - return; - - default: /* Error */ - DPRINTF("bulk read 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; - } - return; - } -} - -static void -udav_intr_callback(struct usb2_xfer *xfer) -{ - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - case USB_ST_SETUP: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -udav_stop(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue); - - UDAV_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - sc->sc_flags &= ~UDAV_FLAG_LINK; - - /* - * stop all the transfers, if not already stopped: - */ - usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]); - usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]); - usb2_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]); - - udav_reset(sc); -} - -static int -udav_ifmedia_upd(struct ifnet *ifp) -{ - struct udav_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - UDAV_LOCK_ASSERT(sc, MA_OWNED); - - sc->sc_flags &= ~UDAV_FLAG_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 (0); -} - -static void -udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct udav_softc *sc = ifp->if_softc; - struct mii_data *mii = GET_MII(sc); - - UDAV_LOCK(sc); - mii_pollstat(mii); - UDAV_UNLOCK(sc); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; -} - -static void -udav_tick(struct usb2_ether *ue) -{ - struct udav_softc *sc = ue->ue_sc; - struct mii_data *mii = GET_MII(sc); - - UDAV_LOCK_ASSERT(sc, MA_OWNED); - - mii_tick(mii); - if ((sc->sc_flags & UDAV_FLAG_LINK) == 0 - && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->sc_flags |= UDAV_FLAG_LINK; - udav_start(ue); - } -} - -static int -udav_miibus_readreg(device_t dev, int phy, int reg) -{ - struct udav_softc *sc = device_get_softc(dev); - uint16_t data16; - uint8_t val[2]; - int locked; - - /* XXX: one PHY only for the internal PHY */ - if (phy != 0) - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - UDAV_LOCK(sc); - - /* select internal PHY and set PHY register address */ - udav_csr_write1(sc, UDAV_EPAR, - UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); - - /* select PHY operation and start read command */ - udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); - - /* XXX: should we wait? */ - - /* end read command */ - UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); - - /* retrieve the result from data registers */ - udav_csr_read(sc, UDAV_EPDRL, val, 2); - - data16 = (val[0] | (val[1] << 8)); - - DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n", - phy, reg, data16); - - if (!locked) - UDAV_UNLOCK(sc); - return (data16); -} - -static int -udav_miibus_writereg(device_t dev, int phy, int reg, int data) -{ - struct udav_softc *sc = device_get_softc(dev); - uint8_t val[2]; - int locked; - - /* XXX: one PHY only for the internal PHY */ - if (phy != 0) - return (0); - - locked = mtx_owned(&sc->sc_mtx); - if (!locked) - UDAV_LOCK(sc); - - /* select internal PHY and set PHY register address */ - udav_csr_write1(sc, UDAV_EPAR, - UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); - - /* put the value to the data registers */ - val[0] = (data & 0xff); - val[1] = (data >> 8) & 0xff; - udav_csr_write(sc, UDAV_EPDRL, val, 2); - - /* select PHY operation and start write command */ - udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); - - /* XXX: should we wait? */ - - /* end write command */ - UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); - - if (!locked) - UDAV_UNLOCK(sc); - return (0); -} - -static void -udav_miibus_statchg(device_t dev) -{ - /* nothing to do */ -} - -/* - * 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); - - usb2_ether_ifshutdown(&sc->sc_ue); - - return (0); -} diff --git a/sys/dev/usb2/ethernet/if_udavreg.h b/sys/dev/usb2/ethernet/if_udavreg.h deleted file mode 100644 index d652f5b..0000000 --- a/sys/dev/usb2/ethernet/if_udavreg.h +++ /dev/null @@ -1,166 +0,0 @@ -/* $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_TX_TIMEOUT 1000 -#define UDAV_TIMEOUT 10000 - -#define UDAV_TX_TIMEOUT 1000 -#define UDAV_TIMEOUT 10000 - -/* 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) usb2_ether_getmii(&(sc)->sc_ue) - -struct udav_rxpkt { - uint8_t rxstat; - uint16_t pktlen; -} __packed; - -enum { - UDAV_BULK_DT_WR, - UDAV_BULK_DT_RD, - UDAV_INTR_DT_RD, - UDAV_N_TRANSFER, -}; - -struct udav_softc { - struct usb2_ether sc_ue; - struct mtx sc_mtx; - struct usb2_xfer *sc_xfer[UDAV_N_TRANSFER]; - - int sc_flags; -#define UDAV_FLAG_LINK 0x0001 -#define UDAV_FLAG_EXT_PHY 0x0040 -}; - -#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define UDAV_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/sys/dev/usb2/ethernet/usb2_ethernet.c b/sys/dev/usb2/ethernet/usb2_ethernet.c deleted file mode 100644 index a01f942..0000000 --- a/sys/dev/usb2/ethernet/usb2_ethernet.c +++ /dev/null @@ -1,587 +0,0 @@ -/* $FreeBSD$ */ -/*- - * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.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. - * - * 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 - -SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, "USB Ethernet parameters"); - -#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx) -#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx) -#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t) - -MODULE_DEPEND(usb2_ethernet, usb2_core, 1, 1, 1); -MODULE_DEPEND(usb2_ethernet, miibus, 1, 1, 1); - -static struct unrhdr *ueunit; - -static usb2_proc_callback_t ue_attach_post_task; -static usb2_proc_callback_t ue_promisc_task; -static usb2_proc_callback_t ue_setmulti_task; -static usb2_proc_callback_t ue_ifmedia_task; -static usb2_proc_callback_t ue_tick_task; -static usb2_proc_callback_t ue_start_task; -static usb2_proc_callback_t ue_stop_task; - -static void ue_init(void *); -static void ue_start(struct ifnet *); -static int ue_ifmedia_upd(struct ifnet *); -static void ue_watchdog(void *); - -/* - * Return values: - * 0: success - * Else: device has been detached - */ -uint8_t -usb2_ether_pause(struct usb2_ether *ue, unsigned int _ticks) -{ - if (usb2_proc_is_gone(&ue->ue_tq)) { - /* nothing to do */ - return (1); - } - usb2_pause_mtx(ue->ue_mtx, _ticks); - return (0); -} - -static void -ue_queue_command(struct usb2_ether *ue, - usb2_proc_callback_t *fn, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) -{ - struct usb2_ether_cfg_task *task; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - if (usb2_proc_is_gone(&ue->ue_tq)) { - return; /* nothing to do */ - } - /* - * NOTE: The task cannot get executed before we drop the - * "sc_mtx" mutex. It is safe to update fields in the message - * structure after that the message got queued. - */ - task = (struct usb2_ether_cfg_task *) - usb2_proc_msignal(&ue->ue_tq, t0, t1); - - /* Setup callback and self pointers */ - task->hdr.pm_callback = fn; - task->ue = ue; - - /* - * Start and stop must be synchronous! - */ - if ((fn == ue_start_task) || (fn == ue_stop_task)) - usb2_proc_mwait(&ue->ue_tq, t0, t1); -} - -struct ifnet * -usb2_ether_getifp(struct usb2_ether *ue) -{ - return (ue->ue_ifp); -} - -struct mii_data * -usb2_ether_getmii(struct usb2_ether *ue) -{ - return (device_get_softc(ue->ue_miibus)); -} - -void * -usb2_ether_getsc(struct usb2_ether *ue) -{ - return (ue->ue_sc); -} - -static int -ue_sysctl_parent(SYSCTL_HANDLER_ARGS) -{ - struct usb2_ether *ue = arg1; - const char *name; - - name = device_get_nameunit(ue->ue_dev); - return SYSCTL_OUT(req, name, strlen(name)); -} - -int -usb2_ether_ifattach(struct usb2_ether *ue) -{ - int error; - - /* check some critical parameters */ - if ((ue->ue_dev == NULL) || - (ue->ue_udev == NULL) || - (ue->ue_mtx == NULL) || - (ue->ue_methods == NULL)) - return (EINVAL); - - error = usb2_proc_create(&ue->ue_tq, ue->ue_mtx, - device_get_nameunit(ue->ue_dev), USB_PRI_MED); - if (error) { - device_printf(ue->ue_dev, "could not setup taskqueue\n"); - goto error; - } - - /* fork rest of the attach code */ - UE_LOCK(ue); - ue_queue_command(ue, ue_attach_post_task, - &ue->ue_sync_task[0].hdr, - &ue->ue_sync_task[1].hdr); - UE_UNLOCK(ue); - -error: - return (error); -} - -static void -ue_attach_post_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - struct ifnet *ifp; - int error; - char num[14]; /* sufficient for 32 bits */ - - /* first call driver's post attach routine */ - ue->ue_methods->ue_attach_post(ue); - - UE_UNLOCK(ue); - - ue->ue_unit = alloc_unr(ueunit); - usb2_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0); - sysctl_ctx_init(&ue->ue_sysctl_ctx); - - ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(ue->ue_dev, "could not allocate ifnet\n"); - goto error; - } - - ifp->if_softc = ue; - if_initname(ifp, "ue", ue->ue_unit); - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - if (ue->ue_methods->ue_ioctl != NULL) - ifp->if_ioctl = ue->ue_methods->ue_ioctl; - else - ifp->if_ioctl = usb2_ether_ioctl; - ifp->if_start = ue_start; - ifp->if_init = ue_init; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; - IFQ_SET_READY(&ifp->if_snd); - ue->ue_ifp = ifp; - - if (ue->ue_methods->ue_mii_upd != NULL && - ue->ue_methods->ue_mii_sts != NULL) { - mtx_lock(&Giant); /* device_xxx() depends on this */ - error = mii_phy_probe(ue->ue_dev, &ue->ue_miibus, - ue_ifmedia_upd, ue->ue_methods->ue_mii_sts); - mtx_unlock(&Giant); - if (error) { - device_printf(ue->ue_dev, "MII without any PHY\n"); - goto error; - } - } - - if_printf(ifp, " on %s\n", device_get_nameunit(ue->ue_dev)); - ether_ifattach(ifp, ue->ue_eaddr); - - snprintf(num, sizeof(num), "%u", ue->ue_unit); - ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx, - &SYSCTL_NODE_CHILDREN(_net, ue), - OID_AUTO, num, CTLFLAG_RD, NULL, ""); - SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx, - SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, - "%parent", CTLFLAG_RD, ue, 0, - ue_sysctl_parent, "A", "parent device"); - - UE_LOCK(ue); - return; - -error: - free_unr(ueunit, ue->ue_unit); - if (ue->ue_ifp != NULL) { - if_free(ue->ue_ifp); - ue->ue_ifp = NULL; - } - UE_LOCK(ue); - return; -} - -void -usb2_ether_ifdetach(struct usb2_ether *ue) -{ - struct ifnet *ifp; - - /* wait for any post attach or other command to complete */ - usb2_proc_drain(&ue->ue_tq); - - /* read "ifnet" pointer after taskqueue drain */ - ifp = ue->ue_ifp; - - if (ifp != NULL) { - - /* we are not running any more */ - UE_LOCK(ue); - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - UE_UNLOCK(ue); - - /* drain any callouts */ - usb2_callout_drain(&ue->ue_watchdog); - - /* detach miibus */ - if (ue->ue_miibus != NULL) { - mtx_lock(&Giant); /* device_xxx() depends on this */ - device_delete_child(ue->ue_dev, ue->ue_miibus); - mtx_unlock(&Giant); - } - - /* detach ethernet */ - ether_ifdetach(ifp); - - /* free interface instance */ - if_free(ifp); - - /* free sysctl */ - sysctl_ctx_free(&ue->ue_sysctl_ctx); - - /* free unit */ - free_unr(ueunit, ue->ue_unit); - } - - /* free taskqueue, if any */ - usb2_proc_free(&ue->ue_tq); -} - -void -usb2_ether_ifshutdown(struct usb2_ether *ue) -{ - struct ifnet *ifp = ue->ue_ifp; - - UE_LOCK(ue); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ue_queue_command(ue, ue_stop_task, - &ue->ue_sync_task[0].hdr, - &ue->ue_sync_task[1].hdr); - UE_UNLOCK(ue); -} - -uint8_t -usb2_ether_is_gone(struct usb2_ether *ue) -{ - return (usb2_proc_is_gone(&ue->ue_tq)); -} - -static void -ue_init(void *arg) -{ - struct usb2_ether *ue = arg; - - UE_LOCK(ue); - ue_queue_command(ue, ue_start_task, - &ue->ue_sync_task[0].hdr, - &ue->ue_sync_task[1].hdr); - UE_UNLOCK(ue); -} - -static void -ue_start_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - struct ifnet *ifp = ue->ue_ifp; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - ue->ue_methods->ue_init(ue); - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - if (ue->ue_methods->ue_tick != NULL) - usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); -} - -static void -ue_stop_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - usb2_callout_stop(&ue->ue_watchdog); - - ue->ue_methods->ue_stop(ue); -} - -static void -ue_start(struct ifnet *ifp) -{ - struct usb2_ether *ue = ifp->if_softc; - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - UE_LOCK(ue); - ue->ue_methods->ue_start(ue); - UE_UNLOCK(ue); -} - -static void -ue_promisc_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - - ue->ue_methods->ue_setpromisc(ue); -} - -static void -ue_setmulti_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - - ue->ue_methods->ue_setmulti(ue); -} - -static int -ue_ifmedia_upd(struct ifnet *ifp) -{ - struct usb2_ether *ue = ifp->if_softc; - - /* Defer to process context */ - UE_LOCK(ue); - ue_queue_command(ue, ue_ifmedia_task, - &ue->ue_media_task[0].hdr, - &ue->ue_media_task[1].hdr); - UE_UNLOCK(ue); - - return (0); -} - -static void -ue_ifmedia_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - struct ifnet *ifp = ue->ue_ifp; - - ue->ue_methods->ue_mii_upd(ifp); -} - -static void -ue_watchdog(void *arg) -{ - struct usb2_ether *ue = arg; - struct ifnet *ifp = ue->ue_ifp; - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - ue_queue_command(ue, ue_tick_task, - &ue->ue_tick_task[0].hdr, - &ue->ue_tick_task[1].hdr); - - usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); -} - -static void -ue_tick_task(struct usb2_proc_msg *_task) -{ - struct usb2_ether_cfg_task *task = - (struct usb2_ether_cfg_task *)_task; - struct usb2_ether *ue = task->ue; - struct ifnet *ifp = ue->ue_ifp; - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - ue->ue_methods->ue_tick(ue); -} - -int -usb2_ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) -{ - struct usb2_ether *ue = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)data; - struct mii_data *mii; - int error = 0; - - switch (command) { - case SIOCSIFFLAGS: - UE_LOCK(ue); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ue_queue_command(ue, ue_promisc_task, - &ue->ue_promisc_task[0].hdr, - &ue->ue_promisc_task[1].hdr); - else - ue_queue_command(ue, ue_start_task, - &ue->ue_sync_task[0].hdr, - &ue->ue_sync_task[1].hdr); - } else { - ue_queue_command(ue, ue_stop_task, - &ue->ue_sync_task[0].hdr, - &ue->ue_sync_task[1].hdr); - } - UE_UNLOCK(ue); - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - UE_LOCK(ue); - ue_queue_command(ue, ue_setmulti_task, - &ue->ue_multi_task[0].hdr, - &ue->ue_multi_task[1].hdr); - UE_UNLOCK(ue); - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - if (ue->ue_miibus != NULL) { - mii = device_get_softc(ue->ue_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - } else - error = ether_ioctl(ifp, command, data); - break; - default: - error = ether_ioctl(ifp, command, data); - break; - } - return (error); -} - -static int -usb2_ether_modevent(module_t mod, int type, void *data) -{ - - switch (type) { - case MOD_LOAD: - ueunit = new_unrhdr(0, INT_MAX, NULL); - break; - case MOD_UNLOAD: - break; - default: - return (EOPNOTSUPP); - } - return (0); -} -static moduledata_t usb2_ether_mod = { - "usb2_ethernet", - usb2_ether_modevent, - 0 -}; - -int -usb2_ether_rxmbuf(struct usb2_ether *ue, struct mbuf *m, - unsigned int len) -{ - struct ifnet *ifp = ue->ue_ifp; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - /* finalize mbuf */ - ifp->if_ipackets++; - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = len; - - /* enqueue for later when the lock can be released */ - _IF_ENQUEUE(&ue->ue_rxq, m); - return (0); -} - -int -usb2_ether_rxbuf(struct usb2_ether *ue, struct usb2_page_cache *pc, - unsigned int offset, unsigned int len) -{ - struct ifnet *ifp = ue->ue_ifp; - struct mbuf *m; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - if (len < ETHER_HDR_LEN || len > MCLBYTES) - return (1); - - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) { - ifp->if_ierrors++; - return (ENOMEM); - } - - m_adj(m, ETHER_ALIGN); - usb2_copy_out(pc, offset, mtod(m, uint8_t *), len); - - /* finalize mbuf */ - ifp->if_ipackets++; - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = len; - - /* enqueue for later when the lock can be released */ - _IF_ENQUEUE(&ue->ue_rxq, m); - return (0); -} - -void -usb2_ether_rxflush(struct usb2_ether *ue) -{ - struct ifnet *ifp = ue->ue_ifp; - struct mbuf *m; - - UE_LOCK_ASSERT(ue, MA_OWNED); - - for (;;) { - _IF_DEQUEUE(&ue->ue_rxq, m); - if (m == NULL) - break; - - /* - * The USB xfer has been resubmitted so its safe to unlock now. - */ - UE_UNLOCK(ue); - ifp->if_input(ifp, m); - UE_LOCK(ue); - } -} - -DECLARE_MODULE(usb2_ethernet, usb2_ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); -MODULE_VERSION(usb2_ethernet, 1); diff --git a/sys/dev/usb2/ethernet/usb2_ethernet.h b/sys/dev/usb2/ethernet/usb2_ethernet.h deleted file mode 100644 index 0ee36f2..0000000 --- a/sys/dev/usb2/ethernet/usb2_ethernet.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $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 - -#include "miibus_if.h" - -#include -#include - -struct usb2_ether; -struct usb2_device_request; - -typedef void (usb2_ether_fn_t)(struct usb2_ether *); - -struct usb2_ether_methods { - usb2_ether_fn_t *ue_attach_post; - usb2_ether_fn_t *ue_start; - usb2_ether_fn_t *ue_init; - usb2_ether_fn_t *ue_stop; - usb2_ether_fn_t *ue_setmulti; - usb2_ether_fn_t *ue_setpromisc; - usb2_ether_fn_t *ue_tick; - int (*ue_mii_upd)(struct ifnet *); - void (*ue_mii_sts)(struct ifnet *, - struct ifmediareq *); - int (*ue_ioctl)(struct ifnet *, u_long, caddr_t); - -}; - -struct usb2_ether_cfg_task { - struct usb2_proc_msg hdr; - struct usb2_ether *ue; -}; - -struct usb2_ether { - /* NOTE: the "ue_ifp" pointer must be first --hps */ - struct ifnet *ue_ifp; - struct mtx *ue_mtx; - const struct usb2_ether_methods *ue_methods; - struct sysctl_oid *ue_sysctl_oid; - void *ue_sc; - struct usb2_device *ue_udev; /* used by usb2_ether_do_request() */ - device_t ue_dev; - device_t ue_miibus; - - struct usb2_process ue_tq; - struct sysctl_ctx_list ue_sysctl_ctx; - struct ifqueue ue_rxq; - struct usb2_callout ue_watchdog; - struct usb2_ether_cfg_task ue_sync_task[2]; - struct usb2_ether_cfg_task ue_media_task[2]; - struct usb2_ether_cfg_task ue_multi_task[2]; - struct usb2_ether_cfg_task ue_promisc_task[2]; - struct usb2_ether_cfg_task ue_tick_task[2]; - - int ue_unit; - - /* ethernet address from eeprom */ - uint8_t ue_eaddr[ETHER_ADDR_LEN]; -}; - -#define usb2_ether_do_request(ue,req,data,timo) \ - usb2_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo) - -uint8_t usb2_ether_pause(struct usb2_ether *, unsigned int); -struct ifnet *usb2_ether_getifp(struct usb2_ether *); -struct mii_data *usb2_ether_getmii(struct usb2_ether *); -void *usb2_ether_getsc(struct usb2_ether *); -int usb2_ether_ifattach(struct usb2_ether *); -void usb2_ether_ifdetach(struct usb2_ether *); -int usb2_ether_ioctl(struct ifnet *, u_long, caddr_t); -int usb2_ether_rxmbuf(struct usb2_ether *, struct mbuf *, - unsigned int); -int usb2_ether_rxbuf(struct usb2_ether *, - struct usb2_page_cache *, - unsigned int, unsigned int); -void usb2_ether_rxflush(struct usb2_ether *); -void usb2_ether_ifshutdown(struct usb2_ether *); -uint8_t usb2_ether_is_gone(struct usb2_ether *); -#endif /* _USB2_ETHERNET_H_ */ diff --git a/sys/dev/usb2/image/usb2_image.c b/sys/dev/usb2/image/usb2_image.c deleted file mode 100644 index 5f6badb..0000000 --- a/sys/dev/usb2/image/usb2_image.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index ce1526e..0000000 --- a/sys/dev/usb2/image/usb2_image.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 54dfd55..0000000 --- a/sys/dev/usb2/image/uscanner2.c +++ /dev/null @@ -1,644 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR uscanner_debug - -#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, debug, CTLFLAG_RW, &uscanner_debug, - 0, "uscanner debug level"); -#endif - -/* - * uscanner transfers macros definition. - */ -#define USCANNER_BSIZE (1 << 15) -#define USCANNER_IFQ_MAXLEN 2 - -/* - * 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 - -enum { - USCANNER_BULK_DT_WR, - USCANNER_BULK_DT_RD, - USCANNER_BULK_CS_WR, - USCANNER_BULK_CS_RD, - USCANNER_N_TRANSFER = 4, -}; - -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] = { - [USCANNER_BULK_DT_WR] = { - .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, - }, - - [USCANNER_BULK_DT_RD] = { - .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, - }, - - [USCANNER_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 = &uscanner_write_clear_stall_callback, - .mh.timeout = 1000, - .mh.interval = 50, /* 50ms */ - }, - - [USCANNER_BULK_CS_RD] = { - .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_4470C, 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[USCANNER_BULK_CS_RD]); - 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[USCANNER_BULK_CS_RD]); - } - break; - } -} - -/* - * 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[USCANNER_BULK_DT_RD]; - - if (usb2_clear_stall_callback(xfer, xfer_other)) { - DPRINTF("stall cleared\n"); - sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; - usb2_transfer_start(xfer_other); - } -} - -/* - * 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[USCANNER_BULK_CS_WR]); - 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[USCANNER_BULK_CS_WR]); - } - break; - } -} - -/* - * 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[USCANNER_BULK_DT_WR]; - - if (usb2_clear_stall_callback(xfer, xfer_other)) { - DPRINTF("stall cleared\n"); - sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; - usb2_transfer_start(xfer_other); - } -} - -/* - * 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[USCANNER_BULK_DT_RD]->max_data_length, - USCANNER_IFQ_MAXLEN)) { - return (ENOMEM); - } - } - if (fflags & FWRITE) { - if (usb2_fifo_alloc_buffer(fifo, - sc->sc_xfer[USCANNER_BULK_DT_WR]->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); - } -} - -/* - * 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[USCANNER_BULK_DT_RD]); -} - -/* - * 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[USCANNER_BULK_DT_WR]); -} - -/* - * 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[USCANNER_BULK_CS_RD]); - usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_RD]); -} - -/* - * 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[USCANNER_BULK_CS_WR]); - usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_WR]); -} diff --git a/sys/dev/usb2/include/ufm2_ioctl.h b/sys/dev/usb2/include/ufm2_ioctl.h deleted file mode 100644 index 921b3d4..0000000 --- a/sys/dev/usb2/include/ufm2_ioctl.h +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * 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/usb2_cdc.h b/sys/dev/usb2/include/usb2_cdc.h deleted file mode 100644 index d1e3dcb..0000000 --- a/sys/dev/usb2/include/usb2_cdc.h +++ /dev/null @@ -1,191 +0,0 @@ -/* $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 - -#endif /* _USB_CDC_H_ */ diff --git a/sys/dev/usb2/include/usb2_defs.h b/sys/dev/usb2/include/usb2_defs.h deleted file mode 100644 index 64caf39..0000000 --- a/sys/dev/usb2/include/usb2_defs.h +++ /dev/null @@ -1,77 +0,0 @@ -/* $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_ROOT_HUB_ADDR 1 /* index */ - -#define USB_MIN_DEVICES 2 /* unused + root HUB */ - -#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 "There cannot be less FIFOs than USB endpoints." -#endif -#if (USB_FIFO_MAX & 1) -#error "Number of FIFOs must be odd." -#endif -#if (USB_EP_MAX < (2*16)) -#error "Number of hardware USB endpoints cannot be less than 32." -#endif -#if (USB_MAX_DEVICES < USB_MIN_DEVICES) -#error "Minimum number of devices is greater than maximum number of devices." -#endif -#if (USB_ROOT_HUB_ADDR >= USB_MIN_DEVICES) -#error "The root hub address must be less than USB_MIN_DEVICES." -#endif -#endif /* _USB2_DEFS_H_ */ diff --git a/sys/dev/usb2/include/usb2_endian.h b/sys/dev/usb2/include/usb2_endian.h deleted file mode 100644 index 2f49008..0000000 --- a/sys/dev/usb2/include/usb2_endian.h +++ /dev/null @@ -1,119 +0,0 @@ -/* $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 deleted file mode 100644 index 91f0245..0000000 --- a/sys/dev/usb2/include/usb2_error.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $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_ - -enum { /* keep in sync with usb_errstr_table */ - USB_ERR_NORMAL_COMPLETION = 0, - USB_ERR_PENDING_REQUESTS, /* 1 */ - USB_ERR_NOT_STARTED, /* 2 */ - USB_ERR_INVAL, /* 3 */ - USB_ERR_NOMEM, /* 4 */ - USB_ERR_CANCELLED, /* 5 */ - USB_ERR_BAD_ADDRESS, /* 6 */ - USB_ERR_BAD_BUFSIZE, /* 7 */ - USB_ERR_BAD_FLAG, /* 8 */ - USB_ERR_NO_CALLBACK, /* 9 */ - USB_ERR_IN_USE, /* 10 */ - USB_ERR_NO_ADDR, /* 11 */ - USB_ERR_NO_PIPE, /* 12 */ - USB_ERR_ZERO_NFRAMES, /* 13 */ - USB_ERR_ZERO_MAXP, /* 14 */ - USB_ERR_SET_ADDR_FAILED, /* 15 */ - USB_ERR_NO_POWER, /* 16 */ - USB_ERR_TOO_DEEP, /* 17 */ - USB_ERR_IOERROR, /* 18 */ - USB_ERR_NOT_CONFIGURED, /* 19 */ - USB_ERR_TIMEOUT, /* 20 */ - USB_ERR_SHORT_XFER, /* 21 */ - USB_ERR_STALLED, /* 22 */ - USB_ERR_INTERRUPTED, /* 23 */ - USB_ERR_DMA_LOAD_FAILED, /* 24 */ - USB_ERR_BAD_CONTEXT, /* 25 */ - USB_ERR_NO_ROOT_HUB, /* 26 */ - USB_ERR_NO_INTR_THREAD, /* 27 */ - USB_ERR_NOT_LOCKED, /* 28 */ - USB_ERR_MAX -}; - -#endif /* _USB2_ERROR_H_ */ diff --git a/sys/dev/usb2/include/usb2_hid.h b/sys/dev/usb2/include/usb2_hid.h deleted file mode 100644 index 1a8650e..0000000 --- a/sys/dev/usb2/include/usb2_hid.h +++ /dev/null @@ -1,175 +0,0 @@ -/* $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_ - -#include - -#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 deleted file mode 100644 index 58c392a..0000000 --- a/sys/dev/usb2/include/usb2_ioctl.h +++ /dev/null @@ -1,292 +0,0 @@ -/* $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 - -/* Building "kdump" depends on these includes */ - -#include -#include - -#define USB_DEVICE_NAME "usb" -#define USB_GENERIC_NAME "ugen" - -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_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 _IOWR('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_IFACE_DRIVER _IOWR('U', 121, struct usb2_gen_descriptor) -#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 deleted file mode 100644 index 0fad203..0000000 --- a/sys/dev/usb2/include/usb2_mfunc.h +++ /dev/null @@ -1,78 +0,0 @@ -/* $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_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 deleted file mode 100644 index 06d1b21..0000000 --- a/sys/dev/usb2/include/usb2_revision.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $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_ - -/* - * The "USB_SPEED" macro defines all the supported USB speeds. - */ -enum { - USB_SPEED_VARIABLE, - USB_SPEED_LOW, - USB_SPEED_FULL, - USB_SPEED_HIGH, - USB_SPEED_SUPER, - USB_SPEED_MAX -}; - -/* - * The "USB_REV" macro defines all the supported USB revisions. - */ -enum { - USB_REV_UNKNOWN, - USB_REV_PRE_1_0, - USB_REV_1_0, - USB_REV_1_1, - USB_REV_2_0, - USB_REV_2_5, - USB_REV_3_0, - USB_REV_MAX -}; - -/* - * The "USB_MODE" macro defines all the supported USB modes. - */ -enum { - USB_MODE_HOST, - USB_MODE_DEVICE, - USB_MODE_MAX -}; - -#endif /* _USB2_REVISION_H_ */ diff --git a/sys/dev/usb2/include/usb2_standard.h b/sys/dev/usb2/include/usb2_standard.h deleted file mode 100644 index f81f346..0000000 --- a/sys/dev/usb2/include/usb2_standard.h +++ /dev/null @@ -1,619 +0,0 @@ -/* $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. - */ - -/* - * This file contains standard definitions for the following USB - * protocol versions: - * - * USB v1.0 - * USB v1.1 - * USB v2.0 - * USB v3.0 - */ - -#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 */ - -/* Definition of software USB power modes */ -#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 */ - -#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_PORT_RESUME_DELAY 20 /* 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_PORT_RESUME_DELAY (20*2) /* 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 /* language ID string index */ -#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_DEBUG 0x0A -#define UDESC_IFACE_ASSOC 0x0B /* interface association */ -#define UDESC_BOS 0x0F /* binary object store */ -#define UDESC_DEVICE_CAPABILITY 0x10 -#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 UDESC_ENDPOINT_SS_COMP 0x30 /* super speed */ -#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 -#define UR_SET_SEL 0x30 -#define UR_ISOCH_DELAY 0x31 - -/* 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 -#define UR_SET_HUB_DEPTH 0x0c -#define UR_GET_PORT_ERR_COUNT 0x0d - -/* Feature numbers */ -#define UF_ENDPOINT_HALT 0 -#define UF_DEVICE_REMOTE_WAKEUP 1 -#define UF_TEST_MODE 2 -#define UF_U1_ENABLE 0x30 -#define UF_U2_ENABLE 0x31 -#define UF_LTM_ENABLE 0x32 - -/* 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_LINK_STATE 5 -#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 - -/* SuperSpeed HUB specific features */ -#define UHF_PORT_U1_TIMEOUT 23 -#define UHF_PORT_U2_TIMEOUT 24 -#define UHF_C_PORT_LINK_STATE 25 -#define UHF_C_PORT_CONFIG_ERROR 26 -#define UHF_PORT_REMOTE_WAKE_MASK 27 -#define UHF_BH_PORT_RESET 28 -#define UHF_C_BH_PORT_RESET 29 -#define UHF_FORCE_LINKPM_ACCEPT 30 - -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_USB_3_0 0x0300 -#define UD_IS_USB2(d) ((d)->bcdUSB[1] == 0x02) -#define UD_IS_USB3(d) ((d)->bcdUSB[1] == 0x03) - 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; - -/* Binary Device Object Store (BOS) */ -struct usb2_bos_descriptor { - uByte bLength; - uByte bDescriptorType; - uWord wTotalLength; - uByte bNumDeviceCaps; -} __packed; - -/* Binary Device Object Store Capability */ -struct usb2_bos_cap_descriptor { - uByte bLength; - uByte bDescriptorType; - uByte bDevCapabilityType; -#define USB_DEVCAP_RESERVED 0x00 -#define USB_DEVCAP_WUSB 0x01 -#define USB_DEVCAP_USB2EXT 0x02 -#define USB_DEVCAP_SUPER_SPEED 0x03 -#define USB_DEVCAP_CONTAINER_ID 0x04 - /* data ... */ -} __packed; - -struct usb2_devcap_usb2ext_descriptor { - uByte bLength; - uByte bDescriptorType; - uByte bDevCapabilityType; - uByte bmAttributes; -#define USB_V2EXT_LPM 0x02 -} __packed; - -struct usb2_devcap_ss_descriptor { - uByte bLength; - uByte bDescriptorType; - uByte bDevCapabilityType; - uByte bmAttributes; - uWord wSpeedsSupported; - uByte bFunctionaltySupport; - uByte bU1DevExitLat; - uByte bU2DevExitLat; -} __packed; - -struct usb2_devcap_container_id_descriptor { - uByte bLength; - uByte bDescriptorType; - uByte bDevCapabilityType; - uByte bReserved; - uByte ContainerID; -} __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; - -struct usb2_interface_assoc_descriptor { - uByte bLength; - uByte bDescriptorType; - uByte bFirstInterface; - uByte bInterfaceCount; - uByte bFunctionClass; - uByte bFunctionSubClass; - uByte bFunctionProtocol; - uByte iFunction; -} __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_endpoint_ss_comp_descriptor { - uByte bLength; - uByte bDescriptorType; - uWord bMaxBurst; - uByte bmAttributes; - uWord wBytesPerInterval; -} __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) - uByte PortPowerCtrlMask[1]; /* deprecated */ -} __packed; - -struct usb2_hub_ss_descriptor { - uByte bDescLength; - uByte bDescriptorType; - uByte bNbrPorts; /* max 15 */ - uWord wHubCharacteristics; - uByte bPwrOn2PwrGood; /* delay in 2 ms units */ - uByte bHubContrCurrent; - uByte bHubHdrDecLat; - uWord wHubDelay; - uByte DeviceRemovable[2]; /* max 15 ports */ -} __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_POWER 0x0100 -#define UPS_LOW_SPEED 0x0200 -#define UPS_HIGH_SPEED 0x0400 -#define UPS_PORT_TEST 0x0800 -#define UPS_PORT_INDICATOR 0x1000 -#define UPS_PORT_MODE_DEVICE 0x8000 /* currently FreeBSD specific */ - 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 deleted file mode 100644 index 84eb49c..0000000 --- a/sys/dev/usb2/input/uhid2.c +++ /dev/null @@ -1,791 +0,0 @@ -/* $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 "usbdevs.h" -#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_BSIZE 1024 /* bytes, buffer size */ -#define UHID_FRAME_NUM 50 /* bytes, frame number */ - -enum { - UHID_INTR_DT_RD, - UHID_CTRL_DT_WR, - UHID_CTRL_DT_RD, - UHID_N_TRANSFER, -}; - -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_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_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: -re_submit: - if (usb2_fifo_put_bytes_max( - sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { - xfer->frlengths[0] = sc->sc_isize; - usb2_start_hardware(xfer); - } - return; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto re_submit; - } - 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); -} - -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); -} - -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] = { - - [UHID_INTR_DT_RD] = { - .type = UE_INTERRUPT, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.bufsize = UHID_BSIZE, - .mh.callback = &uhid_intr_callback, - }, - - [UHID_CTRL_DT_WR] = { - .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 */ - }, - - [UHID_CTRL_DT_RD] = { - .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[UHID_CTRL_DT_RD]); - } else { - usb2_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]); - } -} - -static void -uhid_stop_read(struct usb2_fifo *fifo) -{ - struct uhid_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]); - usb2_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]); -} - -static void -uhid_start_write(struct usb2_fifo *fifo) -{ - struct uhid_softc *sc = fifo->priv_sc0; - - usb2_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]); -} - -static void -uhid_stop_write(struct usb2_fifo *fifo) -{ - struct uhid_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]); -} - -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); - } -} - -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; - if (ugd->ugd_data == NULL) - break; /* descriptor length only */ - 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); - - 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 deleted file mode 100644 index 487e560..0000000 --- a/sys/dev/usb2/input/ukbd2.c +++ /dev/null @@ -1,1492 +0,0 @@ -#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_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; - -enum { - UKBD_INTR_DT, - UKBD_INTR_CS, - UKBD_CTRL_LED, - UKBD_N_TRANSFER = 3, -}; - -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 *); -static void ukbd_set_leds(struct ukbd_softc *, uint8_t); -static int ukbd_set_typematic(keyboard_t *, int); -#ifdef UKBD_EMULATE_ATSCANCODE -static int ukbd_key2scan(struct ukbd_softc *, int, int, int); -#endif -static uint32_t ukbd_read_char(keyboard_t *, int); -static void ukbd_clear_state(keyboard_t *); -static int ukbd_ioctl(keyboard_t *, u_long, caddr_t); -static int ukbd_enable(keyboard_t *); -static int ukbd_disable(keyboard_t *); -static void ukbd_interrupt(struct ukbd_softc *); - -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"); - } -} - -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[UKBD_INTR_DT]); - } - 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); -} - -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[UKBD_INTR_DT]; - - if (usb2_clear_stall_callback(xfer, xfer_other)) { - DPRINTF("stall cleared\n"); - sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; - usb2_transfer_start(xfer_other); - } -} - -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[UKBD_INTR_CS]); - 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[UKBD_INTR_CS]); - } - 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] = { - - [UKBD_INTR_DT] = { - .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, - }, - - [UKBD_INTR_CS] = { - .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 */ - }, - - [UKBD_CTRL_LED] = { - .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; - - 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, 0); - - 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[UKBD_INTR_DT]); - - /* start the timer */ - - ukbd_timeout(sc); - mtx_unlock(&Giant); - 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)); -} - -/* 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[UKBD_CTRL_LED]); -} - -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 deleted file mode 100644 index 9de44f6..0000000 --- a/sys/dev/usb2/input/ums2.c +++ /dev/null @@ -1,904 +0,0 @@ -/*- - * 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 "usbdevs.h" -#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_BUTTON_MAX 31 /* exclusive, must be less than 32 */ -#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) - -enum { - UMS_INTR_DT, - UMS_INTR_CS, - UMS_N_TRANSFER = 2, -}; - -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); -} - -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[UMS_INTR_DT]; - - if (usb2_clear_stall_callback(xfer, xfer_other)) { - DPRINTF("stall cleared\n"); - sc->sc_flags &= ~UMS_FLAG_INTR_STALL; - usb2_transfer_start(xfer_other); - } -} - -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[UMS_INTR_CS]); - } 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[UMS_INTR_CS]); - } - return; - } -} - -static const struct usb2_config ums_config[UMS_N_TRANSFER] = { - - [UMS_INTR_DT] = { - .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, - }, - - [UMS_INTR_CS] = { - .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, 0); - - /* - * 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[UMS_INTR_DT]->max_frame_size) { - DPRINTF("WARNING: report size, %d bytes, is larger " - "than interrupt size, %d bytes!\n", - isize, sc->sc_xfer[UMS_INTR_DT]->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[UMS_INTR_DT]); -} - -static void -ums_stop_read(struct usb2_fifo *fifo) -{ - struct ums_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[UMS_INTR_CS]); - usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); - usb2_callout_stop(&sc->sc_callout); -} - - -#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"); - } -} - -static void -ums_reset_buf(struct ums_softc *sc) -{ - /* reset read queue */ - usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); -} - -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); - } -} - -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 deleted file mode 100644 index 56f9ff2..0000000 --- a/sys/dev/usb2/input/usb2_input.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index 0b51853..0000000 --- a/sys/dev/usb2/input/usb2_input.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 9f4363d..0000000 --- a/sys/dev/usb2/input/usb2_rdesc.h +++ /dev/null @@ -1,276 +0,0 @@ -/*- - * 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 deleted file mode 100644 index b60bbf7..0000000 --- a/sys/dev/usb2/misc/udbp2.c +++ /dev/null @@ -1,854 +0,0 @@ -/*- - * 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 "usbdevs.h" -#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, hook_p, void *, int); - -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; - - 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); - } -} - -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); -} - -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); - } -} - -/*********************************************************************** - * 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 deleted file mode 100644 index e6fd853..0000000 --- a/sys/dev/usb2/misc/udbp2.h +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * 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 deleted file mode 100644 index df62436..0000000 --- a/sys/dev/usb2/misc/ufm2.c +++ /dev/null @@ -1,330 +0,0 @@ -/*- - * 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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#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 *, uint8_t, uint16_t, uint16_t, - uint8_t *); -static int ufm_set_freq(struct ufm_softc *, void *); -static int ufm_get_freq(struct ufm_softc *, void *); -static int ufm_start(struct ufm_softc *, void *); -static int ufm_stop(struct ufm_softc *, void *); -static int ufm_get_stat(struct ufm_softc *, void *); - -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; - - 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. - */ - usb2_pause_mtx(NULL, hz / 4); - - 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 deleted file mode 100644 index 74eb6c5..0000000 --- a/sys/dev/usb2/misc/usb2_misc.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index dabf9e3..0000000 --- a/sys/dev/usb2/misc/usb2_misc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 3e2aaa2..0000000 --- a/sys/dev/usb2/ndis/if_ndis_usb2.c +++ /dev/null @@ -1,144 +0,0 @@ -/*- - * 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 deleted file mode 100644 index 1776e0d..0000000 --- a/sys/dev/usb2/ndis/usb2_ndis.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index 7f187e1..0000000 --- a/sys/dev/usb2/ndis/usb2_ndis.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index f86be0a..0000000 --- a/sys/dev/usb2/quirk/usb2_quirk.c +++ /dev/null @@ -1,397 +0,0 @@ -/* $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 "usbdevs.h" - -#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_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD4X20, 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_MICROSOFT, USB_PRODUCT_MICROSOFT_COMFORT3000, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, 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)}, -}; - -static const char *usb_quirk_str[USB_QUIRK_MAX] = { - [UQ_NONE] = "UQ_NONE", - [UQ_AUDIO_SWAP_LR] = "UQ_AUDIO_SWAP_LR", - [UQ_AU_INP_ASYNC] = "UQ_AU_INP_ASYNC", - [UQ_AU_NO_FRAC] = "UQ_AU_NO_FRAC", - [UQ_AU_NO_XU] = "UQ_AU_NO_XU", - [UQ_BAD_ADC] = "UQ_BAD_ADC", - [UQ_BAD_AUDIO] = "UQ_BAD_AUDIO", - [UQ_BROKEN_BIDIR] = "UQ_BROKEN_BIDIR", - [UQ_BUS_POWERED] = "UQ_BUS_POWERED", - [UQ_HID_IGNORE] = "UQ_HID_IGNORE", - [UQ_KBD_IGNORE] = "UQ_KBD_IGNORE", - [UQ_MS_BAD_CLASS] = "UQ_MS_BAD_CLASS", - [UQ_MS_LEADING_BYTE] = "UQ_MS_LEADING_BYTE", - [UQ_MS_REVZ] = "UQ_MS_REVZ", - [UQ_NO_STRINGS] = "UQ_NO_STRINGS", - [UQ_OPEN_CLEARSTALL] = "UQ_OPEN_CLEARSTALL", - [UQ_POWER_CLAIM] = "UQ_POWER_CLAIM", - [UQ_SPUR_BUT_UP] = "UQ_SPUR_BUT_UP", - [UQ_SWAP_UNICODE] = "UQ_SWAP_UNICODE", - [UQ_CFG_INDEX_1] = "UQ_CFG_INDEX_1", - [UQ_CFG_INDEX_2] = "UQ_CFG_INDEX_2", - [UQ_CFG_INDEX_3] = "UQ_CFG_INDEX_3", - [UQ_CFG_INDEX_4] = "UQ_CFG_INDEX_4", - [UQ_CFG_INDEX_0] = "UQ_CFG_INDEX_0", -}; - -/*------------------------------------------------------------------------* - * 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_str[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; -} - -static void -usb2_quirk_uninit(void *arg) -{ - usb2_quirk_unload(arg); - - /* destroy mutex */ - mtx_destroy(&usb2_quirk_mtx); -} - -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 deleted file mode 100644 index c9223e8..0000000 --- a/sys/dev/usb2/quirk/usb2_quirk.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $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 */ -enum { /* keep in sync with usb_quirk_str table */ - UQ_NONE, - UQ_AUDIO_SWAP_LR, /* left and right sound channels are swapped */ - UQ_AU_INP_ASYNC, /* input is async despite claim of adaptive */ - UQ_AU_NO_FRAC, /* don't adjust for fractional samples */ - UQ_AU_NO_XU, /* audio device has broken extension unit */ - UQ_BAD_ADC, /* bad audio spec version number */ - UQ_BAD_AUDIO, /* device claims audio class, but isn't */ - UQ_BROKEN_BIDIR, /* printer has broken bidir mode */ - UQ_BUS_POWERED, /* device is bus powered, despite claim */ - UQ_HID_IGNORE, /* device should be ignored by hid class */ - UQ_KBD_IGNORE, /* device should be ignored by kbd class */ - UQ_MS_BAD_CLASS, /* doesn't identify properly */ - UQ_MS_LEADING_BYTE, /* mouse sends an unknown leading byte */ - UQ_MS_REVZ, /* mouse has Z-axis reversed */ - UQ_NO_STRINGS, /* string descriptors are broken */ - UQ_OPEN_CLEARSTALL, /* device needs clear endpoint stall */ - UQ_POWER_CLAIM, /* hub lies about power status */ - UQ_SPUR_BUT_UP, /* spurious mouse button up events */ - UQ_SWAP_UNICODE, /* has some Unicode strings swapped */ - UQ_CFG_INDEX_1, /* select configuration index 1 by default */ - UQ_CFG_INDEX_2, /* select configuration index 2 by default */ - UQ_CFG_INDEX_3, /* select configuration index 3 by default */ - UQ_CFG_INDEX_4, /* select configuration index 4 by default */ - UQ_CFG_INDEX_0, /* select configuration index 0 by default */ - USB_QUIRK_MAX -}; - -#endif /* _USB2_QUIRK_H_ */ diff --git a/sys/dev/usb2/serial/u3g2.c b/sys/dev/usb2/serial/u3g2.c deleted file mode 100644 index e97c671..0000000 --- a/sys/dev/usb2/serial/u3g2.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (c) 2008 AnyWi Technologies - * Author: Andrea Guzzo - * * based on uark.c 1.1 2006/08/14 08:30:22 jsg * - * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk * - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * $FreeBSD$ - */ - -/* - * NOTE: - * - * - The detour through the tty layer is ridiculously expensive wrt - * buffering due to the high speeds. - * - * We should consider adding a simple r/w device which allows - * attaching of PPP in a more efficient way. - * - * NOTE: - * - * - The device ID's are stored in "core/usb2_msctest.c" - */ - -#include "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR u3g_debug - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if USB_DEBUG -static int u3g_debug = 0; - -SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB u3g"); -SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW, - &u3g_debug, 0, "u3g debug level"); -#endif - -#define U3G_MAXPORTS 4 -#define U3G_CONFIG_INDEX 0 -#define U3G_BSIZE 2048 - -#define U3GSP_GPRS 0 -#define U3GSP_EDGE 1 -#define U3GSP_CDMA 2 -#define U3GSP_UMTS 3 -#define U3GSP_HSDPA 4 -#define U3GSP_HSUPA 5 -#define U3GSP_HSPA 6 -#define U3GSP_MAX 7 - -#define U3GFL_NONE 0x00 /* No flags */ -#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */ -#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */ -#define U3GFL_SIERRA_INIT 0x04 /* Init command required */ - -struct u3g_speeds_s { - uint32_t ispeed; - uint32_t ospeed; -}; - -enum { - U3G_BULK_WR, - U3G_BULK_RD, - U3G_N_TRANSFER, -}; - -struct u3g_softc { - struct usb2_com_super_softc sc_super_ucom; - struct usb2_com_softc sc_ucom[U3G_MAXPORTS]; - - struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER]; - struct usb2_device *sc_udev; - - uint8_t sc_lsr; /* local status register */ - uint8_t sc_msr; /* U3G status register */ - uint8_t sc_numports; -}; - -static device_probe_t u3g_probe; -static device_attach_t u3g_attach; -static device_detach_t u3g_detach; - -static usb2_callback_t u3g_write_callback; -static usb2_callback_t u3g_read_callback; - -static void u3g_start_read(struct usb2_com_softc *ucom); -static void u3g_stop_read(struct usb2_com_softc *ucom); -static void u3g_start_write(struct usb2_com_softc *ucom); -static void u3g_stop_write(struct usb2_com_softc *ucom); - -static int u3g_driver_loaded(struct module *mod, int what, void *arg); - -static const struct usb2_config u3g_config[U3G_N_TRANSFER] = { - - [U3G_BULK_WR] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = U3G_BSIZE,/* bytes */ - .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, - .mh.callback = &u3g_write_callback, - }, - - [U3G_BULK_RD] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = U3G_BSIZE,/* bytes */ - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.callback = &u3g_read_callback, - }, -}; - -static const struct usb2_com_callback u3g_callback = { - .usb2_com_start_read = &u3g_start_read, - .usb2_com_stop_read = &u3g_stop_read, - .usb2_com_start_write = &u3g_start_write, - .usb2_com_stop_write = &u3g_stop_write, -}; - -#if 0 -static const struct u3g_speeds_s u3g_speeds[U3GSP_MAX] = { - [U3GSP_GPRS] = {64000, 64000}, - [U3GSP_EDGE] = {384000, 64000}, - [U3GSP_CDMA] = {384000, 64000}, - [U3GSP_UMTS] = {384000, 64000}, - [U3GSP_HSDPA] = {1200000, 384000}, - [U3GSP_HSUPA] = {1200000, 384000}, - [U3GSP_HSPA] = {7200000, 384000}, -}; -#endif - -static device_method_t u3g_methods[] = { - DEVMETHOD(device_probe, u3g_probe), - DEVMETHOD(device_attach, u3g_attach), - DEVMETHOD(device_detach, u3g_detach), - {0, 0} -}; - -static devclass_t u3g_devclass; - -static driver_t u3g_driver = { - .name = "u3g", - .methods = u3g_methods, - .size = sizeof(struct u3g_softc), -}; - -DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0); -MODULE_DEPEND(u3g, usb2_serial, 1, 1, 1); -MODULE_DEPEND(u3g, usb2_core, 1, 1, 1); - -/* Huawei specific defines */ - -#define U3GINFO(flag,speed) ((flag)|((speed) * 256)) -#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256) - -/* - * NOTE: The entries marked with XXX should be checked for the correct - * speed indication to set the buffer sizes. - */ -static const struct usb2_device_id u3g_devs[] = { - /* OEM: Option */ - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - /* OEM: Qualcomm, Inc. */ - {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - /* OEM: Huawei */ - {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, - {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, - /* OEM: Novatel */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - /* OEM: Merlin */ - {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - /* OEM: Sierra Wireless: */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */ - /* Sierra TruInstaller device ID */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, -}; - -static void -u3g_sierra_init(struct usb2_device *udev) -{ - struct usb2_device_request req; - - DPRINTFN(0, "\n"); - - req.bmRequestType = UT_VENDOR; - req.bRequest = UR_SET_INTERFACE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_CONNECTION); - USETW(req.wLength, 0); - - if (usb2_do_request_flags(udev, NULL, &req, - NULL, 0, NULL, USB_MS_HZ)) { - /* ignore any errors */ - } - return; -} - -static void -u3g_huawei_init(struct usb2_device *udev) -{ - struct usb2_device_request req; - - DPRINTFN(0, "\n"); - - req.bmRequestType = UT_WRITE_DEVICE; - req.bRequest = UR_SET_FEATURE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_SUSPEND); - USETW(req.wLength, 0); - - if (usb2_do_request_flags(udev, NULL, &req, - NULL, 0, NULL, USB_MS_HZ)) { - /* ignore any errors */ - } - return; -} - -static int -u3g_lookup_huawei(struct usb2_attach_arg *uaa) -{ - /* Calling the lookup function will also set the driver info! */ - return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); -} - -/* - * The following function handles 3G modem devices (E220, Mobile, - * etc.) with auto-install flash disks for Windows/MacOSX on the first - * interface. After some command or some delay they change appearance - * to a modem. - */ -static usb2_error_t -u3g_test_huawei_autoinst(struct usb2_device *udev, - struct usb2_attach_arg *uaa) -{ - struct usb2_interface *iface; - struct usb2_interface_descriptor *id; - uint32_t flags; - - if (udev == NULL) { - return (USB_ERR_INVAL); - } - iface = usb2_get_iface(udev, 0); - 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); - } - if (u3g_lookup_huawei(uaa)) { - /* no device match */ - return (USB_ERR_INVAL); - } - flags = USB_GET_DRIVER_INFO(uaa); - - if (flags & U3GFL_HUAWEI_INIT) { - u3g_huawei_init(udev); - } else if (flags & U3GFL_SCSI_EJECT) { - return (usb2_test_autoinstall(udev, 0, 1)); - } else if (flags & U3GFL_SIERRA_INIT) { - u3g_sierra_init(udev); - } else { - /* no quirks */ - return (USB_ERR_INVAL); - } - return (0); /* success */ -} - -static int -u3g_driver_loaded(struct module *mod, int what, void *arg) -{ - switch (what) { - case MOD_LOAD: - /* register our autoinstall handler */ - usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst; - break; - case MOD_UNLOAD: - usb2_test_huawei_unload(NULL); - break; - default: - return (EOPNOTSUPP); - } - return (0); -} - -static int -u3g_probe(device_t self) -{ - struct usb2_attach_arg *uaa = device_get_ivars(self); - - if (uaa->usb2_mode != USB_MODE_HOST) { - return (ENXIO); - } - if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) { - return (ENXIO); - } - if (uaa->info.bInterfaceClass != UICLASS_VENDOR) { - return (ENXIO); - } - return (u3g_lookup_huawei(uaa)); -} - -static int -u3g_attach(device_t dev) -{ - struct usb2_config u3g_config_tmp[U3G_N_TRANSFER]; - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct u3g_softc *sc = device_get_softc(dev); - struct usb2_interface *iface; - struct usb2_interface_descriptor *id; - uint8_t m; - uint8_t n; - uint8_t i; - uint8_t x; - int error; - - DPRINTF("sc=%p\n", sc); - - /* copy in USB config */ - for (n = 0; n != U3G_N_TRANSFER; n++) - u3g_config_tmp[n] = u3g_config[n]; - - device_set_usb2_desc(dev); - - sc->sc_udev = uaa->device; - - x = 0; /* interface index */ - i = 0; /* endpoint index */ - m = 0; /* number of ports */ - - while (m != U3G_MAXPORTS) { - - /* update BULK endpoint index */ - for (n = 0; n != U3G_N_TRANSFER; n++) - u3g_config_tmp[n].ep_index = i; - - iface = usb2_get_iface(uaa->device, x); - if (iface == NULL) { - if (m != 0) - break; /* end of interfaces */ - DPRINTF("did not find any modem endpoints\n"); - goto detach; - } - - id = usb2_get_interface_descriptor(iface); - if ((id == NULL) || - (id->bInterfaceClass != UICLASS_VENDOR)) { - /* next interface */ - x++; - i = 0; - continue; - } - - /* try to allocate a set of BULK endpoints */ - error = usb2_transfer_setup(uaa->device, &x, - sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER, - &sc->sc_ucom[m], &Giant); - if (error) { - /* next interface */ - x++; - i = 0; - continue; - } - - /* grab other interface, if any */ - if (x != uaa->info.bIfaceIndex) - usb2_set_parent_iface(uaa->device, x, - uaa->info.bIfaceIndex); - - /* set stall by default */ - usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]); - usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]); - - m++; /* found one port */ - i++; /* next endpoint index */ - } - - sc->sc_numports = m; - - error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, - sc->sc_numports, sc, &u3g_callback, &Giant); - if (error) { - DPRINTF("usb2_com_attach failed\n"); - goto detach; - } - if (sc->sc_numports != 1) { - /* be verbose */ - device_printf(dev, "Found %u ports.\n", - (unsigned int)sc->sc_numports); - } - return (0); - -detach: - u3g_detach(dev); - return (ENXIO); -} - -static int -u3g_detach(device_t dev) -{ - struct u3g_softc *sc = device_get_softc(dev); - uint8_t m; - - DPRINTF("sc=%p\n", sc); - - /* NOTE: It is not dangerous to detach more ports than attached! */ - usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS); - - for (m = 0; m != U3G_MAXPORTS; m++) - usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER); - - return (0); -} - -static void -u3g_start_read(struct usb2_com_softc *ucom) -{ - struct u3g_softc *sc = ucom->sc_parent; - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); - return; -} - -static void -u3g_stop_read(struct usb2_com_softc *ucom) -{ - struct u3g_softc *sc = ucom->sc_parent; - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); - return; -} - -static void -u3g_start_write(struct usb2_com_softc *ucom) -{ - struct u3g_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); - return; -} - -static void -u3g_stop_write(struct usb2_com_softc *ucom) -{ - struct u3g_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); - return; -} - -static void -u3g_write_callback(struct usb2_xfer *xfer) -{ - struct usb2_com_softc *ucom = xfer->priv_sc; - uint32_t actlen; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - case USB_ST_SETUP: -tr_setup: - if (usb2_com_get_data(ucom, xfer->frbuffers, 0, - U3G_BSIZE, &actlen)) { - xfer->frlengths[0] = actlen; - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* do a builtin clear-stall */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } - return; -} - -static void -u3g_read_callback(struct usb2_xfer *xfer) -{ - struct usb2_com_softc *ucom = xfer->priv_sc; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen); - - 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) { - /* do a builtin clear-stall */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } - return; -} diff --git a/sys/dev/usb2/serial/uark2.c b/sys/dev/usb2/serial/uark2.c deleted file mode 100644 index 83bce0f..0000000 --- a/sys/dev/usb2/serial/uark2.c +++ /dev/null @@ -1,407 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#include -#include -#include -#include -#include -#include - -#include - -#define UARK_BUF_SIZE 1024 /* bytes */ - -#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 - -enum { - UARK_BULK_DT_WR, - UARK_BULK_DT_RD, - UARK_N_TRANSFER, -}; - -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_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_read_callback; - -static void uark_start_read(struct usb2_com_softc *); -static void uark_stop_read(struct usb2_com_softc *); -static void uark_start_write(struct usb2_com_softc *); -static void uark_stop_write(struct usb2_com_softc *); -static int uark_pre_param(struct usb2_com_softc *, struct termios *); -static void uark_cfg_param(struct usb2_com_softc *, struct termios *); -static void uark_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void uark_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void uark_cfg_write(struct uark_softc *, uint16_t, uint16_t); - -static const struct usb2_config - uark_xfer_config[UARK_N_TRANSFER] = { - - [UARK_BULK_DT_WR] = { - .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, - }, - - [UARK_BULK_DT_RD] = { - .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, - }, -}; - -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); - -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; - - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]); - - 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -uark_start_read(struct usb2_com_softc *ucom) -{ - struct uark_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]); -} - -static void -uark_stop_read(struct usb2_com_softc *ucom) -{ - struct uark_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]); -} - -static void -uark_start_write(struct usb2_com_softc *ucom) -{ - struct uark_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]); -} - -static void -uark_stop_write(struct usb2_com_softc *ucom) -{ - struct uark_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]); -} - -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); -} - -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; -} - -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); -} - -static void -uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value) -{ - struct usb2_device_request req; - usb2_error_t err; - - req.bmRequestType = UARK_WRITE; - req.bRequest = UARK_REQUEST; - USETW(req.wValue, value); - USETW(req.wIndex, index); - USETW(req.wLength, 0); - - err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } -} diff --git a/sys/dev/usb2/serial/ubsa2.c b/sys/dev/usb2/serial/ubsa2.c deleted file mode 100644 index 7aeb988..0000000 --- a/sys/dev/usb2/serial/ubsa2.c +++ /dev/null @@ -1,634 +0,0 @@ -/*- - * 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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR ubsa_debug - -#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_BSIZE 1024 /* bytes */ - -#define UBSA_CONFIG_INDEX 0 -#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 */ - -enum { - UBSA_BULK_DT_WR, - UBSA_BULK_DT_RD, - UBSA_INTR_DT_RD, - UBSA_N_TRANSFER, -}; - -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; - - 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_read_callback; -static usb2_callback_t ubsa_intr_callback; - -static void ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t); -static void ubsa_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void ubsa_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void ubsa_cfg_set_break(struct usb2_com_softc *, uint8_t); -static int ubsa_pre_param(struct usb2_com_softc *, struct termios *); -static void ubsa_cfg_param(struct usb2_com_softc *, struct termios *); -static void ubsa_start_read(struct usb2_com_softc *); -static void ubsa_stop_read(struct usb2_com_softc *); -static void ubsa_start_write(struct usb2_com_softc *); -static void ubsa_stop_write(struct usb2_com_softc *); -static void ubsa_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); - -static const struct usb2_config ubsa_config[UBSA_N_TRANSFER] = { - - [UBSA_BULK_DT_WR] = { - .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, - }, - - [UBSA_BULK_DT_RD] = { - .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, - }, - - [UBSA_INTR_DT_RD] = { - .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, - }, -}; - -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)}, -}; - -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); - -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); - - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]); - - 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; - - 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } -} - -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); -} - -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); -} - -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); -} - -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); -} - -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[UBSA_INTR_DT_RD]); - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]); -} - -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[UBSA_INTR_DT_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]); -} - -static void -ubsa_start_write(struct usb2_com_softc *ucom) -{ - struct ubsa_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]); -} - -static void -ubsa_stop_write(struct usb2_com_softc *ucom) -{ - struct ubsa_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]); -} - -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; -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - - } -} diff --git a/sys/dev/usb2/serial/ubser2.c b/sys/dev/usb2/serial/ubser2.c deleted file mode 100644 index 4ec1542..0000000 --- a/sys/dev/usb2/serial/ubser2.c +++ /dev/null @@ -1,518 +0,0 @@ -/*- - * 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 - -#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 - -enum { - UBSER_BULK_DT_WR, - UBSER_BULK_DT_RD, - UBSER_N_TRANSFER, -}; - -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_N_TRANSFER]; - struct usb2_device *sc_udev; - - uint16_t sc_tx_size; - - uint8_t sc_numser; - 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_callback; -static usb2_callback_t ubser_read_callback; - -static int ubser_pre_param(struct usb2_com_softc *, struct termios *); -static void ubser_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void ubser_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void ubser_start_read(struct usb2_com_softc *); -static void ubser_stop_read(struct usb2_com_softc *); -static void ubser_start_write(struct usb2_com_softc *); -static void ubser_stop_write(struct usb2_com_softc *); - -static const struct usb2_config ubser_config[UBSER_N_TRANSFER] = { - - [UBSER_BULK_DT_WR] = { - .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_BULK_DT_RD] = { - .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, - }, -}; - -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); - -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; - - 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_N_TRANSFER, sc, &Giant); - if (error) { - goto detach; - } - sc->sc_tx_size = sc->sc_xfer[UBSER_BULK_DT_WR]->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); - - usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]); - - usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); - - 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); - - DPRINTF("\n"); - - usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser); - - usb2_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER); - - 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; - } -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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_com_cfg_do_request(sc->sc_udev, ucom, - &req, NULL, 0, 1000); - if (err) { - DPRINTFN(0, "send break failed, error=%s\n", - usb2_errstr(err)); - } - } -} - -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; -} - -static void -ubser_start_read(struct usb2_com_softc *ucom) -{ - struct ubser_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); -} - -static void -ubser_stop_read(struct usb2_com_softc *ucom) -{ - struct ubser_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]); -} - -static void -ubser_start_write(struct usb2_com_softc *ucom) -{ - struct ubser_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]); -} - -static void -ubser_stop_write(struct usb2_com_softc *ucom) -{ - struct ubser_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]); -} diff --git a/sys/dev/usb2/serial/uchcom2.c b/sys/dev/usb2/serial/uchcom2.c deleted file mode 100644 index 2c3e8f2..0000000 --- a/sys/dev/usb2/serial/uchcom2.c +++ /dev/null @@ -1,883 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR uchcom_debug - -#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 */ - -enum { - UCHCOM_BULK_DT_WR, - UCHCOM_BULK_DT_RD, - UCHCOM_INTR_DT_RD, - UCHCOM_N_TRANSFER, -}; - -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 */ -}; - -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_pre_param(struct usb2_com_softc *, struct termios *); -static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *); -static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void uchcom_start_read(struct usb2_com_softc *); -static void uchcom_start_write(struct usb2_com_softc *); -static void uchcom_stop_read(struct usb2_com_softc *); -static void uchcom_stop_write(struct usb2_com_softc *); -static void uchcom_update_version(struct uchcom_softc *); -static void uchcom_convert_status(struct uchcom_softc *, uint8_t); -static void uchcom_update_status(struct uchcom_softc *); -static void uchcom_set_dtrrts(struct uchcom_softc *); -static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); -static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); -static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t); -static void uchcom_clear_chip(struct uchcom_softc *); -static void uchcom_reset_chip(struct uchcom_softc *); - -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_write_callback; -static usb2_callback_t uchcom_read_callback; - -static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { - - [UCHCOM_BULK_DT_WR] = { - .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, - }, - - [UCHCOM_BULK_DT_RD] = { - .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, - }, - - [UCHCOM_INTR_DT_RD] = { - .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, - }, -}; - -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_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"); - - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); - - 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_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); - - usb2_com_cfg_do_request(sc->sc_udev, - &sc->sc_ucom, &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, - &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); -} - -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)); -} - -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]; -} - -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]; -} - -static void -uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) -{ - uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); -} - -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); -} - -static void -uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) -{ - uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); -} - - -/* ---------------------------------------------------------------------- - * middle layer - */ - -static void -uchcom_update_version(struct uchcom_softc *sc) -{ - uchcom_get_version(sc, &sc->sc_version); -} - -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); -} - - -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); -} - -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); -} - -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); -} - -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); -} - -static void -uchcom_clear_chip(struct uchcom_softc *sc) -{ - DPRINTF("\n"); - uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); -} - -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); -} - -/* ---------------------------------------------------------------------- - * 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; -} - -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); -} - -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); -} - -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); -} - -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[UCHCOM_INTR_DT_RD]); - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); -} - -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[UCHCOM_INTR_DT_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); -} - -static void -uchcom_start_write(struct usb2_com_softc *ucom) -{ - struct uchcom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); -} - -static void -uchcom_stop_write(struct usb2_com_softc *ucom) -{ - struct uchcom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); -} - -/* ---------------------------------------------------------------------- - * 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: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - break; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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); diff --git a/sys/dev/usb2/serial/ucycom2.c b/sys/dev/usb2/serial/ucycom2.c deleted file mode 100644 index 5bf59a2..0000000 --- a/sys/dev/usb2/serial/ucycom2.c +++ /dev/null @@ -1,564 +0,0 @@ -#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 "usbdevs.h" -#include -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ - -#define UCYCOM_IFACE_INDEX 0 - -enum { - UCYCOM_CTRL_RD, - UCYCOM_INTR_RD, - UCYCOM_N_TRANSFER, -}; - -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_N_TRANSFER]; - - 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_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_callback; - -static void ucycom_cfg_open(struct usb2_com_softc *); -static void ucycom_start_read(struct usb2_com_softc *); -static void ucycom_stop_read(struct usb2_com_softc *); -static void ucycom_start_write(struct usb2_com_softc *); -static void ucycom_stop_write(struct usb2_com_softc *); -static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); -static int ucycom_pre_param(struct usb2_com_softc *, struct termios *); -static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *); - -static const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = { - - [UCYCOM_CTRL_RD] = { - .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 */ - }, - - [UCYCOM_INTR_RD] = { - .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, - }, -}; - -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); - -/* - * 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; - - 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_N_TRANSFER, - 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_N_TRANSFER); - - 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); -} - -static void -ucycom_start_read(struct usb2_com_softc *ucom) -{ - struct ucycom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); -} - -static void -ucycom_stop_read(struct usb2_com_softc *ucom) -{ - struct ucycom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); -} - -static void -ucycom_start_write(struct usb2_com_softc *ucom) -{ - struct ucycom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); -} - -static void -ucycom_stop_write(struct usb2_com_softc *ucom) -{ - struct ucycom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); -} - -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; - - err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, sc->sc_temp_cfg, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } -} - -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); -} - -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: - xfer->frlengths[0] = sc->sc_ilen; - usb2_start_hardware(xfer); - return; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - - } -} diff --git a/sys/dev/usb2/serial/ufoma2.c b/sys/dev/usb2/serial/ufoma2.c deleted file mode 100644 index 12f49d7..0000000 --- a/sys/dev/usb2/serial/ufoma2.c +++ /dev/null @@ -1,1212 +0,0 @@ -/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ - -#include -__FBSDID("$FreeBSD$"); -#define UFOMA_HANDSFREE -/*- - * 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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#include -#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 */ - -enum { - UFOMA_CTRL_ENDPT_INTR, - UFOMA_CTRL_ENDPT_READ, - UFOMA_CTRL_ENDPT_WRITE, - UFOMA_CTRL_ENDPT_MAX, -}; - -enum { - UFOMA_BULK_ENDPT_WRITE, - UFOMA_BULK_ENDPT_READ, - UFOMA_BULK_ENDPT_MAX, -}; - -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_nobulk; - 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_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_callback; -static usb2_callback_t ufoma_bulk_write_callback; -static usb2_callback_t ufoma_bulk_read_callback; - -static void *ufoma_get_intconf(struct usb2_config_descriptor *, - struct usb2_interface_descriptor *, uint8_t, uint8_t); -static void ufoma_cfg_link_state(struct ufoma_softc *); -static void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t); -static void ufoma_cfg_open(struct usb2_com_softc *); -static void ufoma_cfg_close(struct usb2_com_softc *); -static void ufoma_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void ufoma_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void ufoma_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void ufoma_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static int ufoma_pre_param(struct usb2_com_softc *, struct termios *); -static void ufoma_cfg_param(struct usb2_com_softc *, struct termios *); -static int ufoma_modem_setup(device_t, struct ufoma_softc *, - struct usb2_attach_arg *); -static void ufoma_start_read(struct usb2_com_softc *); -static void ufoma_stop_read(struct usb2_com_softc *); -static void ufoma_start_write(struct usb2_com_softc *); -static void ufoma_stop_write(struct usb2_com_softc *); - -/*sysctl stuff*/ -static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS); -static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS); -static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS); - - -static const struct usb2_config - ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { - - [UFOMA_CTRL_ENDPT_INTR] = { - .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, - }, - - [UFOMA_CTRL_ENDPT_READ] = { - .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 */ - }, - - [UFOMA_CTRL_ENDPT_WRITE] = { - .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] = { - - [UFOMA_BULK_ENDPT_WRITE] = { - .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, - }, - - [UFOMA_BULK_ENDPT_READ] = { - .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, - }, -}; - -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); - -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; - struct sysctl_ctx_list *sctx; - struct sysctl_oid *soid; - - usb2_mcpc_acm_descriptor *mad; - uint8_t elements; - int32_t error; - - 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_nobulk = 1; - } else { - sc->sc_nobulk = 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, if any */ - usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); - usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); - - 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; - } - /*Sysctls*/ - sctx = device_get_sysctl_ctx(dev); - soid = device_get_sysctl_tree(dev); - - SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode", - CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support, - "A", "Supporting port role"); - - SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode", - CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current, - "A", "Current port role"); - - SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode", - CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open, - "A", "Mode to transit when port is opened"); - SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit", - CTLFLAG_RD, &(sc->sc_ucom.sc_unit), 0, - "Unit number as USB serial"); - - 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_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]); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, sc->sc_modetable, 0, 1000); - - error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz); - - if (error) { - DPRINTF("NO response\n"); - } -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - - error = usb2_cv_timedwait(&sc->sc_cv, &Giant, - (UFOMA_MAX_TIMEOUT * hz)); - if (error) { - DPRINTF("No response\n"); - } -} - -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_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_nobulk)) { - DPRINTF("Wrong serial state!\n"); - break; - } - if (sc->sc_num_msg != 0xFF) { - sc->sc_num_msg++; - } - usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); - break; - - case UCDC_N_SERIAL_STATE: - if (sc->sc_nobulk) { - 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: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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); - } -} - -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); -} - -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_nobulk || - (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { - 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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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; -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -static void -ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) -{ - struct ufoma_softc *sc = ucom->sc_parent; - - if (sc->sc_nobulk) { - return; - } - if (onoff) - sc->sc_line |= UCDC_LINE_DTR; - else - sc->sc_line &= ~UCDC_LINE_DTR; - - ufoma_cfg_set_line_state(sc); -} - -static void -ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) -{ - struct ufoma_softc *sc = ucom->sc_parent; - - if (sc->sc_nobulk) { - return; - } - if (onoff) - sc->sc_line |= UCDC_LINE_RTS; - else - sc->sc_line &= ~UCDC_LINE_RTS; - - ufoma_cfg_set_line_state(sc); -} - -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_nobulk || - (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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, &ls, 0, 1000); -} - -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[UFOMA_CTRL_ENDPT_INTR]); - - /* start data transfer */ - if (sc->sc_nobulk) { - usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); - } else { - usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); - } -} - -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[UFOMA_CTRL_ENDPT_INTR]); - - /* stop data transfer */ - if (sc->sc_nobulk) { - usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); - } else { - usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); - } -} - -static void -ufoma_start_write(struct usb2_com_softc *ucom) -{ - struct ufoma_softc *sc = ucom->sc_parent; - - if (sc->sc_nobulk) { - usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); - } else { - usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); - } -} - -static void -ufoma_stop_write(struct usb2_com_softc *ucom) -{ - struct ufoma_softc *sc = ucom->sc_parent; - - if (sc->sc_nobulk) { - usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); - } else { - usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); - } -} - -struct umcpc_modetostr_tab{ - int mode; - char *str; -}umcpc_modetostr_tab[]={ - {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"}, - {UMCPC_ACM_MODE_MODEM, "modem"}, - {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"}, - {UMCPC_ACM_MODE_OBEX, "obex"}, - {UMCPC_ACM_MODE_VENDOR1, "vendor1"}, - {UMCPC_ACM_MODE_VENDOR2, "vendor2"}, - {UMCPC_ACM_MODE_UNLINKED, "unlinked"}, - {0, NULL} -}; - -static char *ufoma_mode_to_str(int mode) -{ - int i; - for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ - if(umcpc_modetostr_tab[i].mode == mode){ - return umcpc_modetostr_tab[i].str; - } - } - return NULL; -} - -static int ufoma_str_to_mode(char *str) -{ - int i; - for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ - if(strcmp(str, umcpc_modetostr_tab[i].str)==0){ - return umcpc_modetostr_tab[i].mode; - } - } - return -1; -} - -static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS) -{ - struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; - struct sbuf sb; - int i; - char *mode; - - sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND); - for(i = 1; i < sc->sc_modetable[0]; i++){ - mode = ufoma_mode_to_str(sc->sc_modetable[i]); - if(mode !=NULL){ - sbuf_cat(&sb, mode); - }else{ - sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]); - } - if(i < (sc->sc_modetable[0]-1)) - sbuf_cat(&sb, ","); - } - sbuf_trim(&sb); - sbuf_finish(&sb); - sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); - sbuf_delete(&sb); - - return 0; -} -static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS) -{ - struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; - char *mode; - char subbuf[]="(XXX)"; - mode = ufoma_mode_to_str(sc->sc_currentmode); - if(!mode){ - mode = subbuf; - snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode); - } - sysctl_handle_string(oidp, mode, strlen(mode), req); - - return 0; - -} -static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS) -{ - struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; - char *mode; - char subbuf[40]; - int newmode; - int error; - int i; - - mode = ufoma_mode_to_str(sc->sc_modetoactivate); - if(mode){ - strncpy(subbuf, mode, sizeof(subbuf)); - }else{ - snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate); - } - error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req); - if(error != 0 || req->newptr == NULL){ - return error; - } - - if((newmode = ufoma_str_to_mode(subbuf)) == -1){ - return EINVAL; - } - - for(i = 1 ; i < sc->sc_modetable[0] ; i++){ - if(sc->sc_modetable[i] == newmode){ - sc->sc_modetoactivate = newmode; - return 0; - } - } - - return EINVAL; -} diff --git a/sys/dev/usb2/serial/uftdi2.c b/sys/dev/usb2/serial/uftdi2.c deleted file mode 100644 index af211ed..0000000 --- a/sys/dev/usb2/serial/uftdi2.c +++ /dev/null @@ -1,784 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR uftdi_debug - -#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_IBUFSIZE 64 /* bytes, maximum number of bytes per - * frame */ -#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to - * do size encoding */ - -enum { - UFTDI_BULK_DT_WR, - UFTDI_BULK_DT_RD, - UFTDI_N_TRANSFER, -}; - -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_N_TRANSFER]; - 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_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_read_callback; - -static void uftdi_cfg_open(struct usb2_com_softc *); -static void uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t); -static int uftdi_set_parm_soft(struct termios *, - struct uftdi_param_config *, uint8_t); -static int uftdi_pre_param(struct usb2_com_softc *, struct termios *); -static void uftdi_cfg_param(struct usb2_com_softc *, struct termios *); -static void uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void uftdi_start_read(struct usb2_com_softc *); -static void uftdi_stop_read(struct usb2_com_softc *); -static void uftdi_start_write(struct usb2_com_softc *); -static void uftdi_stop_write(struct usb2_com_softc *); -static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *); - -static const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = { - - [UFTDI_BULK_DT_WR] = { - .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, - }, - - [UFTDI_BULK_DT_RD] = { - .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, - }, -}; - -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); - -static struct usb2_device_id uftdi_devs[] = { - {USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)}, - {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_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, 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; - - 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_N_TRANSFER, 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]); - - /* 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_N_TRANSFER); - - return (0); -} - -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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - - /* 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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - - /* - * 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 - */ -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -uftdi_read_callback(struct usb2_xfer *xfer) -{ - struct uftdi_softc *sc = xfer->priv_sc; - uint8_t buf[2]; - uint8_t ftdi_msr; - 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); - - ftdi_msr = FTDI_GET_MSR(buf); - lsr = FTDI_GET_LSR(buf); - - msr = 0; - if (ftdi_msr & FTDI_SIO_CTS_MASK) - msr |= SER_CTS; - if (ftdi_msr & FTDI_SIO_DSR_MASK) - msr |= SER_DSR; - if (ftdi_msr & FTDI_SIO_RI_MASK) - msr |= SER_RI; - if (ftdi_msr & FTDI_SIO_RLSD_MASK) - msr |= SER_DCD; - - 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: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - - 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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - - 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); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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; -} - -static void -uftdi_start_read(struct usb2_com_softc *ucom) -{ - struct uftdi_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]); -} - -static void -uftdi_stop_read(struct usb2_com_softc *ucom) -{ - struct uftdi_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]); -} - -static void -uftdi_start_write(struct usb2_com_softc *ucom) -{ - struct uftdi_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]); -} - -static void -uftdi_stop_write(struct usb2_com_softc *ucom) -{ - struct uftdi_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]); -} - -/*------------------------------------------------------------------------* - * 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 deleted file mode 100644 index 0074bc5..0000000 --- a/sys/dev/usb2/serial/uftdi2_reg.h +++ /dev/null @@ -1,340 +0,0 @@ -/* $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 deleted file mode 100644 index 248fc7e..0000000 --- a/sys/dev/usb2/serial/ugensa2.c +++ /dev/null @@ -1,352 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define UGENSA_BUF_SIZE 2048 /* bytes */ -#define UGENSA_CONFIG_INDEX 0 -#define UGENSA_IFACE_INDEX 0 -#define UGENSA_IFACE_MAX 8 /* exclusivly */ - -enum { - UGENSA_BULK_DT_WR, - UGENSA_BULK_DT_RD, - UGENSA_N_TRANSFER, -}; - -struct ugensa_sub_softc { - struct usb2_com_softc *sc_usb2_com_ptr; - struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER]; -}; - -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_read_callback; - -static void ugensa_start_read(struct usb2_com_softc *); -static void ugensa_stop_read(struct usb2_com_softc *); -static void ugensa_start_write(struct usb2_com_softc *); -static void ugensa_stop_write(struct usb2_com_softc *); - -static const struct usb2_config - ugensa_xfer_config[UGENSA_N_TRANSFER] = { - - [UGENSA_BULK_DT_WR] = { - .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, - }, - - [UGENSA_BULK_DT_RD] = { - .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, - }, -}; - -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); - -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_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 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; - - 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 */ - usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); - usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); - - /* 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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[UGENSA_BULK_DT_RD]); -} - -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[UGENSA_BULK_DT_RD]); -} - -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[UGENSA_BULK_DT_WR]); -} - -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[UGENSA_BULK_DT_WR]); -} diff --git a/sys/dev/usb2/serial/uipaq2.c b/sys/dev/usb2/serial/uipaq2.c deleted file mode 100644 index 68ba3fc..0000000 --- a/sys/dev/usb2/serial/uipaq2.c +++ /dev/null @@ -1,1314 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#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 - -enum { - UIPAQ_BULK_DT_WR, - UIPAQ_BULK_DT_RD, - UIPAQ_N_TRANSFER, -}; - -struct uipaq_softc { - struct usb2_com_super_softc sc_super_ucom; - struct usb2_com_softc sc_ucom; - - struct usb2_xfer *sc_xfer[UIPAQ_N_TRANSFER]; - struct usb2_device *sc_udev; - - uint16_t sc_line; - - uint8_t sc_lsr; /* local status register */ - uint8_t sc_msr; /* modem status register */ -}; - -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 void uipaq_start_read(struct usb2_com_softc *); -static void uipaq_stop_read(struct usb2_com_softc *); -static void uipaq_start_write(struct usb2_com_softc *); -static void uipaq_stop_write(struct usb2_com_softc *); -static void uipaq_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void uipaq_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void uipaq_cfg_set_break(struct usb2_com_softc *, uint8_t); - -static const struct usb2_config uipaq_config_data[UIPAQ_N_TRANSFER] = { - - [UIPAQ_BULK_DT_WR] = { - .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, - }, - - [UIPAQ_BULK_DT_RD] = { - .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, - }, -}; - -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); - -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; - - 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, hz / 10); - } - - iface_index = UIPAQ_IFACE_INDEX; - error = usb2_transfer_setup(uaa->device, &iface_index, - sc->sc_xfer, uipaq_config_data, - UIPAQ_N_TRANSFER, sc, &Giant); - - if (error) { - goto detach; - } - /* clear stall at first run */ - usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]); - - 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, UIPAQ_N_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[UIPAQ_BULK_DT_RD]); -} - -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[UIPAQ_BULK_DT_RD]); -} - -static void -uipaq_start_write(struct usb2_com_softc *ucom) -{ - struct uipaq_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]); -} - -static void -uipaq_stop_write(struct usb2_com_softc *ucom) -{ - struct uipaq_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} diff --git a/sys/dev/usb2/serial/ulpt2.c b/sys/dev/usb2/serial/ulpt2.c deleted file mode 100644 index 1a4d9a7..0000000 --- a/sys/dev/usb2/serial/ulpt2.c +++ /dev/null @@ -1,726 +0,0 @@ -#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 "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR ulpt_debug - -#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 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) - -enum { - ULPT_BULK_DT_WR, - ULPT_BULK_DT_RD, - ULPT_INTR_DT_RD, - ULPT_N_TRANSFER, -}; - -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_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_read_callback; -static usb2_callback_t ulpt_status_callback; - -static void ulpt_reset(struct ulpt_softc *); -static void ulpt_watchdog(void *); - -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); -} - -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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } -} - -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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - break; - } -} - -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; - } -} - -static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = { - [ULPT_BULK_DT_WR] = { - .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, - }, - - [ULPT_BULK_DT_RD] = { - .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, - }, - - [ULPT_INTR_DT_RD] = { - .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 */ - }, -}; - -static void -ulpt_start_read(struct usb2_fifo *fifo) -{ - struct ulpt_softc *sc = fifo->priv_sc0; - - usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]); -} - -static void -ulpt_stop_read(struct usb2_fifo *fifo) -{ - struct ulpt_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]); -} - -static void -ulpt_start_write(struct usb2_fifo *fifo) -{ - struct ulpt_softc *sc = fifo->priv_sc0; - - usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]); -} - -static void -ulpt_stop_write(struct usb2_fifo *fifo) -{ - struct ulpt_softc *sc = fifo->priv_sc0; - - usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]); -} - -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); - usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]); - mtx_unlock(&sc->sc_mtx); - if (usb2_fifo_alloc_buffer(fifo, - sc->sc_xfer[ULPT_BULK_DT_RD]->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); - usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]); - mtx_unlock(&sc->sc_mtx); - if (usb2_fifo_alloc_buffer(fifo, - sc->sc_xfer[ULPT_BULK_DT_WR]->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); - } -} - -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, 0); - - /* 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); - mtx_unlock(&sc->sc_mtx); - 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[ULPT_INTR_DT_RD]); - - usb2_callout_reset(&sc->sc_watchdog, - hz, &ulpt_watchdog, sc); -} - -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 deleted file mode 100644 index f5d13d9..0000000 --- a/sys/dev/usb2/serial/umct2.c +++ /dev/null @@ -1,579 +0,0 @@ -#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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_debug - -#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 - -enum { - UMCT_BULK_DT_WR, - UMCT_BULK_DT_RD, - UMCT_INTR_DT_RD, - UMCT_N_TRANSFER, -}; - -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_N_TRANSFER]; - - 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_iface_no; - uint8_t sc_name[16]; -}; - -/* 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_callback; -static usb2_callback_t umct_write_callback; -static usb2_callback_t umct_read_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 *, uint8_t *, - uint8_t *); -static void umct_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void umct_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void umct_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static uint8_t umct_calc_baud(uint32_t); -static int umct_pre_param(struct usb2_com_softc *, struct termios *); -static void umct_cfg_param(struct usb2_com_softc *, struct termios *); -static void umct_start_read(struct usb2_com_softc *); -static void umct_stop_read(struct usb2_com_softc *); -static void umct_start_write(struct usb2_com_softc *); -static void umct_stop_write(struct usb2_com_softc *); - -static const struct usb2_config umct_config[UMCT_N_TRANSFER] = { - - [UMCT_BULK_DT_WR] = { - .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, - }, - - [UMCT_BULK_DT_RD] = { - .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 */ - }, - - [UMCT_INTR_DT_RD] = { - .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 */ - }, -}; - -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); - -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; - - 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_N_TRANSFER, 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[UMCT_BULK_DT_RD]->pipe->edesc->wMaxPacketSize); - if (maxp == 0x2) { - - /* guessed wrong - switch around endpoints */ - - struct usb2_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD]; - - sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD]; - sc->sc_xfer[UMCT_BULK_DT_RD] = temp; - - sc->sc_xfer[UMCT_BULK_DT_RD]->callback = &umct_read_callback; - sc->sc_xfer[UMCT_INTR_DT_RD]->callback = &umct_intr_callback; - } - sc->sc_obufsize = sc->sc_xfer[UMCT_BULK_DT_WR]->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_N_TRANSFER); - - 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 (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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, temp, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } - 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: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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; -} - -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); -} - -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); -} - -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); -} - -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); -} - -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[UMCT_INTR_DT_RD]); - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]); -} - -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[UMCT_INTR_DT_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]); -} - -static void -umct_start_write(struct usb2_com_softc *ucom) -{ - struct umct_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]); -} - -static void -umct_stop_write(struct usb2_com_softc *ucom) -{ - struct umct_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]); -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} diff --git a/sys/dev/usb2/serial/umodem2.c b/sys/dev/usb2/serial/umodem2.c deleted file mode 100644 index cfb9715..0000000 --- a/sys/dev/usb2/serial/umodem2.c +++ /dev/null @@ -1,788 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR umodem_debug - -#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 - -enum { - UMODEM_BULK_WR, - UMODEM_BULK_RD, - UMODEM_INTR_RD, - UMODEM_N_TRANSFER, -}; - -#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[UMODEM_N_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_data_iface_no; - uint8_t sc_iface_index[2]; - uint8_t sc_cm_over_data; - uint8_t sc_cm_cap; /* CM capabilities */ - uint8_t sc_acm_cap; /* ACM capabilities */ -}; - -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_write_callback; -static usb2_callback_t umodem_read_callback; - -static void umodem_start_read(struct usb2_com_softc *); -static void umodem_stop_read(struct usb2_com_softc *); -static void umodem_start_write(struct usb2_com_softc *); -static void umodem_stop_write(struct usb2_com_softc *); -static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *); -static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static int umodem_pre_param(struct usb2_com_softc *, struct termios *); -static void umodem_cfg_param(struct usb2_com_softc *, struct termios *); -static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, - struct thread *); -static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t); -static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t, - uint16_t, uint16_t); - -static const struct usb2_config umodem_config[UMODEM_N_TRANSFER] = { - - [UMODEM_BULK_WR] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .if_index = 0, - .mh.bufsize = UMODEM_BUF_SIZE, - .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, - .mh.callback = &umodem_write_callback, - }, - - [UMODEM_BULK_RD] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 0, - .mh.bufsize = UMODEM_BUF_SIZE, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.callback = &umodem_read_callback, - }, - - [UMODEM_INTR_RD] = { - .type = UE_INTERRUPT, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .if_index = 1, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, - .mh.bufsize = 0, /* use wMaxPacketSize */ - .mh.callback = &umodem_intr_callback, - }, -}; - -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_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; - - device_set_usb2_desc(dev); - - sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; - sc->sc_iface_index[1] = 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_iface_index[0] = 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_iface_index, sc->sc_xfer, - umodem_config, UMODEM_N_TRANSFER, - sc, &Giant); - if (error) { - goto detach; - } - - /* clear stall at first run */ - usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); - - 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; - - /* start interrupt endpoint, if any */ - usb2_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); -} - -static void -umodem_stop_read(struct usb2_com_softc *ucom) -{ - struct umodem_softc *sc = ucom->sc_parent; - - /* stop interrupt endpoint, if any */ - usb2_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); -} - -static void -umodem_start_write(struct usb2_com_softc *ucom) -{ - struct umodem_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); -} - -static void -umodem_stop_write(struct usb2_com_softc *ucom) -{ - struct umodem_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); -} - -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; -} - -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; -} - -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)); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, &ls, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - } -} - -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: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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, UMODEM_N_TRANSFER); - - return (0); -} diff --git a/sys/dev/usb2/serial/umoscom2.c b/sys/dev/usb2/serial/umoscom2.c deleted file mode 100644 index 9d09527..0000000 --- a/sys/dev/usb2/serial/umoscom2.c +++ /dev/null @@ -1,672 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR umoscom_debug - -#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_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 - -enum { - UMOSCOM_BULK_DT_WR, - UMOSCOM_BULK_DT_RD, - UMOSCOM_INTR_DT_RD, - UMOSCOM_N_TRANSFER, -}; - -struct umoscom_softc { - struct usb2_com_super_softc sc_super_ucom; - struct usb2_com_softc sc_ucom; - - struct usb2_xfer *sc_xfer[UMOSCOM_N_TRANSFER]; - struct usb2_device *sc_udev; - - uint8_t sc_mcr; - uint8_t sc_lcr; -}; - -/* 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_read_callback; -static usb2_callback_t umoscom_intr_callback; - -static void umoscom_cfg_open(struct usb2_com_softc *); -static void umoscom_cfg_close(struct usb2_com_softc *); -static void umoscom_cfg_set_break(struct usb2_com_softc *, uint8_t); -static void umoscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void umoscom_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static int umoscom_pre_param(struct usb2_com_softc *, struct termios *); -static void umoscom_cfg_param(struct usb2_com_softc *, struct termios *); -static void umoscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t); -static uint8_t umoscom_cfg_read(struct umoscom_softc *, uint16_t); -static void umoscom_start_read(struct usb2_com_softc *); -static void umoscom_stop_read(struct usb2_com_softc *); -static void umoscom_start_write(struct usb2_com_softc *); -static void umoscom_stop_write(struct usb2_com_softc *); - -static const struct usb2_config umoscom_config_data[UMOSCOM_N_TRANSFER] = { - - [UMOSCOM_BULK_DT_WR] = { - .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, - }, - - [UMOSCOM_BULK_DT_RD] = { - .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, - }, - - [UMOSCOM_INTR_DT_RD] = { - .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, - }, -}; - -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); - -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; - - 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, umoscom_config_data, - UMOSCOM_N_TRANSFER, sc, &Giant); - - if (error) { - goto detach; - } - /* clear stall at first run */ - usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); - - 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, UMOSCOM_N_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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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; -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, &val, 0, 1000); - - DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val); - - return (val); -} - -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[UMOSCOM_INTR_DT_RD]); -#endif - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); -} - -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[UMOSCOM_INTR_DT_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); -} - -static void -umoscom_start_write(struct usb2_com_softc *ucom) -{ - struct umoscom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); -} - -static void -umoscom_stop_write(struct usb2_com_softc *ucom) -{ - struct umoscom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); -} - -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: -tr_setup: - DPRINTF("\n"); - - 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"); - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - DPRINTF("\n"); - - 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"); - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: - 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"); - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} diff --git a/sys/dev/usb2/serial/uplcom2.c b/sys/dev/usb2/serial/uplcom2.c deleted file mode 100644 index b55edfd..0000000 --- a/sys/dev/usb2/serial/uplcom2.c +++ /dev/null @@ -1,831 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR uplcom_debug - -#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_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 - -enum { - UPLCOM_BULK_DT_WR, - UPLCOM_BULK_DT_RD, - UPLCOM_INTR_DT_RD, - UPLCOM_N_TRANSFER, -}; - -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_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 *, struct usb2_device *); -static int uplcom_pl2303x_init(struct usb2_device *); -static void uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t); -static int uplcom_pre_param(struct usb2_com_softc *, struct termios *); -static void uplcom_cfg_param(struct usb2_com_softc *, struct termios *); -static void uplcom_start_read(struct usb2_com_softc *); -static void uplcom_stop_read(struct usb2_com_softc *); -static void uplcom_start_write(struct usb2_com_softc *); -static void uplcom_stop_write(struct usb2_com_softc *); -static void uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); - -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_write_callback; -static usb2_callback_t uplcom_read_callback; - -static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = { - - [UPLCOM_BULK_DT_WR] = { - .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, - }, - - [UPLCOM_BULK_DT_RD] = { - .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, - }, - - [UPLCOM_INTR_DT_RD] = { - .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, - }, -}; - -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_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_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"); - - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]); - - 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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); -} - -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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, &ls, 0, 1000); - - 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); - - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - } else { - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = UPLCOM_SET_REQUEST; - USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - } -} - -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[UPLCOM_INTR_DT_RD]); - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]); -} - -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[UPLCOM_INTR_DT_RD]); - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]); -} - -static void -uplcom_start_write(struct usb2_com_softc *ucom) -{ - struct uplcom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]); -} - -static void -uplcom_stop_write(struct usb2_com_softc *ucom) -{ - struct uplcom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]); -} - -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; -} - -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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} diff --git a/sys/dev/usb2/serial/usb2_serial.c b/sys/dev/usb2/serial/usb2_serial.c deleted file mode 100644 index 1036550..0000000 --- a/sys/dev/usb2/serial/usb2_serial.c +++ /dev/null @@ -1,1127 +0,0 @@ -/* $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. - */ - -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR usb2_com_debug - -#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 - -static usb2_proc_callback_t usb2_com_cfg_start_transfers; -static usb2_proc_callback_t usb2_com_cfg_open; -static usb2_proc_callback_t usb2_com_cfg_close; -static usb2_proc_callback_t usb2_com_cfg_line_state; -static usb2_proc_callback_t usb2_com_cfg_status_change; -static usb2_proc_callback_t usb2_com_cfg_param; - -static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *); -static void usb2_com_units_free(uint32_t, uint32_t); -static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t); -static void usb2_com_detach_tty(struct usb2_com_softc *); -static void usb2_com_queue_command(struct usb2_com_softc *, - usb2_proc_callback_t *, struct termios *pt, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1); -static void usb2_com_shutdown(struct usb2_com_softc *); -static void usb2_com_break(struct usb2_com_softc *, uint8_t); -static void usb2_com_dtr(struct usb2_com_softc *, uint8_t); -static void usb2_com_rts(struct usb2_com_softc *, uint8_t); - -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_outwakeup; -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_outwakeup, - .tsw_ioctl = usb2_com_ioctl, - .tsw_param = usb2_com_param, - .tsw_modem = usb2_com_modem, - .tsw_free = usb2_com_free, -}; - -MODULE_DEPEND(usb2_serial, usb2_core, 1, 1, 1); -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); -} - -/* - * "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 "mtx" is applied before all - * callbacks are called back. Also "mtx" must be applied - * before calling into the ucom-layer! - */ -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 *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); - } - - /* XXX unit management does not really belong here */ - if (usb2_com_units_alloc(sub_units, &root_unit)) { - return (ENOMEM); - } - - error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); - if (error) { - usb2_com_units_free(root_unit, sub_units); - return (error); - } - - 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_mtx = mtx; - sc->sc_parent = parent; - sc->sc_callback = callback; - - error = usb2_com_attach_tty(sc, sub_units); - if (error) { - usb2_com_detach(ssc, sc - n, n); - usb2_com_units_free(root_unit + n, sub_units - n); - return (error); - } - sc->sc_flag |= UCOM_FLAG_ATTACHED; - } - return (0); -} - -/* - * 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_proc_drain(&ssc->sc_tq); - - for (n = 0; n != sub_units; n++, sc++) { - if (sc->sc_flag & UCOM_FLAG_ATTACHED) { - - usb2_com_detach_tty(sc); - - usb2_com_units_free(sc->sc_unit, 1); - - /* avoid duplicate detach: */ - sc->sc_flag &= ~UCOM_FLAG_ATTACHED; - } - } - usb2_proc_free(&ssc->sc_tq); -} - -static int -usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units) -{ - struct tty *tp; - int error = 0; - char buf[32]; /* temporary TTY device name buffer */ - - tp = tty_alloc(&usb2_com_class, sc, sc->sc_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 (sub_units > 1) { - /* multiple modems in one */ - if (snprintf(buf, sizeof(buf), "U%u.%u", - sc->sc_unit - sc->sc_local_unit, - sc->sc_local_unit)) { - /* ignore */ - } - } else { - /* single modem */ - 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_tty(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_mtx); - sc->sc_flag |= UCOM_FLAG_GONE; - sc->sc_flag &= ~(UCOM_FLAG_HL_READY | - UCOM_FLAG_LL_READY); - mtx_unlock(sc->sc_mtx); - if (tp) { - tty_lock(tp); - - usb2_com_close(tp); /* close, if any */ - - tty_rel_gone(tp); - - mtx_lock(sc->sc_mtx); - /* Wait for the callback after the TTY is torn down */ - while (sc->sc_ttyfreed == 0) - usb2_cv_wait(&sc->sc_cv, sc->sc_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_mtx); - } - usb2_cv_destroy(&sc->sc_cv); -} - -static void -usb2_com_queue_command(struct usb2_com_softc *sc, - usb2_proc_callback_t *fn, struct termios *pt, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) -{ - struct usb2_com_super_softc *ssc = sc->sc_super; - struct usb2_com_param_task *task; - - mtx_assert(sc->sc_mtx, MA_OWNED); - - if (usb2_proc_is_gone(&ssc->sc_tq)) { - DPRINTF("proc is gone\n"); - return; /* nothing to do */ - } - /* - * NOTE: The task cannot get executed before we drop the - * "sc_mtx" mutex. It is safe to update fields in the message - * structure after that the message got queued. - */ - task = (struct usb2_com_param_task *) - usb2_proc_msignal(&ssc->sc_tq, t0, t1); - - /* Setup callback and softc pointers */ - task->hdr.pm_callback = fn; - task->sc = sc; - - /* - * Make a copy of the termios. This field is only present if - * the "pt" field is not NULL. - */ - if (pt != NULL) - task->termios_copy = *pt; - - /* - * Closing the device should be synchronous. - */ - if (fn == usb2_com_cfg_close) - usb2_proc_mwait(&ssc->sc_tq, t0, t1); - -} - -static void -usb2_com_shutdown(struct usb2_com_softc *sc) -{ - struct tty *tp = sc->sc_tty; - - mtx_assert(sc->sc_mtx, MA_OWNED); - - DPRINTF("\n"); - - /* - * Hang up if necessary: - */ - if (tp->t_termios.c_cflag & HUPCL) { - usb2_com_modem(tp, 0, SER_DTR); - } -} - -/* - * Return values: - * 0: normal - * else: taskqueue is draining or gone - */ -uint8_t -usb2_com_cfg_is_gone(struct usb2_com_softc *sc) -{ - struct usb2_com_super_softc *ssc = sc->sc_super; - - return (usb2_proc_is_gone(&ssc->sc_tq)); -} - -static void -usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task) -{ - struct usb2_com_cfg_task *task = - (struct usb2_com_cfg_task *)_task; - struct usb2_com_softc *sc = task->sc; - - 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); - } -} - -static void -usb2_com_start_transfers(struct usb2_com_softc *sc) -{ - if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { - return; - } - /* - * Make sure that data transfers are started in both - * directions: - */ - 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); - } -} - -static void -usb2_com_cfg_open(struct usb2_proc_msg *_task) -{ - struct usb2_com_cfg_task *task = - (struct usb2_com_cfg_task *)_task; - struct usb2_com_softc *sc = task->sc; - - 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_pause_mtx(sc->sc_mtx, hz / 10); - } - } -} - -static int -usb2_com_open(struct tty *tp) -{ - struct usb2_com_softc *sc = tty_softc(tp); - int error; - - mtx_assert(sc->sc_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; - - /* reset programmed line state */ - sc->sc_pls_curr = 0; - sc->sc_pls_set = 0; - sc->sc_pls_clr = 0; - - usb2_com_queue_command(sc, usb2_com_cfg_open, NULL, - &sc->sc_open_task[0].hdr, - &sc->sc_open_task[1].hdr); - - /* Queue transfer enable command last */ - usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, - &sc->sc_start_task[0].hdr, - &sc->sc_start_task[1].hdr); - - 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_proc_msg *_task) -{ - struct usb2_com_cfg_task *task = - (struct usb2_com_cfg_task *)_task; - struct usb2_com_softc *sc = task->sc; - - 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 */ - } -} - -static void -usb2_com_close(struct tty *tp) -{ - struct usb2_com_softc *sc = tty_softc(tp); - - mtx_assert(sc->sc_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, NULL, - &sc->sc_close_task[0].hdr, - &sc->sc_close_task[1].hdr); - - 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); - } -} - -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_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_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_line_state(struct usb2_proc_msg *_task) -{ - struct usb2_com_cfg_task *task = - (struct usb2_com_cfg_task *)_task; - struct usb2_com_softc *sc = task->sc; - uint8_t notch_bits; - uint8_t any_bits; - uint8_t prev_value; - uint8_t last_value; - uint8_t mask; - - if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { - return; - } - - mask = 0; - /* compute callback mask */ - if (sc->sc_callback->usb2_com_cfg_set_dtr) - mask |= UCOM_LS_DTR; - if (sc->sc_callback->usb2_com_cfg_set_rts) - mask |= UCOM_LS_RTS; - if (sc->sc_callback->usb2_com_cfg_set_break) - mask |= UCOM_LS_BREAK; - - /* compute the bits we are to program */ - notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; - any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; - prev_value = sc->sc_pls_curr ^ notch_bits; - last_value = sc->sc_pls_curr; - - /* reset programmed line state */ - sc->sc_pls_curr = 0; - sc->sc_pls_set = 0; - sc->sc_pls_clr = 0; - - /* ensure that we don't loose any levels */ - if (notch_bits & UCOM_LS_DTR) - sc->sc_callback->usb2_com_cfg_set_dtr(sc, - (prev_value & UCOM_LS_DTR) ? 1 : 0); - if (notch_bits & UCOM_LS_RTS) - sc->sc_callback->usb2_com_cfg_set_rts(sc, - (prev_value & UCOM_LS_RTS) ? 1 : 0); - if (notch_bits & UCOM_LS_BREAK) - sc->sc_callback->usb2_com_cfg_set_break(sc, - (prev_value & UCOM_LS_BREAK) ? 1 : 0); - - /* set last value */ - if (any_bits & UCOM_LS_DTR) - sc->sc_callback->usb2_com_cfg_set_dtr(sc, - (last_value & UCOM_LS_DTR) ? 1 : 0); - if (any_bits & UCOM_LS_RTS) - sc->sc_callback->usb2_com_cfg_set_rts(sc, - (last_value & UCOM_LS_RTS) ? 1 : 0); - if (any_bits & UCOM_LS_BREAK) - sc->sc_callback->usb2_com_cfg_set_break(sc, - (last_value & UCOM_LS_BREAK) ? 1 : 0); -} - -static void -usb2_com_line_state(struct usb2_com_softc *sc, - uint8_t set_bits, uint8_t clear_bits) -{ - mtx_assert(sc->sc_mtx, MA_OWNED); - - if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { - return; - } - - DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); - - /* update current programmed line state */ - sc->sc_pls_curr |= set_bits; - sc->sc_pls_curr &= ~clear_bits; - sc->sc_pls_set |= set_bits; - sc->sc_pls_clr |= clear_bits; - - /* defer driver programming */ - usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL, - &sc->sc_line_state_task[0].hdr, - &sc->sc_line_state_task[1].hdr); -} - -static void -usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff) -{ - DPRINTF("onoff = %d\n", onoff); - - if (onoff) - usb2_com_line_state(sc, UCOM_LS_BREAK, 0); - else - usb2_com_line_state(sc, 0, UCOM_LS_BREAK); -} - -static void -usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff) -{ - DPRINTF("onoff = %d\n", onoff); - - if (onoff) - usb2_com_line_state(sc, UCOM_LS_DTR, 0); - else - usb2_com_line_state(sc, 0, UCOM_LS_DTR); -} - -static void -usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff) -{ - DPRINTF("onoff = %d\n", onoff); - - if (onoff) - usb2_com_line_state(sc, UCOM_LS_RTS, 0); - else - usb2_com_line_state(sc, 0, UCOM_LS_RTS); -} - -static void -usb2_com_cfg_status_change(struct usb2_proc_msg *_task) -{ - struct usb2_com_cfg_task *task = - (struct usb2_com_cfg_task *)_task; - struct usb2_com_softc *sc = task->sc; - struct tty *tp; - uint8_t new_msr; - uint8_t new_lsr; - uint8_t onoff; - - tp = sc->sc_tty; - - mtx_assert(sc->sc_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); - } -} - -void -usb2_com_status_change(struct usb2_com_softc *sc) -{ - mtx_assert(sc->sc_mtx, MA_OWNED); - - if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { - return; - } - DPRINTF("\n"); - - usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL, - &sc->sc_status_task[0].hdr, - &sc->sc_status_task[1].hdr); -} - -static void -usb2_com_cfg_param(struct usb2_proc_msg *_task) -{ - struct usb2_com_param_task *task = - (struct usb2_com_param_task *)_task; - struct usb2_com_softc *sc = task->sc; - - if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { - return; - } - if (sc->sc_callback->usb2_com_cfg_param == NULL) { - return; - } - - (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy); - - /* wait a little */ - usb2_pause_mtx(sc->sc_mtx, hz / 10); -} - -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_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; - } - } - - /* Disable transfers */ - sc->sc_flag &= ~UCOM_FLAG_GP_DATA; - - /* Queue baud rate programming command first */ - usb2_com_queue_command(sc, usb2_com_cfg_param, t, - &sc->sc_param_task[0].hdr, - &sc->sc_param_task[1].hdr); - - /* Queue transfer enable command last */ - usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, - &sc->sc_start_task[0].hdr, - &sc->sc_start_task[1].hdr); - - 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_outwakeup(struct tty *tp) -{ - struct usb2_com_softc *sc = tty_softc(tp); - - mtx_assert(sc->sc_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); -} - -/*------------------------------------------------------------------------* - * 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_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_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); -} - -static void -usb2_com_free(void *xsc) -{ - struct usb2_com_softc *sc = xsc; - - mtx_lock(sc->sc_mtx); - sc->sc_ttyfreed = 1; - usb2_cv_signal(&sc->sc_cv); - mtx_unlock(sc->sc_mtx); -} diff --git a/sys/dev/usb2/serial/usb2_serial.h b/sys/dev/usb2/serial/usb2_serial.h deleted file mode 100644 index c7d57a0..0000000 --- a/sys/dev/usb2/serial/usb2_serial.h +++ /dev/null @@ -1,198 +0,0 @@ -/* $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_device; -struct usb2_com_softc; -struct usb2_device_request; -struct thread; - -/* - * 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_cfg_task { - struct usb2_proc_msg hdr; - struct usb2_com_softc *sc; -}; - -struct usb2_com_param_task { - struct usb2_proc_msg hdr; - struct usb2_com_softc *sc; - struct termios termios_copy; -}; - -struct usb2_com_super_softc { - struct usb2_process sc_tq; -}; - -struct usb2_com_softc { - /* - * NOTE: To avoid loosing level change information we use two - * tasks instead of one for all commands. - * - * Level changes are transitions like: - * - * ON->OFF - * OFF->ON - * OPEN->CLOSE - * CLOSE->OPEN - */ - struct usb2_com_cfg_task sc_start_task[2]; - struct usb2_com_cfg_task sc_open_task[2]; - struct usb2_com_cfg_task sc_close_task[2]; - struct usb2_com_cfg_task sc_line_state_task[2]; - struct usb2_com_cfg_task sc_status_task[2]; - struct usb2_com_param_task sc_param_task[2]; - 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_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 */ - /* programmed line state bits */ - uint8_t sc_pls_set; /* set bits */ - uint8_t sc_pls_clr; /* cleared bits */ - uint8_t sc_pls_curr; /* last state */ -#define UCOM_LS_DTR 0x01 -#define UCOM_LS_RTS 0x02 -#define UCOM_LS_BREAK 0x04 -}; - -#define usb2_com_cfg_do_request(udev,com,req,ptr,flags,timo) \ - usb2_do_request_proc(udev,&(com)->sc_super->sc_tq,req,ptr,flags,NULL,timo) - -int usb2_com_attach(struct usb2_com_super_softc *, - struct usb2_com_softc *, uint32_t, void *, - const struct usb2_com_callback *callback, struct mtx *); -void usb2_com_detach(struct usb2_com_super_softc *, - struct usb2_com_softc *, uint32_t); -void usb2_com_status_change(struct usb2_com_softc *); -uint8_t usb2_com_get_data(struct usb2_com_softc *, struct usb2_page_cache *, - uint32_t, uint32_t, uint32_t *); -void usb2_com_put_data(struct usb2_com_softc *, struct usb2_page_cache *, - uint32_t, uint32_t); -uint8_t usb2_com_cfg_is_gone(struct usb2_com_softc *); -#endif /* _USB2_SERIAL_H_ */ diff --git a/sys/dev/usb2/serial/uslcom2.c b/sys/dev/usb2/serial/uslcom2.c deleted file mode 100644 index 56b4a9a..0000000 --- a/sys/dev/usb2/serial/uslcom2.c +++ /dev/null @@ -1,539 +0,0 @@ -/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ - -#include -__FBSDID("$FreeBSD$"); - -/* - * 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 "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR uslcom_debug - -#include -#include -#include -#include -#include -#include -#include - -#include - -#if USB_DEBUG -static int uslcom_debug = 0; - -SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); -SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW, - &uslcom_debug, 0, "Debug level"); -#endif - -#define USLCOM_BULK_BUF_SIZE 1024 -#define USLCOM_CONFIG_INDEX 0 -#define USLCOM_IFACE_INDEX 0 - -#define USLCOM_SET_DATA_BITS(x) ((x) << 8) - -#define USLCOM_WRITE 0x41 -#define USLCOM_READ 0xc1 - -#define USLCOM_UART 0x00 -#define USLCOM_BAUD_RATE 0x01 -#define USLCOM_DATA 0x03 -#define USLCOM_BREAK 0x05 -#define USLCOM_CTRL 0x07 - -#define USLCOM_UART_DISABLE 0x00 -#define USLCOM_UART_ENABLE 0x01 - -#define USLCOM_CTRL_DTR_ON 0x0001 -#define USLCOM_CTRL_DTR_SET 0x0100 -#define USLCOM_CTRL_RTS_ON 0x0002 -#define USLCOM_CTRL_RTS_SET 0x0200 -#define USLCOM_CTRL_CTS 0x0010 -#define USLCOM_CTRL_DSR 0x0020 -#define USLCOM_CTRL_DCD 0x0080 - -#define USLCOM_BAUD_REF 0x384000 - -#define USLCOM_STOP_BITS_1 0x00 -#define USLCOM_STOP_BITS_2 0x02 - -#define USLCOM_PARITY_NONE 0x00 -#define USLCOM_PARITY_ODD 0x10 -#define USLCOM_PARITY_EVEN 0x20 - -#define USLCOM_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */ - -#define USLCOM_BREAK_OFF 0x00 -#define USLCOM_BREAK_ON 0x01 - -enum { - USLCOM_BULK_DT_WR, - USLCOM_BULK_DT_RD, - USLCOM_N_TRANSFER, -}; - -struct uslcom_softc { - struct usb2_com_super_softc sc_super_ucom; - struct usb2_com_softc sc_ucom; - - struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER]; - struct usb2_device *sc_udev; - - uint8_t sc_msr; - uint8_t sc_lsr; -}; - -static device_probe_t uslcom_probe; -static device_attach_t uslcom_attach; -static device_detach_t uslcom_detach; - -static usb2_callback_t uslcom_write_callback; -static usb2_callback_t uslcom_read_callback; - -static void uslcom_open(struct usb2_com_softc *); -static void uslcom_close(struct usb2_com_softc *); -static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t); -static void uslcom_set_rts(struct usb2_com_softc *, uint8_t); -static void uslcom_set_break(struct usb2_com_softc *, uint8_t); -static int uslcom_pre_param(struct usb2_com_softc *, struct termios *); -static void uslcom_param(struct usb2_com_softc *, struct termios *); -static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); -static void uslcom_start_read(struct usb2_com_softc *); -static void uslcom_stop_read(struct usb2_com_softc *); -static void uslcom_start_write(struct usb2_com_softc *); -static void uslcom_stop_write(struct usb2_com_softc *); - -static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = { - - [USLCOM_BULK_DT_WR] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = USLCOM_BULK_BUF_SIZE, - .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, - .mh.callback = &uslcom_write_callback, - }, - - [USLCOM_BULK_DT_RD] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = USLCOM_BULK_BUF_SIZE, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.callback = &uslcom_read_callback, - }, -}; - -struct usb2_com_callback uslcom_callback = { - .usb2_com_cfg_open = &uslcom_open, - .usb2_com_cfg_close = &uslcom_close, - .usb2_com_cfg_get_status = &uslcom_get_status, - .usb2_com_cfg_set_dtr = &uslcom_set_dtr, - .usb2_com_cfg_set_rts = &uslcom_set_rts, - .usb2_com_cfg_set_break = &uslcom_set_break, - .usb2_com_cfg_param = &uslcom_param, - .usb2_com_pre_param = &uslcom_pre_param, - .usb2_com_start_read = &uslcom_start_read, - .usb2_com_stop_read = &uslcom_stop_read, - .usb2_com_start_write = &uslcom_start_write, - .usb2_com_stop_write = &uslcom_stop_write, -}; - -static const struct usb2_device_id uslcom_devs[] = { - { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) }, - { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) }, - { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) }, - { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) }, - { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) }, - { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) }, -}; - -static device_method_t uslcom_methods[] = { - DEVMETHOD(device_probe, uslcom_probe), - DEVMETHOD(device_attach, uslcom_attach), - DEVMETHOD(device_detach, uslcom_detach), - {0, 0} -}; - -static devclass_t uslcom_devclass; - -static driver_t uslcom_driver = { - .name = "uslcom", - .methods = uslcom_methods, - .size = sizeof(struct uslcom_softc), -}; - -DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0); -MODULE_DEPEND(uslcom, usb2_serial, 1, 1, 1); -MODULE_DEPEND(uslcom, usb2_core, 1, 1, 1); -MODULE_VERSION(uslcom, 1); - -static int -uslcom_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 != USLCOM_CONFIG_INDEX) { - return (ENXIO); - } - if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) { - return (ENXIO); - } - return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); -} - -static int -uslcom_attach(device_t dev) -{ - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct uslcom_softc *sc = device_get_softc(dev); - int error; - - DPRINTFN(11, "\n"); - - device_set_usb2_desc(dev); - - sc->sc_udev = uaa->device; - - error = usb2_transfer_setup(uaa->device, - &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, - USLCOM_N_TRANSFER, sc, &Giant); - if (error) { - DPRINTF("one or more missing USB endpoints, " - "error=%s\n", usb2_errstr(error)); - goto detach; - } - /* clear stall at first run */ - usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); - - error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, - &uslcom_callback, &Giant); - if (error) { - goto detach; - } - return (0); - -detach: - uslcom_detach(dev); - return (ENXIO); -} - -static int -uslcom_detach(device_t dev) -{ - struct uslcom_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, USLCOM_N_TRANSFER); - - return (0); -} - -static void -uslcom_open(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_UART; - USETW(req.wValue, USLCOM_UART_ENABLE); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("UART enable failed (ignored)\n"); - } -} - -static void -uslcom_close(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_UART; - USETW(req.wValue, USLCOM_UART_DISABLE); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("UART disable failed (ignored)\n"); - } -} - -static void -uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - uint16_t ctl; - - DPRINTF("onoff = %d\n", onoff); - - ctl = onoff ? USLCOM_CTRL_DTR_ON : 0; - ctl |= USLCOM_CTRL_DTR_SET; - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_CTRL; - USETW(req.wValue, ctl); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("Setting DTR failed (ignored)\n"); - } -} - -static void -uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - uint16_t ctl; - - DPRINTF("onoff = %d\n", onoff); - - ctl = onoff ? USLCOM_CTRL_RTS_ON : 0; - ctl |= USLCOM_CTRL_RTS_SET; - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_CTRL; - USETW(req.wValue, ctl); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("Setting DTR failed (ignored)\n"); - } -} - -static int -uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) -{ - if (t->c_ospeed <= 0 || t->c_ospeed > 921600) - return (EINVAL); - return (0); -} - -static void -uslcom_param(struct usb2_com_softc *ucom, struct termios *t) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - uint16_t data; - - DPRINTF("\n"); - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_BAUD_RATE; - USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("Set baudrate failed (ignored)\n"); - } - - if (t->c_cflag & CSTOPB) - data = USLCOM_STOP_BITS_2; - else - data = USLCOM_STOP_BITS_1; - if (t->c_cflag & PARENB) { - if (t->c_cflag & PARODD) - data |= USLCOM_PARITY_ODD; - else - data |= USLCOM_PARITY_EVEN; - } else - data |= USLCOM_PARITY_NONE; - switch (t->c_cflag & CSIZE) { - case CS5: - data |= USLCOM_SET_DATA_BITS(5); - break; - case CS6: - data |= USLCOM_SET_DATA_BITS(6); - break; - case CS7: - data |= USLCOM_SET_DATA_BITS(7); - break; - case CS8: - data |= USLCOM_SET_DATA_BITS(8); - break; - } - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_DATA; - USETW(req.wValue, data); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("Set format failed (ignored)\n"); - } - return; -} - -static void -uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) -{ - struct uslcom_softc *sc = ucom->sc_parent; - - DPRINTF("\n"); - - *lsr = sc->sc_lsr; - *msr = sc->sc_msr; -} - -static void -uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff) -{ - struct uslcom_softc *sc = ucom->sc_parent; - struct usb2_device_request req; - uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF; - - req.bmRequestType = USLCOM_WRITE; - req.bRequest = USLCOM_BREAK; - USETW(req.wValue, brk); - USETW(req.wIndex, USLCOM_PORT_NO); - USETW(req.wLength, 0); - - if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000)) { - DPRINTF("Set BREAK failed (ignored)\n"); - } -} - -static void -uslcom_write_callback(struct usb2_xfer *xfer) -{ - struct uslcom_softc *sc = xfer->priv_sc; - uint32_t actlen; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_SETUP: - case USB_ST_TRANSFERRED: -tr_setup: - if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, - USLCOM_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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -uslcom_read_callback(struct usb2_xfer *xfer) -{ - struct uslcom_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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static void -uslcom_start_read(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - - /* start read endpoint */ - usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); -} - -static void -uslcom_stop_read(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - - /* stop read endpoint */ - usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); -} - -static void -uslcom_start_write(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); -} - -static void -uslcom_stop_write(struct usb2_com_softc *ucom) -{ - struct uslcom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); -} diff --git a/sys/dev/usb2/serial/uvisor2.c b/sys/dev/usb2/serial/uvisor2.c deleted file mode 100644 index a33623b..0000000 --- a/sys/dev/usb2/serial/uvisor2.c +++ /dev/null @@ -1,610 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR uvisor_debug - -#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_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; - -enum { - UVISOR_BULK_DT_WR, - UVISOR_BULK_DT_RD, - UVISOR_N_TRANSFER, -}; - -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 - - 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_read_callback; - -static usb2_error_t uvisor_init(struct uvisor_softc *, struct usb2_device *, - struct usb2_config *); -static void uvisor_cfg_open(struct usb2_com_softc *); -static void uvisor_cfg_close(struct usb2_com_softc *); -static void uvisor_start_read(struct usb2_com_softc *); -static void uvisor_stop_read(struct usb2_com_softc *); -static void uvisor_start_write(struct usb2_com_softc *); -static void uvisor_stop_write(struct usb2_com_softc *); - -static const struct usb2_config uvisor_config[UVISOR_N_TRANSFER] = { - - [UVISOR_BULK_DT_WR] = { - .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, - }, - - [UVISOR_BULK_DT_RD] = { - .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, - }, -}; - -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); - -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)); - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_RD]); - - 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, buffer, 0, 1000); - if (err) { - DPRINTFN(0, "close notification failed, error=%s\n", - usb2_errstr(err)); - } -} - -static void -uvisor_start_read(struct usb2_com_softc *ucom) -{ - struct uvisor_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]); -} - -static void -uvisor_stop_read(struct usb2_com_softc *ucom) -{ - struct uvisor_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]); -} - -static void -uvisor_start_write(struct usb2_com_softc *ucom) -{ - struct uvisor_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]); -} - -static void -uvisor_stop_write(struct usb2_com_softc *ucom) -{ - struct uvisor_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]); -} - -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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} diff --git a/sys/dev/usb2/serial/uvscom2.c b/sys/dev/usb2/serial/uvscom2.c deleted file mode 100644 index 1783904..0000000 --- a/sys/dev/usb2/serial/uvscom2.c +++ /dev/null @@ -1,707 +0,0 @@ -/* $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 "usbdevs.h" -#include -#include -#include -#include - -#define USB_DEBUG_VAR uvscom_debug - -#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 */ - -enum { - UVSCOM_BULK_DT_WR, - UVSCOM_BULK_DT_RD, - UVSCOM_INTR_DT_RD, - UVSCOM_N_TRANSFER, -}; - -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_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_read_callback; -static usb2_callback_t uvscom_intr_callback; - -static void uvscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); -static void uvscom_cfg_set_rts(struct usb2_com_softc *, uint8_t); -static void uvscom_cfg_set_break(struct usb2_com_softc *, uint8_t); -static int uvscom_pre_param(struct usb2_com_softc *, struct termios *); -static void uvscom_cfg_param(struct usb2_com_softc *, struct termios *); -static int uvscom_pre_open(struct usb2_com_softc *); -static void uvscom_cfg_open(struct usb2_com_softc *); -static void uvscom_cfg_close(struct usb2_com_softc *); -static void uvscom_start_read(struct usb2_com_softc *); -static void uvscom_stop_read(struct usb2_com_softc *); -static void uvscom_start_write(struct usb2_com_softc *); -static void uvscom_stop_write(struct usb2_com_softc *); -static void uvscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, - uint8_t *); -static void uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t); -static uint16_t uvscom_cfg_read_status(struct uvscom_softc *); - -static const struct usb2_config uvscom_config[UVSCOM_N_TRANSFER] = { - - [UVSCOM_BULK_DT_WR] = { - .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, - }, - - [UVSCOM_BULK_DT_RD] = { - .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, - }, - - [UVSCOM_INTR_DT_RD] = { - .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, - }, -}; - -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_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_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; - - 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 */ - usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]); - usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]); - - 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(&Giant); - usb2_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]); - mtx_unlock(&Giant); - - 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[UVSCOM_INTR_DT_RD]) { - usb2_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]); - } - 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: -tr_setup: - 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) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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: -tr_setup: - 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 */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - 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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -static void -uvscom_start_read(struct usb2_com_softc *ucom) -{ - struct uvscom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]); -} - -static void -uvscom_stop_read(struct usb2_com_softc *ucom) -{ - struct uvscom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]); -} - -static void -uvscom_start_write(struct usb2_com_softc *ucom) -{ - struct uvscom_softc *sc = ucom->sc_parent; - - usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]); -} - -static void -uvscom_stop_write(struct usb2_com_softc *ucom) -{ - struct uvscom_softc *sc = ucom->sc_parent; - - usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]); -} - -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; -} - -static void -uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value) -{ - struct usb2_device_request req; - usb2_error_t err; - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = index; - USETW(req.wValue, value); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - - err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, NULL, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } -} - -static uint16_t -uvscom_cfg_read_status(struct uvscom_softc *sc) -{ - struct usb2_device_request req; - usb2_error_t err; - uint8_t data[2]; - - 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_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, - &req, data, 0, 1000); - if (err) { - DPRINTFN(0, "device request failed, err=%s " - "(ignored)\n", usb2_errstr(err)); - } - return (data[0] | (data[1] << 8)); -} diff --git a/sys/dev/usb2/sound/uaudio2.c b/sys/dev/usb2/sound/uaudio2.c deleted file mode 100644 index 731e271..0000000 --- a/sys/dev/usb2/sound/uaudio2.c +++ /dev/null @@ -1,3751 +0,0 @@ -/* $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 "usbdevs.h" -#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" - -static int uaudio_default_rate = 96000; -static int uaudio_default_bits = 32; -static int uaudio_default_channels = 2; - -#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"); -SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, - &uaudio_default_rate, 0, "uaudio default sample rate"); -SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, - &uaudio_default_bits, 0, "uaudio default sample bits"); -SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, - &uaudio_default_channels, 0, "uaudio default sample channels"); -#endif - -#define UAUDIO_MINFRAMES 16 /* must be factor of 8 due HS-USB */ -#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ -#define UAUDIO_RECURSE_LIMIT 24 /* rounds */ - -#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 intr_size; /* in bytes */ - 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; - -static void uaudio_chan_fill_info_sub(struct uaudio_softc *, - struct usb2_device *, uint32_t, uint16_t, uint8_t, uint8_t); -static void uaudio_chan_fill_info(struct uaudio_softc *, - struct usb2_device *); -static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *, - struct uaudio_mixer_node *); -static void uaudio_mixer_add_ctl(struct uaudio_softc *, - struct uaudio_mixer_node *); -static void uaudio_mixer_add_input(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_output(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_mixer(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_selector(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static uint32_t uaudio_mixer_feature_get_bmaControls( - const struct usb2_audio_feature_unit *, uint8_t); -static void uaudio_mixer_add_feature(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_processing_updown(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_processing(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_extension(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static struct usb2_audio_cluster uaudio_mixer_get_cluster(uint8_t, - const struct uaudio_terminal_node *); -static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *, - struct uaudio_mixer_node *); -static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *, - struct uaudio_mixer_node *); -static const struct uaudio_terminal_node *uaudio_mixer_get_input( - const struct uaudio_terminal_node *, uint8_t); -static const struct uaudio_terminal_node *uaudio_mixer_get_output( - const struct uaudio_terminal_node *, uint8_t); -static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *, - const uint8_t *, uint8_t, struct uaudio_search_result *); -static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, - uint8_t, uint8_t, struct uaudio_search_result *); -static void uaudio_mixer_fill_info(struct uaudio_softc *, - struct usb2_device *, void *); -static uint16_t uaudio_mixer_get(struct usb2_device *, uint8_t, - struct uaudio_mixer_node *); -static void uaudio_mixer_ctl_set(struct uaudio_softc *, - struct uaudio_mixer_node *, uint8_t, int32_t val); -static usb2_error_t uaudio_set_speed(struct usb2_device *, uint8_t, uint32_t); -static int uaudio_mixer_signext(uint8_t, int); -static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val); -static const void *uaudio_mixer_verify_desc(const void *, uint32_t); -static void uaudio_mixer_init(struct uaudio_softc *); -static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t); -static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb2_fifo *); -static void umidi_start_read(struct usb2_fifo *); -static void umidi_stop_read(struct usb2_fifo *); -static void umidi_start_write(struct usb2_fifo *); -static void umidi_stop_write(struct usb2_fifo *); -static int umidi_open(struct usb2_fifo *, int, struct thread *); -static int umidi_ioctl(struct usb2_fifo *, u_long cmd, void *, int, struct thread *); -static void umidi_close(struct usb2_fifo *, int, struct thread *); -static void umidi_init(device_t dev); -static int32_t umidi_probe(device_t dev); -static int32_t umidi_detach(device_t dev); - -#if USB_DEBUG -static void uaudio_chan_dump_ep_desc( - const usb2_endpoint_descriptor_audio_t *); -static void uaudio_mixer_dump_cluster(uint8_t, - const struct uaudio_terminal_node *); -static const char *uaudio_mixer_get_terminal_name(uint16_t); -#endif - -static const struct usb2_config - uaudio_cfg_record[UAUDIO_NCHANBUFS] = { - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_MINFRAMES, - .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_MINFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &uaudio_chan_record_callback, - }, -}; - -static const struct usb2_config - uaudio_cfg_play[UAUDIO_NCHANBUFS] = { - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_MINFRAMES, - .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_MINFRAMES, - .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); - - if (uaa->use_generic == 0) - 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; - - 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]; - - 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"); - usb2_pause_mtx(NULL, 2 * hz); - 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); - } -} - -#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; - - if (ep_dir == UE_DIR_IN) - chan->usb2_cfg = - uaudio_cfg_record; - else - chan->usb2_cfg = - uaudio_cfg_play; - - sample_size = ((chan->p_asf1d->bNrChannels * - chan->p_asf1d->bBitResolution) / 8); - - /* - * NOTE: "chan->bytes_per_frame" - * should not be zero! - */ - 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; - } - } -} - -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_isoc_fps(udev); - uint8_t bits = uaudio_default_bits; - uint8_t y; - uint8_t channels = uaudio_default_channels; - uint8_t x; - - bits -= (bits % 8); - if ((bits == 0) || (bits > 32)) { - /* set a valid value */ - bits = 32; - } - rate -= (rate % fps); - if ((rate == 0) || (rate > 192000)) { - /* set a valid value */ - rate = 192000 - (192000 % fps); - } - if ((channels == 0) || (channels > 2)) { - /* set a valid value */ - channels = 2; - } - 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); - } -} - -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; - uint32_t blockcount; - uint32_t n; - uint32_t offset; - - /* allow dynamic sizing of play buffer */ - total = ch->intr_size; - - /* allow dynamic sizing of play buffer */ - blockcount = total / ch->bytes_per_frame; - - /* align units */ - blockcount -= (blockcount % UAUDIO_MINFRAMES); - - /* range check - min */ - if (blockcount == 0) { - blockcount = UAUDIO_MINFRAMES; - } - /* 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; - } -} - -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; - uint32_t blockcount; - uint32_t offset0; - uint32_t offset1; - - /* allow dynamic sizing of play buffer */ - total = ch->intr_size; - - /* allow dynamic sizing of play buffer */ - blockcount = total / ch->bytes_per_frame; - - /* align units */ - blockcount -= (blockcount % UAUDIO_MINFRAMES); - - /* range check - min */ - if (blockcount == 0) { - blockcount = UAUDIO_MINFRAMES; - } - /* 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); - uint32_t buf_size; - uint8_t endpoint; - uint8_t iface_index; - uint8_t alt_index; - usb2_error_t err; - - /* compute required buffer size */ - buf_size = (ch->bytes_per_frame * UAUDIO_MINFRAMES); - - /* setup interrupt interval */ - ch->intr_size = buf_size; - - /* double buffering */ - buf_size *= 2; - - ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); - if (ch->buf == NULL) { - goto error; - } - if (sndbuf_setup(b, ch->buf, buf_size) != 0) { - goto error; - } - ch->start = ch->buf; - ch->end = ch->buf + buf_size; - 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) -{ - /* we only support one size */ - blocksize = ch->intr_size; - blockcount = 2; - - if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) || - (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) { - DPRINTFN(1, "resizing to %u x " - "%u bytes\n", blockcount, blocksize); - 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"); - } -} - -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 -} - -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 -} - -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 -} - -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; - } -} - -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); -} - -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); - } - } -} - -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); -} - -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; - } -} - -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); - } -} - -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; - } -} - -#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--; -} - -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--); -} - -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" is allowed to be corrupt */ - wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength; - - /* get USB audio revision */ - 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); - } -} - -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]; - - DPRINTF("\n"); - - 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; - } - break; - - default: /* Error */ - DPRINTF("error=%s\n", usb2_errstr(xfer->error)); - if (xfer->error == USB_ERR_CANCELLED) { - /* do nothing - we are detaching */ - break; - } - 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) -{ - 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, "type=0x%03x val=%d min=%d max=%d val=%d\n", - mc->type, val, mc->minval, mc->maxval, 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]); -} - -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]; - } - } - } -} - -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); - } - } -} - -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); - } -} - -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); - } -} - -/* - * 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]); -} - -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"); - } -} - -static void -umidi_start_write(struct usb2_fifo *fifo) -{ - struct umidi_chan *chan = fifo->priv_sc0; - - usb2_transfer_start(chan->xfer[0]); -} - -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]); - } -} - -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); - } -} - - -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); -} - -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 deleted file mode 100644 index e763c6d..0000000 --- a/sys/dev/usb2/sound/uaudio2.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $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 deleted file mode 100644 index c073c19..0000000 --- a/sys/dev/usb2/sound/uaudio2_pcm.c +++ /dev/null @@ -1,234 +0,0 @@ -/* $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 deleted file mode 100644 index 2bf68a1..0000000 --- a/sys/dev/usb2/sound/uaudio2_reg.h +++ /dev/null @@ -1,406 +0,0 @@ -/* $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 deleted file mode 100644 index 0e6994b..0000000 --- a/sys/dev/usb2/sound/usb2_sound.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index 5b6ae15..0000000 --- a/sys/dev/usb2/sound/usb2_sound.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index a4d2da4..0000000 --- a/sys/dev/usb2/storage/ata-usb2.c +++ /dev/null @@ -1,1103 +0,0 @@ -/*- - * 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 "usbdevs.h" -#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; - - 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); - } -} - -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); -} - -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); -} - -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); -} - -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); -} - -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; - - /* drop the USB transfer lock while doing the ATA interrupt */ - mtx_unlock(&sc->locked_mtx); - - ata_interrupt(device_get_softc(request->parent)); - - mtx_lock(&sc->locked_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); - } -} - -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); -} - -/* - * 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; -} - -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 deleted file mode 100644 index ff83290..0000000 --- a/sys/dev/usb2/storage/umass2.c +++ /dev/null @@ -1,3620 +0,0 @@ -#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 "usbdevs.h" -#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_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_ALCOR, USB_PRODUCT_ALCOR_TRANSCEND, 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_MYSON, USB_PRODUCT_MYSON_STARREADER, RID_WILDCARD, - UMASS_PROTO_SCSI | UMASS_PROTO_BBB, - NO_SYNCHRONIZE_CACHE - }, - {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_SDS_HOTFIND_D, RID_WILDCARD, - UMASS_PROTO_SCSI | UMASS_PROTO_BBB, - NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE - }, - {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 - }, - {USB_VENDOR_MEIZU, USB_PRODUCT_MEIZU_M6_SL, RID_WILDCARD, - UMASS_PROTO_SCSI | UMASS_PROTO_BBB, - NO_INQUIRY | NO_SYNCHRONIZE_CACHE - }, - {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 *); -static void umass_init_shuttle(struct umass_softc *); -static void umass_reset(struct umass_softc *); -static void umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *, - uint8_t, uint8_t); -static void umass_command_start(struct umass_softc *, uint8_t, void *, - uint32_t, uint32_t, umass_callback_t *, union ccb *); -static uint8_t umass_bbb_get_max_lun(struct umass_softc *); -static void umass_cbi_start_status(struct umass_softc *); -static void umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *, - uint8_t, uint8_t); -static int umass_cam_attach_sim(struct umass_softc *); -static void umass_cam_rescan_callback(struct cam_periph *, union ccb *); -static void umass_cam_rescan(struct umass_softc *); -static void umass_cam_attach(struct umass_softc *); -static void umass_cam_detach_sim(struct umass_softc *); -static void umass_cam_action(struct cam_sim *, union ccb *); -static void umass_cam_poll(struct cam_sim *); -static void umass_cam_cb(struct umass_softc *, union ccb *, uint32_t, - uint8_t); -static void umass_cam_sense_cb(struct umass_softc *, union ccb *, uint32_t, - uint8_t); -static void umass_cam_quirk_cb(struct umass_softc *, union ccb *, uint32_t, - uint8_t); -static uint8_t umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t); -static uint8_t umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t); -static uint8_t umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t); -static uint8_t umass_atapi_transform(struct umass_softc *, uint8_t *, - uint8_t); -static uint8_t umass_no_transform(struct umass_softc *, uint8_t *, uint8_t); -static uint8_t umass_std_transform(struct umass_softc *, union ccb *, uint8_t - *, uint8_t); - -#if USB_DEBUG -static void umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *); -static void umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *); -static void umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t); -static void umass_dump_buffer(struct umass_softc *, uint8_t *, uint32_t, - uint32_t); -#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 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, NULL, 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; - - /* - * 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); - - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), - 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; - -#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, &sc->sc_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, - &sc->sc_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, 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(&sc->sc_mtx); -#endif - umass_cam_detach_sim(sc); - -#if (__FreeBSD_version >= 700037) - mtx_unlock(&sc->sc_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]); -} - -/* - * 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); - } -} - -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); -} - -static void -umass_cancel_ccb(struct umass_softc *sc) -{ - union ccb *ccb; - - mtx_assert(&sc->sc_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); - } -} - -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); -} - -/* - * 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); -} - -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); -} - -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); -} - -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); -} - -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); - } -} - -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); - } -} - -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); -} - -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); -} - -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); -} - -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; - - if (xfer->flags.ext_buffer) { - usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0); - } - 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); -} - -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); -} - -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 */ - - /* - * 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) - &sc->sc_mtx /* mutex */ , -#endif - 1 /* maximum device openings */ , - 0 /* maximum tagged device openings */ , - devq); - - if (sc->sc_sim == NULL) { - cam_simq_free(devq); - return (ENOMEM); - } - -#if (__FreeBSD_version >= 700037) - mtx_lock(&sc->sc_mtx); -#endif - -#if (__FreeBSD_version >= 700048) - if (xpt_bus_register(sc->sc_sim, sc->sc_dev, sc->sc_unit) != CAM_SUCCESS) { - mtx_unlock(&sc->sc_mtx); - return (ENOMEM); - } -#else - if (xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) { -#if (__FreeBSD_version >= 700037) - mtx_unlock(&sc->sc_mtx); -#endif - return (ENOMEM); - } -#endif - -#if (__FreeBSD_version >= 700037) - mtx_unlock(&sc->sc_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); -} - -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(&sc->sc_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(&sc->sc_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(&sc->sc_mtx); -#endif - - /* The scan is in progress now. */ -} - -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); - } -} - -/* umass_cam_detach - * detach from the CAM layer - */ - -static void -umass_cam_detach_sim(struct umass_softc *sc) -{ - if (sc->sc_sim != NULL) { - if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) { - /* accessing the softc is not possible after this */ - sc->sc_sim->softc = UMASS_GONE; - cam_sim_free(sc->sc_sim, /* free_devq */ TRUE); - } else { - panic("%s: CAM layer is busy!\n", - sc->sc_name); - } - sc->sc_sim = NULL; - } -} - -/* 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(&sc->sc_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(&sc->sc_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); -} - - -/* 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); - } -} - -/* - * 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); -} - -/* - * 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" : ""))); -} - -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" : "")))); -} - -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" : "")))); -} - -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); -} - -#endif diff --git a/sys/dev/usb2/storage/urio2.c b/sys/dev/usb2/storage/urio2.c deleted file mode 100644 index e592f77..0000000 --- a/sys/dev/usb2/storage/urio2.c +++ /dev/null @@ -1,480 +0,0 @@ -/*- - * 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 "usbdevs.h" -#include -#include -#include -#include -#include - -#define USB_DEBUG_VAR urio_debug - -#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; - - 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); - } -} - -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); - } -} - -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]); -} - -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]); -} - -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]); -} - -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]); -} - -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); - } -} - -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 deleted file mode 100644 index c8233ef..0000000 --- a/sys/dev/usb2/storage/usb2_storage.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index f40828a..0000000 --- a/sys/dev/usb2/storage/usb2_storage.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $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 deleted file mode 100644 index 2fb42a5..0000000 --- a/sys/dev/usb2/storage/ustorage2_fs.c +++ /dev/null @@ -1,1898 +0,0 @@ -/* $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 "usbdevs.h" -#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; - - /* - * 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]); - } -} - -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); -} - -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); - } -} - -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; - } -} - -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; - } -} - -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; - } -} - -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; - } -} - -/* 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) -{ -#if 0 - 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; - } -#endif - 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 deleted file mode 100644 index 3bd0d64..0000000 --- a/sys/dev/usb2/template/usb2_template.c +++ /dev/null @@ -1,1312 +0,0 @@ -/* $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 *, const uint8_t *); -static void usb2_make_endpoint_desc(struct usb2_temp_setup *, - const struct usb2_temp_endpoint_desc *); -static void usb2_make_interface_desc(struct usb2_temp_setup *, - const struct usb2_temp_interface_desc *); -static void usb2_make_config_desc(struct usb2_temp_setup *, - const struct usb2_temp_config_desc *); -static void usb2_make_device_desc(struct usb2_temp_setup *, - const struct usb2_temp_device_desc *); -static uint8_t usb2_hw_ep_match(const struct usb2_hw_ep_profile *, uint8_t, - uint8_t); -static uint8_t usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *, - struct usb2_hw_ep_scratch_sub *, uint8_t); -static uint8_t usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *, uint8_t, - uint8_t); -static usb2_error_t usb2_hw_ep_resolve(struct usb2_device *, - struct usb2_descriptor *); -static const struct usb2_temp_device_desc *usb2_temp_get_tdd(struct usb2_device *); -static void *usb2_temp_get_device_desc(struct usb2_device *); -static void *usb2_temp_get_qualifier_desc(struct usb2_device *); -static void *usb2_temp_get_config_desc(struct usb2_device *, uint16_t *, - uint8_t); -static const void *usb2_temp_get_string_desc(struct usb2_device *, uint16_t, - uint8_t); -static const void *usb2_temp_get_vendor_desc(struct usb2_device *, - const struct usb2_device_request *); -static const void *usb2_temp_get_hub_desc(struct usb2_device *); -static void usb2_temp_get_desc(struct usb2_device *, - struct usb2_device_request *, const void **, uint16_t *); -static usb2_error_t usb2_temp_setup(struct usb2_device *, - const struct usb2_temp_device_desc *); -static void usb2_temp_unsetup(struct usb2_device *); -static usb2_error_t usb2_temp_setup_by_index(struct usb2_device *, - uint16_t index); -static void usb2_temp_init(void *); - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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++; -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -/*------------------------------------------------------------------------* - * 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; - } - } -} - -/*------------------------------------------------------------------------* - * 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; - } - } -} - -/*------------------------------------------------------------------------* - * 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; -} - -/*------------------------------------------------------------------------* - * 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; - } -} - -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; -} - -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 deleted file mode 100644 index 361de3a..0000000 --- a/sys/dev/usb2/template/usb2_template.h +++ /dev/null @@ -1,102 +0,0 @@ -/* $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 deleted file mode 100644 index bc123df..0000000 --- a/sys/dev/usb2/template/usb2_template_cdce.c +++ /dev/null @@ -1,292 +0,0 @@ -#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_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, - -#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_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_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, - ð_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_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 deleted file mode 100644 index b8bc7ad..0000000 --- a/sys/dev/usb2/template/usb2_template_msc.c +++ /dev/null @@ -1,199 +0,0 @@ -#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 deleted file mode 100644 index f1599fc..0000000 --- a/sys/dev/usb2/template/usb2_template_mtp.c +++ /dev/null @@ -1,262 +0,0 @@ -#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 deleted file mode 100644 index 6b702fa..0000000 --- a/sys/dev/usb2/wlan/if_rum2.c +++ /dev/null @@ -1,2436 +0,0 @@ -/* $FreeBSD$ */ - -/*- - * 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -/*- - * Ralink Technology RT2501USB/RT2601USB chipset driver - * http://www.ralinktech.com.tw/ - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR rum_debug - -#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 - -#define rum_do_request(sc,req,data) \ - usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) - -static const struct usb2_device_id rum_devs[] = { - { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM) }, - { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2) }, - { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3) }, - { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4) }, - { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700) }, - { USB_VP(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO) }, - { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1) }, - { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2) }, - { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A) }, - { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3) }, - { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC) }, - { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR) }, - { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2) }, - { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL) }, - { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX) }, - { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F) }, - { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573) }, - { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1) }, - { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340) }, - { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111) }, - { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110) }, - { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS) }, - { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS) }, - { USB_VP(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573) }, - { USB_VP(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573) }, - { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB) }, - { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP) }, - { USB_VP(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4) }, - { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573) }, - { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP) }, - { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2) }, - { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM) }, - { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573) }, - { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671) }, - { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2) }, - { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172) }, - { USB_VP(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573) }, - { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573) }, -}; - -MODULE_DEPEND(rum, wlan, 1, 1, 1); -MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); -MODULE_DEPEND(rum, usb2_wlan, 1, 1, 1); -MODULE_DEPEND(rum, usb2_core, 1, 1, 1); - -static device_probe_t rum_match; -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_write_callback; - -static usb2_proc_callback_t rum_attach_post; -static usb2_proc_callback_t rum_task; -static usb2_proc_callback_t rum_scantask; -static usb2_proc_callback_t rum_promisctask; -static usb2_proc_callback_t rum_amrr_task; -static usb2_proc_callback_t rum_init_task; -static usb2_proc_callback_t rum_stop_task; - -static struct ieee80211vap *rum_vap_create(struct ieee80211com *, - const char name[IFNAMSIZ], int unit, int opmode, - int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void rum_vap_delete(struct ieee80211vap *); -static void rum_tx_free(struct rum_tx_data *, int); -static void rum_setup_tx_list(struct rum_softc *); -static void rum_unsetup_tx_list(struct rum_softc *); -static int rum_newstate(struct ieee80211vap *, - enum ieee80211_state, int); -static void rum_setup_tx_desc(struct rum_softc *, - struct rum_tx_desc *, uint32_t, uint16_t, int, - int); -static int rum_tx_mgt(struct rum_softc *, struct mbuf *, - struct ieee80211_node *); -static int rum_tx_raw(struct rum_softc *, struct mbuf *, - struct ieee80211_node *, - const struct ieee80211_bpf_params *); -static int rum_tx_data(struct rum_softc *, struct mbuf *, - struct ieee80211_node *); -static void rum_start(struct ifnet *); -static int rum_ioctl(struct ifnet *, u_long, caddr_t); -static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, - int); -static uint32_t rum_read(struct rum_softc *, uint16_t); -static void rum_read_multi(struct rum_softc *, uint16_t, void *, - int); -static void rum_write(struct rum_softc *, uint16_t, uint32_t); -static void rum_write_multi(struct rum_softc *, uint16_t, void *, - size_t); -static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); -static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); -static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); -static void rum_select_antenna(struct rum_softc *); -static void rum_enable_mrr(struct rum_softc *); -static void rum_set_txpreamble(struct rum_softc *); -static void rum_set_basicrates(struct rum_softc *); -static void rum_select_band(struct rum_softc *, - struct ieee80211_channel *); -static void rum_set_chan(struct rum_softc *, - struct ieee80211_channel *); -static void rum_enable_tsf_sync(struct rum_softc *); -static void rum_update_slot(struct ifnet *); -static void rum_set_bssid(struct rum_softc *, const uint8_t *); -static void rum_set_macaddr(struct rum_softc *, const uint8_t *); -static const char *rum_get_rf(int); -static void rum_read_eeprom(struct rum_softc *); -static int rum_bbp_init(struct rum_softc *); -static void rum_init(void *); -static int rum_load_microcode(struct rum_softc *, const u_char *, - size_t); -static int rum_prepare_beacon(struct rum_softc *, - struct ieee80211vap *); -static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, - const struct ieee80211_bpf_params *); -static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *, - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void rum_newassoc(struct ieee80211_node *, int); -static void rum_scan_start(struct ieee80211com *); -static void rum_scan_end(struct ieee80211com *); -static void rum_set_channel(struct ieee80211com *); -static int rum_get_rssi(struct rum_softc *, uint8_t); -static void rum_amrr_start(struct rum_softc *, - struct ieee80211_node *); -static void rum_amrr_timeout(void *); -static int rum_pause(struct rum_softc *, int); -static void rum_queue_command(struct rum_softc *, - usb2_proc_callback_t *, struct usb2_proc_msg *, - struct usb2_proc_msg *); - -static const struct { - uint32_t reg; - uint32_t val; -} rum_def_mac[] = { - { RT2573_TXRX_CSR0, 0x025fb032 }, - { RT2573_TXRX_CSR1, 0x9eaa9eaf }, - { RT2573_TXRX_CSR2, 0x8a8b8c8d }, - { RT2573_TXRX_CSR3, 0x00858687 }, - { RT2573_TXRX_CSR7, 0x2e31353b }, - { RT2573_TXRX_CSR8, 0x2a2a2a2c }, - { RT2573_TXRX_CSR15, 0x0000000f }, - { RT2573_MAC_CSR6, 0x00000fff }, - { RT2573_MAC_CSR8, 0x016c030a }, - { RT2573_MAC_CSR10, 0x00000718 }, - { RT2573_MAC_CSR12, 0x00000004 }, - { RT2573_MAC_CSR13, 0x00007f00 }, - { RT2573_SEC_CSR0, 0x00000000 }, - { RT2573_SEC_CSR1, 0x00000000 }, - { RT2573_SEC_CSR5, 0x00000000 }, - { RT2573_PHY_CSR1, 0x000023b0 }, - { RT2573_PHY_CSR5, 0x00040a06 }, - { RT2573_PHY_CSR6, 0x00080606 }, - { RT2573_PHY_CSR7, 0x00000408 }, - { RT2573_AIFSN_CSR, 0x00002273 }, - { RT2573_CWMIN_CSR, 0x00002344 }, - { RT2573_CWMAX_CSR, 0x000034aa } -}; - -static const struct { - uint8_t reg; - uint8_t val; -} rum_def_bbp[] = { - { 3, 0x80 }, - { 15, 0x30 }, - { 17, 0x20 }, - { 21, 0xc8 }, - { 22, 0x38 }, - { 23, 0x06 }, - { 24, 0xfe }, - { 25, 0x0a }, - { 26, 0x0d }, - { 32, 0x0b }, - { 34, 0x12 }, - { 37, 0x07 }, - { 39, 0xf8 }, - { 41, 0x60 }, - { 53, 0x10 }, - { 54, 0x18 }, - { 60, 0x10 }, - { 61, 0x04 }, - { 62, 0x04 }, - { 75, 0xfe }, - { 86, 0xfe }, - { 88, 0xfe }, - { 90, 0x0f }, - { 99, 0x00 }, - { 102, 0x16 }, - { 107, 0x04 } -}; - -static const struct rfprog { - uint8_t chan; - uint32_t r1, r2, r3, r4; -} rum_rf5226[] = { - { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, - { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, - { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, - { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, - { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, - { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, - { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, - { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, - { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, - { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, - { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, - { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, - { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, - { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, - - { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, - { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, - { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, - { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, - - { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, - { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, - { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, - { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, - { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, - { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, - { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, - { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, - - { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, - { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, - { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, - { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, - { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, - { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, - { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, - { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, - { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, - { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, - { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, - - { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, - { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, - { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, - { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, - { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } -}, rum_rf5225[] = { - { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, - { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, - { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, - { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, - { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, - { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, - { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, - { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, - { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, - { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, - { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, - { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, - { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, - { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, - - { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, - { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, - { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, - { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, - - { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, - { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, - { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, - { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, - { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, - { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, - { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, - { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, - - { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, - { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, - { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, - { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, - { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, - { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, - { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, - { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, - { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, - { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, - { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, - - { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, - { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, - { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, - { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, - { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } -}; - -static const struct usb2_config rum_config[RUM_N_TRANSFER] = { - [RUM_BULK_WR] = { - .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 */ - }, - [RUM_BULK_RD] = { - .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, - }, -}; - -static int -rum_match(device_t self) -{ - struct usb2_attach_arg *uaa = device_get_ivars(self); - - 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 self) -{ - struct usb2_attach_arg *uaa = device_get_ivars(self); - struct rum_softc *sc = device_get_softc(self); - uint8_t iface_index; - int error; - - device_set_usb2_desc(self); - sc->sc_udev = uaa->device; - sc->sc_dev = self; - - mtx_init(&sc->sc_mtx, device_get_nameunit(self), - MTX_NETWORK_LOCK, MTX_DEF); - - 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(self, "could not allocate USB transfers, " - "err=%s\n", usb2_errstr(error)); - goto detach; - } - error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, - device_get_nameunit(self), USB_PRI_MED); - if (error) { - device_printf(self, "could not setup config thread!\n"); - goto detach; - } - - /* fork rest of the attach code */ - RUM_LOCK(sc); - rum_queue_command(sc, rum_attach_post, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - RUM_UNLOCK(sc); - return (0); - -detach: - rum_detach(self); - return (ENXIO); /* failure */ -} - -static void -rum_attach_post(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp; - struct ieee80211com *ic; - unsigned int ntries; - int error; - uint32_t tmp; - uint8_t bands; - - /* retrieve RT2573 rev. no */ - for (ntries = 0; ntries < 100; ntries++) { - if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) - break; - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "timeout waiting for chip to settle\n"); - return; - } - - /* retrieve MAC address and various other things from EEPROM */ - rum_read_eeprom(sc); - - device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", - tmp, rum_get_rf(sc->rf_rev)); - - error = rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); - if (error != 0) { - RUM_UNLOCK(sc); - device_printf(sc->sc_dev, "could not load 8051 microcode\n"); - return; - } - RUM_UNLOCK(sc); - - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(sc->sc_dev, "can not if_alloc()\n"); - RUM_LOCK(sc); - return; - } - ic = ifp->if_l2com; - - ifp->if_softc = sc; - if_initname(ifp, "rum", device_get_unit(sc->sc_dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = rum_init; - ifp->if_ioctl = rum_ioctl; - ifp->if_start = rum_start; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; - IFQ_SET_READY(&ifp->if_snd); - - ic->ic_ifp = ifp; - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ - IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); - - /* set device capabilities */ - ic->ic_caps = - IEEE80211_C_STA /* station mode supported */ - | IEEE80211_C_IBSS /* IBSS mode supported */ - | IEEE80211_C_MONITOR /* monitor mode supported */ - | IEEE80211_C_HOSTAP /* HostAp mode supported */ - | IEEE80211_C_TXPMGT /* tx power management */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_BGSCAN /* bg scanning supported */ - | IEEE80211_C_WPA /* 802.11i */ - ; - - bands = 0; - setbit(&bands, IEEE80211_MODE_11B); - setbit(&bands, IEEE80211_MODE_11G); - if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) - setbit(&bands, IEEE80211_MODE_11A); - ieee80211_init_channels(ic, NULL, &bands); - - ieee80211_ifattach(ic); - ic->ic_newassoc = rum_newassoc; - ic->ic_raw_xmit = rum_raw_xmit; - ic->ic_node_alloc = rum_node_alloc; - ic->ic_scan_start = rum_scan_start; - ic->ic_scan_end = rum_scan_end; - ic->ic_set_channel = rum_set_channel; - - ic->ic_vap_create = rum_vap_create; - ic->ic_vap_delete = rum_vap_delete; - - sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); - - bpfattach(ifp, DLT_IEEE802_11_RADIO, - sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); - - sc->sc_rxtap_len = sizeof sc->sc_rxtap; - sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); - sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT); - - sc->sc_txtap_len = sizeof sc->sc_txtap; - sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); - sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT); - - if (bootverbose) - ieee80211_announce(ic); - - RUM_LOCK(sc); -} - -static int -rum_detach(device_t self) -{ - struct rum_softc *sc = device_get_softc(self); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic; - - /* wait for any post attach or other command to complete */ - usb2_proc_drain(&sc->sc_tq); - - /* stop all USB transfers */ - usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); - usb2_proc_free(&sc->sc_tq); - - /* free TX list, if any */ - RUM_LOCK(sc); - rum_unsetup_tx_list(sc); - RUM_UNLOCK(sc); - - if (ifp) { - ic = ifp->if_l2com; - bpfdetach(ifp); - ieee80211_ifdetach(ic); - if_free(ifp); - } - - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static struct ieee80211vap * -rum_vap_create(struct ieee80211com *ic, - const char name[IFNAMSIZ], int unit, int opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct rum_softc *sc = ic->ic_ifp->if_softc; - struct rum_vap *rvp; - struct ieee80211vap *vap; - - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap), - M_80211_VAP, M_NOWAIT | M_ZERO); - if (rvp == NULL) - return NULL; - vap = &rvp->vap; - /* enable s/w bmiss handling for sta mode */ - ieee80211_vap_setup(ic, vap, name, unit, opmode, - flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); - - /* override state transition machine */ - rvp->newstate = vap->iv_newstate; - vap->iv_newstate = rum_newstate; - - rvp->sc = sc; - usb2_callout_init_mtx(&rvp->amrr_ch, &sc->sc_mtx, 0); - ieee80211_amrr_init(&rvp->amrr, vap, - IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, - IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, - 1000 /* 1 sec */); - - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); - ic->ic_opmode = opmode; - return vap; -} - -static void -rum_vap_delete(struct ieee80211vap *vap) -{ - struct rum_vap *rvp = RUM_VAP(vap); - - usb2_callout_drain(&rvp->amrr_ch); - ieee80211_amrr_cleanup(&rvp->amrr); - ieee80211_vap_detach(vap); - free(rvp, M_80211_VAP); -} - -static void -rum_tx_free(struct rum_tx_data *data, int txerr) -{ - struct rum_softc *sc = data->sc; - - if (data->m != NULL) { - if (data->m->m_flags & M_TXCB) - ieee80211_process_callback(data->ni, data->m, - txerr ? ETIMEDOUT : 0); - m_freem(data->m); - data->m = NULL; - - ieee80211_free_node(data->ni); - data->ni = NULL; - } - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; -} - -static void -rum_setup_tx_list(struct rum_softc *sc) -{ - struct rum_tx_data *data; - int i; - - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - for (i = 0; i < RUM_TX_LIST_COUNT; i++) { - data = &sc->tx_data[i]; - - data->sc = sc; - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; - } -} - -static void -rum_unsetup_tx_list(struct rum_softc *sc) -{ - struct rum_tx_data *data; - int i; - - /* make sure any subsequent use of the queues will fail */ - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - /* free up all node references and mbufs */ - for (i = 0; i < RUM_TX_LIST_COUNT; i++) { - data = &sc->tx_data[i]; - - if (data->m != NULL) { - m_freem(data->m); - data->m = NULL; - } - if (data->ni != NULL) { - ieee80211_free_node(data->ni); - data->ni = NULL; - } - } -} - -static void -rum_task(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct rum_vap *rvp = RUM_VAP(vap); - const struct ieee80211_txparam *tp; - enum ieee80211_state ostate; - struct ieee80211_node *ni; - uint32_t tmp; - - ostate = vap->iv_state; - - switch (sc->sc_state) { - case IEEE80211_S_INIT: - if (ostate == IEEE80211_S_RUN) { - /* abort TSF synchronization */ - tmp = rum_read(sc, RT2573_TXRX_CSR9); - rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); - } - break; - - case IEEE80211_S_RUN: - ni = vap->iv_bss; - - if (vap->iv_opmode != IEEE80211_M_MONITOR) { - rum_update_slot(ic->ic_ifp); - rum_enable_mrr(sc); - rum_set_txpreamble(sc); - rum_set_basicrates(sc); - IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); - rum_set_bssid(sc, sc->sc_bssid); - } - - if (vap->iv_opmode == IEEE80211_M_HOSTAP || - vap->iv_opmode == IEEE80211_M_IBSS) - rum_prepare_beacon(sc, vap); - - if (vap->iv_opmode != IEEE80211_M_MONITOR) - rum_enable_tsf_sync(sc); - - /* enable automatic rate adaptation */ - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; - if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) - rum_amrr_start(sc, ni); - break; - default: - break; - } - - RUM_UNLOCK(sc); - IEEE80211_LOCK(ic); - rvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); - RUM_LOCK(sc); -} - -static int -rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct rum_vap *rvp = RUM_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct rum_softc *sc = ic->ic_ifp->if_softc; - - DPRINTF("%s -> %s\n", - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); - - RUM_LOCK(sc); - usb2_callout_stop(&rvp->amrr_ch); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - RUM_UNLOCK(sc); - - if (nstate == IEEE80211_S_INIT) { - rvp->newstate(vap, nstate, arg); - return 0; - } else { - RUM_LOCK(sc); - rum_queue_command(sc, rum_task, &sc->sc_task[0].hdr, - &sc->sc_task[1].hdr); - RUM_UNLOCK(sc); - return EINPROGRESS; - } -} - -static void -rum_bulk_write_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_channel *c = ic->ic_curchan; - struct rum_tx_data *data; - struct mbuf *m; - unsigned int len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); - - /* free resources */ - data = xfer->priv_fifo; - rum_tx_free(data, 0); - xfer->priv_fifo = NULL; - - ifp->if_opackets++; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - data = STAILQ_FIRST(&sc->tx_q); - if (data) { - STAILQ_REMOVE_HEAD(&sc->tx_q, next); - m = data->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_copy_in(xfer->frbuffers, 0, &data->desc, - RT2573_TX_DESC_SIZE); - usb2_m_copy_in(xfer->frbuffers, RT2573_TX_DESC_SIZE, m, - 0, m->m_pkthdr.len); - - if (bpf_peers_present(ifp->if_bpf)) { - struct rum_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = data->rate; - tap->wt_chan_freq = htole16(c->ic_freq); - tap->wt_chan_flags = htole16(c->ic_flags); - tap->wt_antenna = sc->tx_ant; - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); - } - - /* align end on a 4-bytes boundary */ - len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3; - if ((len % 64) == 0) - len += 4; - - DPRINTFN(11, "sending frame len=%u xferlen=%u\n", - m->m_pkthdr.len, len); - - xfer->frlengths[0] = len; - xfer->priv_fifo = data; - - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - data = xfer->priv_fifo; - if (data != NULL) { - rum_tx_free(data, xfer->error); - xfer->priv_fifo = NULL; - } - - if (xfer->error == USB_ERR_STALLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - if (xfer->error == USB_ERR_TIMEOUT) - device_printf(sc->sc_dev, "device timeout\n"); - break; - } -} - -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; - uint8_t rssi = 0; - unsigned int len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); - - len = xfer->actlen; - if (len < RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN) { - DPRINTF("%s: xfer too short %d\n", - device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; - goto tr_setup; - } - - len -= RT2573_RX_DESC_SIZE; - usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc, - RT2573_RX_DESC_SIZE); - - rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); - 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 RUM_TXRX_CSR2: - */ - DPRINTFN(5, "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; - } - usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, - mtod(m, uint8_t *), len); - - /* finalize mbuf */ - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; - - 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, - (flags & RT2573_RX_OFDM) ? - IEEE80211_T_OFDM : IEEE80211_T_CCK); - tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); - tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); - tap->wr_antenna = sc->rx_ant; - tap->wr_antsignal = rssi; - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); - } - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - 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) { - RUM_UNLOCK(sc); - ni = ieee80211_find_rxnode(ic, - mtod(m, struct ieee80211_frame_min *)); - if (ni != NULL) { - (void) ieee80211_input(ni, m, rssi, - RT2573_NOISE_FLOOR, 0); - ieee80211_free_node(ni); - } else - (void) ieee80211_input_all(ic, m, rssi, - RT2573_NOISE_FLOOR, 0); - RUM_LOCK(sc); - } - return; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static uint8_t -rum_plcp_signal(int rate) -{ - switch (rate) { - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ - case 12: return 0xb; - case 18: return 0xf; - case 24: return 0xa; - case 36: return 0xe; - case 48: return 0x9; - case 72: return 0xd; - case 96: return 0x8; - case 108: return 0xc; - - /* CCK rates (NB: not IEEE std, device-specific) */ - case 2: return 0x0; - case 4: return 0x1; - case 11: return 0x2; - case 22: return 0x3; - } - return 0xff; /* XXX unsupported/unknown rate */ -} - -static void -rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, - uint32_t flags, uint16_t xflags, int len, int rate) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint16_t plcp_length; - int remainder; - - desc->flags = htole32(flags); - desc->flags |= htole32(RT2573_TX_VALID); - desc->flags |= htole32(len << 16); - - desc->xflags = htole16(xflags); - - desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | - RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); - - /* setup PLCP fields */ - desc->plcp_signal = rum_plcp_signal(rate); - desc->plcp_service = 4; - - len += IEEE80211_CRC_LEN; - if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { - desc->flags |= htole32(RT2573_TX_OFDM); - - plcp_length = len & 0xfff; - desc->plcp_length_hi = plcp_length >> 6; - desc->plcp_length_lo = plcp_length & 0x3f; - } else { - plcp_length = (16 * len + rate - 1) / rate; - if (rate == 22) { - remainder = (16 * len) % 22; - if (remainder != 0 && remainder < 7) - desc->plcp_service |= RT2573_PLCP_LENGEXT; - } - desc->plcp_length_hi = plcp_length >> 8; - desc->plcp_length_lo = plcp_length & 0xff; - - if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) - desc->plcp_signal |= 0x08; - } -} - -#define RUM_TX_TIMEOUT 5000 - -static int -rum_sendprot(struct rum_softc *sc, - const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) -{ - struct ieee80211com *ic = ni->ni_ic; - const struct ieee80211_frame *wh; - struct rum_tx_data *data; - struct mbuf *mprot; - int protrate, ackrate, pktlen, flags, isshort; - uint16_t dur; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, - ("protection %d", prot)); - - wh = mtod(m, const struct ieee80211_frame *); - pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; - - protrate = ieee80211_ctl_rate(sc->sc_rates, rate); - ackrate = ieee80211_ack_rate(sc->sc_rates, rate); - - isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; - dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); - + ieee80211_ack_duration(sc->sc_rates, rate, isshort); - flags = RT2573_TX_MORE_FRAG; - if (prot == IEEE80211_PROT_RTSCTS) { - /* NB: CTS is the same size as an ACK */ - dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); - flags |= RT2573_TX_NEED_ACK; - mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); - } else { - mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); - } - if (mprot == NULL) { - /* XXX stat + msg */ - return ENOBUFS; - } - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - data->m = mprot; - data->ni = ieee80211_ref_node(ni); - data->rate = protrate; - rum_setup_tx_desc(sc, &data->desc, flags, 0, mprot->m_pkthdr.len, protrate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); - - return 0; -} - -static int -rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct rum_tx_data *data; - struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - uint32_t flags = 0; - uint16_t dur; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return ENOBUFS; - } - wh = mtod(m0, struct ieee80211_frame *); - } - - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - flags |= RT2573_TX_NEED_ACK; - - dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, - ic->ic_flags & IEEE80211_F_SHPREAMBLE); - *(uint16_t *)wh->i_dur = htole16(dur); - - /* tell hardware to add timestamp for probe responses */ - if ((wh->i_fc[0] & - (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == - (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) - flags |= RT2573_TX_TIMESTAMP; - } - - data->m = m0; - data->ni = ni; - data->rate = tp->mgmtrate; - - rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate); - - DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", - m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); - - return 0; -} - -static int -rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - const struct ieee80211_bpf_params *params) -{ - struct rum_tx_data *data; - uint32_t flags; - int rate, error; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - KASSERT(params != NULL, ("no raw xmit params")); - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - rate = params->ibp_rate0 & IEEE80211_RATE_VAL; - /* XXX validate */ - if (rate == 0) { - m_freem(m0); - return EINVAL; - } - flags = 0; - if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) - flags |= RT2573_TX_NEED_ACK; - if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { - error = rum_sendprot(sc, m0, ni, - params->ibp_flags & IEEE80211_BPF_RTS ? - IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, - rate); - if (error) { - m_freem(m0); - return error; - } - flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; - } - - data->m = m0; - data->ni = ni; - data->rate = rate; - - /* XXX need to setup descriptor ourself */ - rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); - - DPRINTFN(10, "sending raw frame len=%u rate=%u\n", - m0->m_pkthdr.len, rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); - - return 0; -} - -static int -rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct rum_tx_data *data; - struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - uint32_t flags = 0; - uint16_t dur; - int error, rate; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - wh = mtod(m0, struct ieee80211_frame *); - - tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; - if (IEEE80211_IS_MULTICAST(wh->i_addr1)) - rate = tp->mcastrate; - else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) - rate = tp->ucastrate; - else - rate = ni->ni_txrate; - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return ENOBUFS; - } - - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - int prot = IEEE80211_PROT_NONE; - if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) - prot = IEEE80211_PROT_RTSCTS; - else if ((ic->ic_flags & IEEE80211_F_USEPROT) && - ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) - prot = ic->ic_protmode; - if (prot != IEEE80211_PROT_NONE) { - error = rum_sendprot(sc, m0, ni, prot, rate); - if (error) { - m_freem(m0); - return error; - } - flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; - } - } - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - data->m = m0; - data->ni = ni; - data->rate = rate; - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - flags |= RT2573_TX_NEED_ACK; - flags |= RT2573_TX_MORE_FRAG; - - dur = ieee80211_ack_duration(sc->sc_rates, rate, - ic->ic_flags & IEEE80211_F_SHPREAMBLE); - *(uint16_t *)wh->i_dur = htole16(dur); - } - - rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); - - DPRINTFN(10, "sending frame len=%d rate=%d\n", - m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); - - return 0; -} - -static void -rum_start(struct ifnet *ifp) -{ - struct rum_softc *sc = ifp->if_softc; - struct ieee80211_node *ni; - struct mbuf *m; - - RUM_LOCK(sc); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - RUM_UNLOCK(sc); - return; - } - for (;;) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - if (sc->tx_nfree == 0) { - IFQ_DRV_PREPEND(&ifp->if_snd, m); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - m = ieee80211_encap(ni, m); - if (m == NULL) { - ieee80211_free_node(ni); - continue; - } - if (rum_tx_data(sc, m, ni) != 0) { - ieee80211_free_node(ni); - ifp->if_oerrors++; - break; - } - } - RUM_UNLOCK(sc); -} - -static int -rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct rum_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - RUM_LOCK(sc); - if (ifp->if_flags & IFF_UP) { - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - rum_queue_command(sc, rum_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - startall = 1; - } else - rum_queue_command(sc, rum_promisctask, - &sc->sc_promisctask[0].hdr, - &sc->sc_promisctask[1].hdr); - } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - rum_queue_command(sc, rum_stop_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - } - } - RUM_UNLOCK(sc); - if (startall) - ieee80211_start_all(ic); - break; - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; - default: - error = EINVAL; - break; - } - return error; -} - -static void -rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = RT2573_READ_EEPROM; - USETW(req.wValue, 0); - USETW(req.wIndex, addr); - USETW(req.wLength, len); - - error = rum_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, "could not read EEPROM: %s\n", - usb2_errstr(error)); - } -} - -static uint32_t -rum_read(struct rum_softc *sc, uint16_t reg) -{ - uint32_t val; - - rum_read_multi(sc, reg, &val, sizeof val); - - return le32toh(val); -} - -static void -rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = RT2573_READ_MULTI_MAC; - USETW(req.wValue, 0); - USETW(req.wIndex, reg); - USETW(req.wLength, len); - - error = rum_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, - "could not multi read MAC register: %s\n", - usb2_errstr(error)); - } -} - -static void -rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) -{ - uint32_t tmp = htole32(val); - - rum_write_multi(sc, reg, &tmp, sizeof tmp); -} - -static void -rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RT2573_WRITE_MULTI_MAC; - USETW(req.wValue, 0); - USETW(req.wIndex, reg); - USETW(req.wLength, len); - - error = rum_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, - "could not multi write MAC register: %s\n", - usb2_errstr(error)); - } -} - -static void -rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) -{ - uint32_t tmp; - int ntries; - - for (ntries = 0; ntries < 100; ntries++) { - if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) - break; - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not write to BBP\n"); - return; - } - - tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; - rum_write(sc, RT2573_PHY_CSR3, tmp); -} - -static uint8_t -rum_bbp_read(struct rum_softc *sc, uint8_t reg) -{ - uint32_t val; - int ntries; - - for (ntries = 0; ntries < 100; ntries++) { - if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) - break; - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not read BBP\n"); - return 0; - } - - val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; - rum_write(sc, RT2573_PHY_CSR3, val); - - for (ntries = 0; ntries < 100; ntries++) { - val = rum_read(sc, RT2573_PHY_CSR3); - if (!(val & RT2573_BBP_BUSY)) - return val & 0xff; - if (rum_pause(sc, hz / 100)) - break; - } - - device_printf(sc->sc_dev, "could not read BBP\n"); - return 0; -} - -static void -rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) -{ - uint32_t tmp; - int ntries; - - for (ntries = 0; ntries < 100; ntries++) { - if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) - break; - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not write to RF\n"); - return; - } - - tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | - (reg & 3); - rum_write(sc, RT2573_PHY_CSR4, tmp); - - /* remember last written value in sc */ - sc->rf_regs[reg] = val; - - DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff); -} - -static void -rum_select_antenna(struct rum_softc *sc) -{ - uint8_t bbp4, bbp77; - uint32_t tmp; - - bbp4 = rum_bbp_read(sc, 4); - bbp77 = rum_bbp_read(sc, 77); - - /* TBD */ - - /* make sure Rx is disabled before switching antenna */ - tmp = rum_read(sc, RT2573_TXRX_CSR0); - rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); - - rum_bbp_write(sc, 4, bbp4); - rum_bbp_write(sc, 77, bbp77); - - rum_write(sc, RT2573_TXRX_CSR0, tmp); -} - -/* - * Enable multi-rate retries for frames sent at OFDM rates. - * In 802.11b/g mode, allow fallback to CCK rates. - */ -static void -rum_enable_mrr(struct rum_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - - tmp = rum_read(sc, RT2573_TXRX_CSR4); - - tmp &= ~RT2573_MRR_CCK_FALLBACK; - if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) - tmp |= RT2573_MRR_CCK_FALLBACK; - tmp |= RT2573_MRR_ENABLED; - - rum_write(sc, RT2573_TXRX_CSR4, tmp); -} - -static void -rum_set_txpreamble(struct rum_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - - tmp = rum_read(sc, RT2573_TXRX_CSR4); - - tmp &= ~RT2573_SHORT_PREAMBLE; - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - tmp |= RT2573_SHORT_PREAMBLE; - - rum_write(sc, RT2573_TXRX_CSR4, tmp); -} - -static void -rum_set_basicrates(struct rum_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - /* update basic rate set */ - if (ic->ic_curmode == IEEE80211_MODE_11B) { - /* 11b basic rates: 1, 2Mbps */ - rum_write(sc, RT2573_TXRX_CSR5, 0x3); - } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { - /* 11a basic rates: 6, 12, 24Mbps */ - rum_write(sc, RT2573_TXRX_CSR5, 0x150); - } else { - /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ - rum_write(sc, RT2573_TXRX_CSR5, 0xf); - } -} - -/* - * Reprogram MAC/BBP to switch to a new band. Values taken from the reference - * driver. - */ -static void -rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) -{ - uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; - uint32_t tmp; - - /* update all BBP registers that depend on the band */ - bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; - bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; - if (IEEE80211_IS_CHAN_5GHZ(c)) { - bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; - bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; - } - if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || - (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { - bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; - } - - sc->bbp17 = bbp17; - rum_bbp_write(sc, 17, bbp17); - rum_bbp_write(sc, 96, bbp96); - rum_bbp_write(sc, 104, bbp104); - - if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || - (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { - rum_bbp_write(sc, 75, 0x80); - rum_bbp_write(sc, 86, 0x80); - rum_bbp_write(sc, 88, 0x80); - } - - rum_bbp_write(sc, 35, bbp35); - rum_bbp_write(sc, 97, bbp97); - rum_bbp_write(sc, 98, bbp98); - - tmp = rum_read(sc, RT2573_PHY_CSR0); - tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); - if (IEEE80211_IS_CHAN_2GHZ(c)) - tmp |= RT2573_PA_PE_2GHZ; - else - tmp |= RT2573_PA_PE_5GHZ; - rum_write(sc, RT2573_PHY_CSR0, tmp); -} - -static void -rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct rfprog *rfprog; - uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; - int8_t power; - int i, chan; - - chan = ieee80211_chan2ieee(ic, c); - if (chan == 0 || chan == IEEE80211_CHAN_ANY) - return; - - /* select the appropriate RF settings based on what EEPROM says */ - rfprog = (sc->rf_rev == RT2573_RF_5225 || - sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; - - /* find the settings for this channel (we know it exists) */ - for (i = 0; rfprog[i].chan != chan; i++); - - power = sc->txpow[i]; - if (power < 0) { - bbp94 += power; - power = 0; - } else if (power > 31) { - bbp94 += power - 31; - power = 31; - } - - /* - * If we are switching from the 2GHz band to the 5GHz band or - * vice-versa, BBP registers need to be reprogrammed. - */ - if (c->ic_flags != ic->ic_curchan->ic_flags) { - rum_select_band(sc, c); - rum_select_antenna(sc); - } - ic->ic_curchan = c; - - rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); - rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); - rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); - rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); - - rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); - rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); - rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); - rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); - - rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); - rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); - rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); - rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); - - rum_pause(sc, hz / 100); - - /* enable smart mode for MIMO-capable RFs */ - bbp3 = rum_bbp_read(sc, 3); - - bbp3 &= ~RT2573_SMART_MODE; - if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) - bbp3 |= RT2573_SMART_MODE; - - rum_bbp_write(sc, 3, bbp3); - - if (bbp94 != RT2573_BBPR94_DEFAULT) - rum_bbp_write(sc, 94, bbp94); -} - -/* - * Enable TSF synchronization and tell h/w to start sending beacons for IBSS - * and HostAP operating modes. - */ -static void -rum_enable_tsf_sync(struct rum_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - uint32_t tmp; - - if (vap->iv_opmode != IEEE80211_M_STA) { - /* - * Change default 16ms TBTT adjustment to 8ms. - * Must be done before enabling beacon generation. - */ - rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8); - } - - tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; - - /* set beacon interval (in 1/16ms unit) */ - tmp |= vap->iv_bss->ni_intval * 16; - - tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; - if (vap->iv_opmode == IEEE80211_M_STA) - tmp |= RT2573_TSF_MODE(1); - else - tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; - - rum_write(sc, RT2573_TXRX_CSR9, tmp); -} - -static void -rum_update_slot(struct ifnet *ifp) -{ - struct rum_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - uint8_t slottime; - uint32_t tmp; - - slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; - - tmp = rum_read(sc, RT2573_MAC_CSR9); - tmp = (tmp & ~0xff) | slottime; - rum_write(sc, RT2573_MAC_CSR9, tmp); - - DPRINTF("setting slot time to %uus\n", slottime); -} - -static void -rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) -{ - uint32_t tmp; - - tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24; - rum_write(sc, RT2573_MAC_CSR4, tmp); - - tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16; - rum_write(sc, RT2573_MAC_CSR5, tmp); -} - -static void -rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) -{ - uint32_t tmp; - - tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24; - rum_write(sc, RT2573_MAC_CSR2, tmp); - - tmp = addr[4] | addr[5] << 8 | 0xff << 16; - rum_write(sc, RT2573_MAC_CSR3, tmp); -} - -static void -rum_promisctask(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - - tmp = rum_read(sc, RT2573_TXRX_CSR0); - - tmp &= ~RT2573_DROP_NOT_TO_ME; - if (!(ifp->if_flags & IFF_PROMISC)) - tmp |= RT2573_DROP_NOT_TO_ME; - - rum_write(sc, RT2573_TXRX_CSR0, tmp); - - DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? - "entering" : "leaving"); -} - -static const char * -rum_get_rf(int rev) -{ - switch (rev) { - case RT2573_RF_2527: return "RT2527 (MIMO XR)"; - case RT2573_RF_2528: return "RT2528"; - case RT2573_RF_5225: return "RT5225 (MIMO XR)"; - case RT2573_RF_5226: return "RT5226"; - default: return "unknown"; - } -} - -static void -rum_read_eeprom(struct rum_softc *sc) -{ - uint16_t val; -#ifdef RUM_DEBUG - int i; -#endif - - /* read MAC address */ - rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_bssid, 6); - - rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); - val = le16toh(val); - sc->rf_rev = (val >> 11) & 0x1f; - sc->hw_radio = (val >> 10) & 0x1; - sc->rx_ant = (val >> 4) & 0x3; - sc->tx_ant = (val >> 2) & 0x3; - sc->nb_ant = val & 0x3; - - DPRINTF("RF revision=%d\n", sc->rf_rev); - - rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); - val = le16toh(val); - sc->ext_5ghz_lna = (val >> 6) & 0x1; - sc->ext_2ghz_lna = (val >> 4) & 0x1; - - DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", - sc->ext_2ghz_lna, sc->ext_5ghz_lna); - - rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); - val = le16toh(val); - if ((val & 0xff) != 0xff) - sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ - - /* Only [-10, 10] is valid */ - if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) - sc->rssi_2ghz_corr = 0; - - rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); - val = le16toh(val); - if ((val & 0xff) != 0xff) - sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ - - /* Only [-10, 10] is valid */ - if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) - sc->rssi_5ghz_corr = 0; - - if (sc->ext_2ghz_lna) - sc->rssi_2ghz_corr -= 14; - if (sc->ext_5ghz_lna) - sc->rssi_5ghz_corr -= 14; - - DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", - sc->rssi_2ghz_corr, sc->rssi_5ghz_corr); - - rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); - val = le16toh(val); - if ((val & 0xff) != 0xff) - sc->rffreq = val & 0xff; - - DPRINTF("RF freq=%d\n", sc->rffreq); - - /* read Tx power for all a/b/g channels */ - rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); - /* XXX default Tx power for 802.11a channels */ - memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); -#ifdef RUM_DEBUG - for (i = 0; i < 14; i++) - DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]); -#endif - - /* read default values for BBP registers */ - rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); -#ifdef RUM_DEBUG - for (i = 0; i < 14; i++) { - if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) - continue; - DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg, - sc->bbp_prom[i].val); - } -#endif -} - -static int -rum_bbp_init(struct rum_softc *sc) -{ -#define N(a) (sizeof (a) / sizeof ((a)[0])) - int i, ntries; - - /* wait for BBP to be ready */ - for (ntries = 0; ntries < 100; ntries++) { - const uint8_t val = rum_bbp_read(sc, 0); - if (val != 0 && val != 0xff) - break; - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "timeout waiting for BBP\n"); - return EIO; - } - - /* initialize BBP registers to default values */ - for (i = 0; i < N(rum_def_bbp); i++) - rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); - - /* write vendor-specific BBP values (from EEPROM) */ - for (i = 0; i < 16; i++) { - if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) - continue; - rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); - } - - return 0; -#undef N -} - -static void -rum_init_task(struct usb2_proc_msg *pm) -{ -#define N(a) (sizeof (a) / sizeof ((a)[0])) - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - usb2_error_t error; - int i, ntries; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - rum_stop_task(pm); - - /* initialize MAC registers to default values */ - for (i = 0; i < N(rum_def_mac); i++) - rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); - - /* set host ready */ - rum_write(sc, RT2573_MAC_CSR1, 3); - rum_write(sc, RT2573_MAC_CSR1, 0); - - /* wait for BBP/RF to wakeup */ - for (ntries = 0; ntries < 100; ntries++) { - if (rum_read(sc, RT2573_MAC_CSR12) & 8) - break; - rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ - if (rum_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, - "timeout waiting for BBP/RF to wakeup\n"); - goto fail; - } - - if ((error = rum_bbp_init(sc)) != 0) - goto fail; - - /* select default channel */ - rum_select_band(sc, ic->ic_curchan); - rum_select_antenna(sc); - rum_set_chan(sc, ic->ic_curchan); - - /* clear STA registers */ - rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); - - IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); - rum_set_macaddr(sc, ic->ic_myaddr); - - /* initialize ASIC */ - rum_write(sc, RT2573_MAC_CSR1, 4); - - /* - * Allocate Tx and Rx xfer queues. - */ - rum_setup_tx_list(sc); - - /* update Rx filter */ - tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; - - tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | - RT2573_DROP_ACKCTS; - if (ic->ic_opmode != IEEE80211_M_HOSTAP) - tmp |= RT2573_DROP_TODS; - if (!(ifp->if_flags & IFF_PROMISC)) - tmp |= RT2573_DROP_NOT_TO_ME; - } - rum_write(sc, RT2573_TXRX_CSR0, tmp); - - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - usb2_transfer_set_stall(sc->sc_xfer[RUM_BULK_WR]); - usb2_transfer_start(sc->sc_xfer[RUM_BULK_RD]); - return; - -fail: rum_stop_task(pm); -#undef N -} - -static void -rum_init(void *priv) -{ - struct rum_softc *sc = priv; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - RUM_LOCK(sc); - rum_queue_command(sc, rum_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - RUM_UNLOCK(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vap's */ -} - -static void -rum_stop_task(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - RUM_UNLOCK(sc); - - /* - * Drain the USB transfers, if not already drained: - */ - usb2_transfer_drain(sc->sc_xfer[RUM_BULK_WR]); - usb2_transfer_drain(sc->sc_xfer[RUM_BULK_RD]); - - RUM_LOCK(sc); - - rum_unsetup_tx_list(sc); - - /* disable Rx */ - tmp = rum_read(sc, RT2573_TXRX_CSR0); - rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); - - /* reset ASIC */ - rum_write(sc, RT2573_MAC_CSR1, 3); - rum_write(sc, RT2573_MAC_CSR1, 0); -} - -static int -rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size) -{ - struct usb2_device_request req; - uint16_t reg = RT2573_MCU_CODE_BASE; - usb2_error_t error; - - /* copy firmware image into NIC */ - for (; size >= 4; reg += 4, ucode += 4, size -= 4) - rum_write(sc, reg, UGETDW(ucode)); - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RT2573_MCU_CNTL; - USETW(req.wValue, RT2573_MCU_RUN); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - - error = rum_do_request(sc, &req, NULL); - if (error != 0) { - device_printf(sc->sc_dev, "could not run firmware: %s\n", - usb2_errstr(error)); - } - return error; -} - -static int -rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - const struct ieee80211_txparam *tp; - struct rum_tx_desc desc; - struct mbuf *m0; - - m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo); - if (m0 == NULL) { - return ENOBUFS; - } - - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; - rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ, - m0->m_pkthdr.len, tp->mgmtrate); - - /* copy the first 24 bytes of Tx descriptor into NIC memory */ - rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24); - - /* copy beacon header and payload into NIC memory */ - rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *), - m0->m_pkthdr.len); - - m_freem(m0); - - return 0; -} - -static int -rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ifnet *ifp = ni->ni_ic->ic_ifp; - struct rum_softc *sc = ifp->if_softc; - - RUM_LOCK(sc); - /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - RUM_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } - if (sc->tx_nfree == 0) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - RUM_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return EIO; - } - - ifp->if_opackets++; - - if (params == NULL) { - /* - * Legacy path; interpret frame contents to decide - * precisely how to send the frame. - */ - if (rum_tx_mgt(sc, m, ni) != 0) - goto bad; - } else { - /* - * Caller supplied explicit parameters to use in - * sending the frame. - */ - if (rum_tx_raw(sc, m, ni, params) != 0) - goto bad; - } - RUM_UNLOCK(sc); - - return 0; -bad: - ifp->if_oerrors++; - RUM_UNLOCK(sc); - ieee80211_free_node(ni); - return EIO; -} - -static void -rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct rum_vap *rvp = RUM_VAP(vap); - - /* clear statistic registers (STA_CSR0 to STA_CSR5) */ - rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); - - ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni); - - usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); -} - -static void -rum_amrr_timeout(void *arg) -{ - struct rum_vap *rvp = arg; - struct rum_softc *sc = rvp->sc; - - rum_queue_command(sc, rum_amrr_task, - &rvp->amrr_task[0].hdr, &rvp->amrr_task[1].hdr); -} - -static void -rum_amrr_task(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct rum_vap *rvp = RUM_VAP(vap); - struct ieee80211_node *ni = vap->iv_bss; - int ok, fail; - - /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ - rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta)); - - ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */ - (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */ - fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ - - ieee80211_amrr_tx_update(&RUM_NODE(ni)->amn, - ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail); - (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn); - - ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ - - usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); -} - -/* ARGUSED */ -static struct ieee80211_node * -rum_node_alloc(struct ieee80211vap *vap __unused, - const uint8_t mac[IEEE80211_ADDR_LEN] __unused) -{ - struct rum_node *rn; - - rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); - return rn != NULL ? &rn->ni : NULL; -} - -static void -rum_newassoc(struct ieee80211_node *ni, int isnew) -{ - struct ieee80211vap *vap = ni->ni_vap; - - ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); -} - -static void -rum_scan_start(struct ieee80211com *ic) -{ - struct rum_softc *sc = ic->ic_ifp->if_softc; - - RUM_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = RUM_SCAN_START; - rum_queue_command(sc, rum_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - RUM_UNLOCK(sc); - -} - -static void -rum_scan_end(struct ieee80211com *ic) -{ - struct rum_softc *sc = ic->ic_ifp->if_softc; - - RUM_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = RUM_SCAN_END; - rum_queue_command(sc, rum_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - RUM_UNLOCK(sc); - -} - -static void -rum_set_channel(struct ieee80211com *ic) -{ - struct rum_softc *sc = ic->ic_ifp->if_softc; - - RUM_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = RUM_SET_CHANNEL; - sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); - rum_queue_command(sc, rum_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - RUM_UNLOCK(sc); -} - -static void -rum_scantask(struct usb2_proc_msg *pm) -{ - struct rum_task *task = (struct rum_task *)pm; - struct rum_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - switch (sc->sc_scan_action) { - case RUM_SCAN_START: - /* abort TSF synchronization */ - tmp = rum_read(sc, RT2573_TXRX_CSR9); - rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); - rum_set_bssid(sc, ifp->if_broadcastaddr); - break; - - case RUM_SET_CHANNEL: - rum_set_chan(sc, ic->ic_curchan); - break; - - default: /* RUM_SCAN_END */ - rum_enable_tsf_sync(sc); - rum_set_bssid(sc, sc->sc_bssid); - break; - } -} - -static int -rum_get_rssi(struct rum_softc *sc, uint8_t raw) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - int lna, agc, rssi; - - lna = (raw >> 5) & 0x3; - agc = raw & 0x1f; - - if (lna == 0) { - /* - * No RSSI mapping - * - * NB: Since RSSI is relative to noise floor, -1 is - * adequate for caller to know error happened. - */ - return -1; - } - - rssi = (2 * agc) - RT2573_NOISE_FLOOR; - - if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { - rssi += sc->rssi_2ghz_corr; - - if (lna == 1) - rssi -= 64; - else if (lna == 2) - rssi -= 74; - else if (lna == 3) - rssi -= 90; - } else { - rssi += sc->rssi_5ghz_corr; - - if (!sc->ext_5ghz_lna && lna != 1) - rssi += 4; - - if (lna == 1) - rssi -= 64; - else if (lna == 2) - rssi -= 86; - else if (lna == 3) - rssi -= 100; - } - return rssi; -} - -static int -rum_pause(struct rum_softc *sc, int timeout) -{ - if (usb2_proc_is_gone(&sc->sc_tq)) - return (1); - - usb2_pause_mtx(&sc->sc_mtx, timeout); - return (0); -} - -static void -rum_queue_command(struct rum_softc *sc, usb2_proc_callback_t *fn, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) -{ - struct rum_task *task; - - RUM_LOCK_ASSERT(sc, MA_OWNED); - - if (usb2_proc_is_gone(&sc->sc_tq)) { - DPRINTF("proc is gone\n"); - return; /* nothing to do */ - } - /* - * NOTE: The task cannot get executed before we drop the - * "sc_mtx" mutex. It is safe to update fields in the message - * structure after that the message got queued. - */ - task = (struct rum_task *) - usb2_proc_msignal(&sc->sc_tq, t0, t1); - - /* Setup callback and softc pointers */ - task->hdr.pm_callback = fn; - task->sc = sc; - - /* - * Init and stop must be synchronous! - */ - if ((fn == rum_init_task) || (fn == rum_stop_task)) - usb2_proc_mwait(&sc->sc_tq, t0, t1); -} - -static device_method_t rum_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, rum_match), - DEVMETHOD(device_attach, rum_attach), - DEVMETHOD(device_detach, rum_detach), - - { 0, 0 } -}; - -static driver_t rum_driver = { - .name = "rum", - .methods = rum_methods, - .size = sizeof(struct rum_softc), -}; - -static devclass_t rum_devclass; - -DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0); diff --git a/sys/dev/usb2/wlan/if_rumfw.h b/sys/dev/usb2/wlan/if_rumfw.h deleted file mode 100644 index 0f08674..0000000 --- a/sys/dev/usb2/wlan/if_rumfw.h +++ /dev/null @@ -1,213 +0,0 @@ -/* $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_rumreg.h b/sys/dev/usb2/wlan/if_rumreg.h deleted file mode 100644 index 75a51bc..0000000 --- a/sys/dev/usb2/wlan/if_rumreg.h +++ /dev/null @@ -1,235 +0,0 @@ -/* $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) - - 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_rumvar.h b/sys/dev/usb2/wlan/if_rumvar.h deleted file mode 100644 index 1b58dc4..0000000 --- a/sys/dev/usb2/wlan/if_rumvar.h +++ /dev/null @@ -1,156 +0,0 @@ -/* $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_TX_LIST_COUNT 8 - -struct rum_rx_radiotap_header { - struct ieee80211_radiotap_header wr_ihdr; - uint8_t wr_flags; - uint8_t wr_rate; - uint16_t wr_chan_freq; - uint16_t wr_chan_flags; - uint8_t wr_antenna; - uint8_t wr_antsignal; -}; - -#define RT2573_RX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA) | \ - (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) - -struct rum_tx_radiotap_header { - struct ieee80211_radiotap_header wt_ihdr; - uint8_t wt_flags; - uint8_t wt_rate; - uint16_t wt_chan_freq; - uint16_t wt_chan_flags; - uint8_t wt_antenna; -}; - -#define RT2573_TX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA)) - -struct rum_softc; - -struct rum_task { - struct usb2_proc_msg hdr; - struct rum_softc *sc; -}; - -struct rum_tx_data { - STAILQ_ENTRY(rum_tx_data) next; - struct rum_softc *sc; - struct rum_tx_desc desc; - struct mbuf *m; - struct ieee80211_node *ni; - int rate; -}; -typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead; - -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 rum_softc *sc; - struct ieee80211_beacon_offsets bo; - struct ieee80211_amrr amrr; - struct usb2_callout amrr_ch; - struct rum_task amrr_task[2]; - - int (*newstate)(struct ieee80211vap *, - enum ieee80211_state, int); -}; -#define RUM_VAP(vap) ((struct rum_vap *)(vap)) - -enum { - RUM_BULK_WR, - RUM_BULK_RD, - RUM_N_TRANSFER = 2, -}; - -struct rum_softc { - struct ifnet *sc_ifp; - device_t sc_dev; - struct usb2_device *sc_udev; - struct usb2_process sc_tq; - - const struct ieee80211_rate_table *sc_rates; - struct usb2_xfer *sc_xfer[RUM_N_TRANSFER]; - - uint8_t rf_rev; - uint8_t rffreq; - - enum ieee80211_state sc_state; - int sc_arg; - struct rum_task sc_synctask[2]; - struct rum_task sc_task[2]; - struct rum_task sc_promisctask[2]; - struct rum_task sc_scantask[2]; - int sc_scan_action; -#define RUM_SCAN_START 0 -#define RUM_SCAN_END 1 -#define RUM_SET_CHANNEL 2 - - struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; - rum_txdhead tx_q; - rum_txdhead tx_free; - int tx_nfree; - struct rum_rx_desc sc_rx_desc; - - struct mtx sc_mtx; - - uint32_t sta[6]; - uint32_t rf_regs[4]; - uint8_t txpow[44]; - uint8_t sc_bssid[6]; - - struct { - uint8_t val; - uint8_t reg; - } __packed bbp_prom[16]; - - int hw_radio; - int rx_ant; - int tx_ant; - int nb_ant; - int ext_2ghz_lna; - int ext_5ghz_lna; - int rssi_2ghz_corr; - int rssi_5ghz_corr; - uint8_t bbp17; - - struct rum_rx_radiotap_header sc_rxtap; - int sc_rxtap_len; - - struct rum_tx_radiotap_header sc_txtap; - int sc_txtap_len; -}; - -#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) -#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) -#define RUM_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/sys/dev/usb2/wlan/if_ural2.c b/sys/dev/usb2/wlan/if_ural2.c deleted file mode 100644 index b48cecd..0000000 --- a/sys/dev/usb2/wlan/if_ural2.c +++ /dev/null @@ -1,2365 +0,0 @@ -/* $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. - */ - -#include -__FBSDID("$FreeBSD$"); - -/*- - * Ralink Technology RT2500USB chipset driver - * http://www.ralinktech.com/ - */ - -#include "usbdevs.h" -#include -#include -#include - -#define USB_DEBUG_VAR ural_debug - -#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_do_request(sc,req,data) \ - usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) - -#define URAL_RSSI(rssi) \ - ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ - ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) - -/* various supported device vendors/products */ -static const struct usb2_device_id ural_devs[] = { - { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G) }, - { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570) }, - { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050) }, - { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051) }, - { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS) }, - { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G) }, - { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP) }, - { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU) }, - { USB_VP(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122) }, - { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G) }, - { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG) }, - { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB) }, - { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2) }, - { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3) }, - { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2) }, - { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3) }, - { USB_VP(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G) }, - { USB_VP(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG) }, - { USB_VP(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R) }, - { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570) }, - { USB_VP(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570) }, - { USB_VP(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570) }, -}; - -static usb2_callback_t ural_bulk_read_callback; -static usb2_callback_t ural_bulk_write_callback; - -static usb2_proc_callback_t ural_attach_post; -static usb2_proc_callback_t ural_task; -static usb2_proc_callback_t ural_scantask; -static usb2_proc_callback_t ural_promisctask; -static usb2_proc_callback_t ural_amrr_task; -static usb2_proc_callback_t ural_init_task; -static usb2_proc_callback_t ural_stop_task; - -static struct ieee80211vap *ural_vap_create(struct ieee80211com *, - const char name[IFNAMSIZ], int unit, int opmode, - int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void ural_vap_delete(struct ieee80211vap *); -static void ural_tx_free(struct ural_tx_data *, int); -static void ural_setup_tx_list(struct ural_softc *); -static void ural_unsetup_tx_list(struct ural_softc *); -static int ural_newstate(struct ieee80211vap *, - enum ieee80211_state, int); -static void ural_setup_tx_desc(struct ural_softc *, - struct ural_tx_desc *, uint32_t, int, int); -static int ural_tx_bcn(struct ural_softc *, struct mbuf *, - struct ieee80211_node *); -static int ural_tx_mgt(struct ural_softc *, struct mbuf *, - struct ieee80211_node *); -static int ural_tx_data(struct ural_softc *, struct mbuf *, - struct ieee80211_node *); -static void ural_start(struct ifnet *); -static int ural_ioctl(struct ifnet *, u_long, caddr_t); -static void ural_set_testmode(struct ural_softc *); -static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, - int); -static uint16_t ural_read(struct ural_softc *, uint16_t); -static void ural_read_multi(struct ural_softc *, uint16_t, void *, - int); -static void ural_write(struct ural_softc *, uint16_t, uint16_t); -static void ural_write_multi(struct ural_softc *, uint16_t, void *, - int) __unused; -static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); -static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); -static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); -static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *, - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void ural_newassoc(struct ieee80211_node *, int); -static void ural_scan_start(struct ieee80211com *); -static void ural_scan_end(struct ieee80211com *); -static void ural_set_channel(struct ieee80211com *); -static void ural_set_chan(struct ural_softc *, - struct ieee80211_channel *); -static void ural_disable_rf_tune(struct ural_softc *); -static void ural_enable_tsf_sync(struct ural_softc *); -static void ural_update_slot(struct ifnet *); -static void ural_set_txpreamble(struct ural_softc *); -static void ural_set_basicrates(struct ural_softc *, - const struct ieee80211_channel *); -static void ural_set_bssid(struct ural_softc *, const uint8_t *); -static void ural_set_macaddr(struct ural_softc *, uint8_t *); -static const char *ural_get_rf(int); -static void ural_read_eeprom(struct ural_softc *); -static int ural_bbp_init(struct ural_softc *); -static void ural_set_txantenna(struct ural_softc *, int); -static void ural_set_rxantenna(struct ural_softc *, int); -static void ural_init(void *); -static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, - const struct ieee80211_bpf_params *); -static void ural_amrr_start(struct ural_softc *, - struct ieee80211_node *); -static void ural_amrr_timeout(void *); -static int ural_pause(struct ural_softc *sc, int timeout); -static void ural_queue_command(struct ural_softc *, - usb2_proc_callback_t *, struct usb2_proc_msg *, - struct usb2_proc_msg *); - -/* - * Default values for MAC registers; values taken from the reference driver. - */ -static const struct { - uint16_t reg; - uint16_t val; -} ural_def_mac[] = { - { RAL_TXRX_CSR5, 0x8c8d }, - { RAL_TXRX_CSR6, 0x8b8a }, - { RAL_TXRX_CSR7, 0x8687 }, - { RAL_TXRX_CSR8, 0x0085 }, - { RAL_MAC_CSR13, 0x1111 }, - { RAL_MAC_CSR14, 0x1e11 }, - { RAL_TXRX_CSR21, 0xe78f }, - { RAL_MAC_CSR9, 0xff1d }, - { RAL_MAC_CSR11, 0x0002 }, - { RAL_MAC_CSR22, 0x0053 }, - { RAL_MAC_CSR15, 0x0000 }, - { RAL_MAC_CSR8, 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. - */ -static const struct { - uint8_t reg; - uint8_t val; -} ural_def_bbp[] = { - { 3, 0x02 }, - { 4, 0x19 }, - { 14, 0x1c }, - { 15, 0x30 }, - { 16, 0xac }, - { 17, 0x48 }, - { 18, 0x18 }, - { 19, 0xff }, - { 20, 0x1e }, - { 21, 0x08 }, - { 22, 0x08 }, - { 23, 0x08 }, - { 24, 0x80 }, - { 25, 0x50 }, - { 26, 0x08 }, - { 27, 0x23 }, - { 30, 0x10 }, - { 31, 0x2b }, - { 32, 0xb9 }, - { 34, 0x12 }, - { 35, 0x50 }, - { 39, 0xc4 }, - { 40, 0x02 }, - { 41, 0x60 }, - { 53, 0x10 }, - { 54, 0x18 }, - { 56, 0x08 }, - { 57, 0x10 }, - { 58, 0x08 }, - { 61, 0x60 }, - { 62, 0x10 }, - { 75, 0xff } -}; - -/* - * Default values for RF register R2 indexed by channel numbers. - */ -static const uint32_t ural_rf2522_r2[] = { - 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, - 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e -}; - -static const uint32_t ural_rf2523_r2[] = { - 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, - 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 -}; - -static const uint32_t ural_rf2524_r2[] = { - 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, - 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 -}; - -static const uint32_t ural_rf2525_r2[] = { - 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, - 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 -}; - -static const uint32_t ural_rf2525_hi_r2[] = { - 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, - 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e -}; - -static const uint32_t ural_rf2525e_r2[] = { - 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, - 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b -}; - -static const uint32_t ural_rf2526_hi_r2[] = { - 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, - 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 -}; - -static const uint32_t ural_rf2526_r2[] = { - 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, - 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d -}; - -/* - * For dual-band RF, RF registers R1 and R4 also depend on channel number; - * values taken from the reference driver. - */ -static const struct { - uint8_t chan; - uint32_t r1; - uint32_t r2; - uint32_t r4; -} ural_rf5222[] = { - { 1, 0x08808, 0x0044d, 0x00282 }, - { 2, 0x08808, 0x0044e, 0x00282 }, - { 3, 0x08808, 0x0044f, 0x00282 }, - { 4, 0x08808, 0x00460, 0x00282 }, - { 5, 0x08808, 0x00461, 0x00282 }, - { 6, 0x08808, 0x00462, 0x00282 }, - { 7, 0x08808, 0x00463, 0x00282 }, - { 8, 0x08808, 0x00464, 0x00282 }, - { 9, 0x08808, 0x00465, 0x00282 }, - { 10, 0x08808, 0x00466, 0x00282 }, - { 11, 0x08808, 0x00467, 0x00282 }, - { 12, 0x08808, 0x00468, 0x00282 }, - { 13, 0x08808, 0x00469, 0x00282 }, - { 14, 0x08808, 0x0046b, 0x00286 }, - - { 36, 0x08804, 0x06225, 0x00287 }, - { 40, 0x08804, 0x06226, 0x00287 }, - { 44, 0x08804, 0x06227, 0x00287 }, - { 48, 0x08804, 0x06228, 0x00287 }, - { 52, 0x08804, 0x06229, 0x00287 }, - { 56, 0x08804, 0x0622a, 0x00287 }, - { 60, 0x08804, 0x0622b, 0x00287 }, - { 64, 0x08804, 0x0622c, 0x00287 }, - - { 100, 0x08804, 0x02200, 0x00283 }, - { 104, 0x08804, 0x02201, 0x00283 }, - { 108, 0x08804, 0x02202, 0x00283 }, - { 112, 0x08804, 0x02203, 0x00283 }, - { 116, 0x08804, 0x02204, 0x00283 }, - { 120, 0x08804, 0x02205, 0x00283 }, - { 124, 0x08804, 0x02206, 0x00283 }, - { 128, 0x08804, 0x02207, 0x00283 }, - { 132, 0x08804, 0x02208, 0x00283 }, - { 136, 0x08804, 0x02209, 0x00283 }, - { 140, 0x08804, 0x0220a, 0x00283 }, - - { 149, 0x08808, 0x02429, 0x00281 }, - { 153, 0x08808, 0x0242b, 0x00281 }, - { 157, 0x08808, 0x0242d, 0x00281 }, - { 161, 0x08808, 0x0242f, 0x00281 } -}; - -static const struct usb2_config ural_config[URAL_N_TRANSFER] = { - [URAL_BULK_WR] = { - .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 */ - }, - [URAL_BULK_RD] = { - .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, - }, -}; - -static device_probe_t ural_match; -static device_attach_t ural_attach; -static device_detach_t ural_detach; - -static device_method_t ural_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, ural_match), - DEVMETHOD(device_attach, ural_attach), - DEVMETHOD(device_detach, ural_detach), - - { 0, 0 } -}; - -static driver_t ural_driver = { - .name = "ural", - .methods = ural_methods, - .size = sizeof(struct ural_softc), -}; - -static devclass_t ural_devclass; - -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_match(device_t self) -{ - struct usb2_attach_arg *uaa = device_get_ivars(self); - - 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 self) -{ - struct usb2_attach_arg *uaa = device_get_ivars(self); - struct ural_softc *sc = device_get_softc(self); - int error; - uint8_t iface_index; - - device_set_usb2_desc(self); - sc->sc_udev = uaa->device; - sc->sc_dev = self; - - mtx_init(&sc->sc_mtx, device_get_nameunit(self), - MTX_NETWORK_LOCK, MTX_DEF); - - 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(self, "could not allocate USB transfers, " - "err=%s\n", usb2_errstr(error)); - goto detach; - } - error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, - device_get_nameunit(self), USB_PRI_MED); - if (error) { - device_printf(self, "could not setup config thread!\n"); - goto detach; - } - - /* fork rest of the attach code */ - RAL_LOCK(sc); - ural_queue_command(sc, ural_attach_post, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - RAL_UNLOCK(sc); - return (0); - -detach: - ural_detach(self); - return (ENXIO); /* failure */ -} - -static void -ural_attach_post(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp; - struct ieee80211com *ic; - uint8_t bands; - - /* retrieve RT2570 rev. no */ - sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); - - /* retrieve MAC address and various other things from EEPROM */ - ural_read_eeprom(sc); - RAL_UNLOCK(sc); - - device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", - sc->asic_rev, ural_get_rf(sc->rf_rev)); - - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(sc->sc_dev, "can not if_alloc()\n"); - RAL_LOCK(sc); - return; - } - ic = ifp->if_l2com; - - ifp->if_softc = sc; - if_initname(ifp, "ural", device_get_unit(sc->sc_dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = ural_init; - ifp->if_ioctl = ural_ioctl; - ifp->if_start = ural_start; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; - IFQ_SET_READY(&ifp->if_snd); - - ic->ic_ifp = ifp; - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ - IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); - - /* set device capabilities */ - ic->ic_caps = - IEEE80211_C_STA /* station mode supported */ - | IEEE80211_C_IBSS /* IBSS mode supported */ - | IEEE80211_C_MONITOR /* monitor mode supported */ - | IEEE80211_C_HOSTAP /* HostAp mode supported */ - | IEEE80211_C_TXPMGT /* tx power management */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_BGSCAN /* bg scanning supported */ - | IEEE80211_C_WPA /* 802.11i */ - ; - - bands = 0; - setbit(&bands, IEEE80211_MODE_11B); - setbit(&bands, IEEE80211_MODE_11G); - if (sc->rf_rev == RAL_RF_5222) - setbit(&bands, IEEE80211_MODE_11A); - ieee80211_init_channels(ic, NULL, &bands); - - ieee80211_ifattach(ic); - ic->ic_newassoc = ural_newassoc; - ic->ic_raw_xmit = ural_raw_xmit; - ic->ic_node_alloc = ural_node_alloc; - ic->ic_scan_start = ural_scan_start; - ic->ic_scan_end = ural_scan_end; - ic->ic_set_channel = ural_set_channel; - - ic->ic_vap_create = ural_vap_create; - ic->ic_vap_delete = ural_vap_delete; - - sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); - - bpfattach(ifp, DLT_IEEE802_11_RADIO, - sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); - - sc->sc_rxtap_len = sizeof sc->sc_rxtap; - sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); - sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); - - sc->sc_txtap_len = sizeof sc->sc_txtap; - sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); - sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); - - if (bootverbose) - ieee80211_announce(ic); - - RAL_LOCK(sc); -} - -static int -ural_detach(device_t self) -{ - struct ural_softc *sc = device_get_softc(self); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic; - - /* wait for any post attach or other command to complete */ - usb2_proc_drain(&sc->sc_tq); - - /* stop all USB transfers */ - usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); - usb2_proc_free(&sc->sc_tq); - - /* free TX list, if any */ - RAL_LOCK(sc); - ural_unsetup_tx_list(sc); - RAL_UNLOCK(sc); - - if (ifp) { - ic = ifp->if_l2com; - bpfdetach(ifp); - ieee80211_ifdetach(ic); - if_free(ifp); - } - - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static struct ieee80211vap * -ural_vap_create(struct ieee80211com *ic, - const char name[IFNAMSIZ], int unit, int opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct ural_softc *sc = ic->ic_ifp->if_softc; - struct ural_vap *uvp; - struct ieee80211vap *vap; - - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap), - M_80211_VAP, M_NOWAIT | M_ZERO); - if (uvp == NULL) - return NULL; - vap = &uvp->vap; - /* enable s/w bmiss handling for sta mode */ - ieee80211_vap_setup(ic, vap, name, unit, opmode, - flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); - - /* override state transition machine */ - uvp->newstate = vap->iv_newstate; - vap->iv_newstate = ural_newstate; - - uvp->sc = sc; - usb2_callout_init_mtx(&uvp->amrr_ch, &sc->sc_mtx, 0); - ieee80211_amrr_init(&uvp->amrr, vap, - IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, - IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, - 1000 /* 1 sec */); - - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); - ic->ic_opmode = opmode; - return vap; -} - -static void -ural_vap_delete(struct ieee80211vap *vap) -{ - struct ural_vap *uvp = URAL_VAP(vap); - - usb2_callout_drain(&uvp->amrr_ch); - ieee80211_amrr_cleanup(&uvp->amrr); - ieee80211_vap_detach(vap); - free(uvp, M_80211_VAP); -} - -static void -ural_tx_free(struct ural_tx_data *data, int txerr) -{ - struct ural_softc *sc = data->sc; - - if (data->m != NULL) { - if (data->m->m_flags & M_TXCB) - ieee80211_process_callback(data->ni, data->m, - txerr ? ETIMEDOUT : 0); - m_freem(data->m); - data->m = NULL; - - ieee80211_free_node(data->ni); - data->ni = NULL; - } - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; -} - -static void -ural_setup_tx_list(struct ural_softc *sc) -{ - struct ural_tx_data *data; - int i; - - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - for (i = 0; i < RAL_TX_LIST_COUNT; i++) { - data = &sc->tx_data[i]; - - data->sc = sc; - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; - } -} - -static void -ural_unsetup_tx_list(struct ural_softc *sc) -{ - struct ural_tx_data *data; - int i; - - /* make sure any subsequent use of the queues will fail */ - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - /* free up all node references and mbufs */ - for (i = 0; i < RAL_TX_LIST_COUNT; i++) { - data = &sc->tx_data[i]; - - if (data->m != NULL) { - m_freem(data->m); - data->m = NULL; - } - if (data->ni != NULL) { - ieee80211_free_node(data->ni); - data->ni = NULL; - } - } -} - -static void -ural_task(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct ural_vap *uvp = URAL_VAP(vap); - const struct ieee80211_txparam *tp; - enum ieee80211_state ostate; - struct ieee80211_node *ni; - struct mbuf *m; - - ostate = vap->iv_state; - - switch (sc->sc_state) { - case IEEE80211_S_INIT: - if (ostate == IEEE80211_S_RUN) { - /* abort TSF synchronization */ - ural_write(sc, RAL_TXRX_CSR19, 0); - - /* force tx led to stop blinking */ - ural_write(sc, RAL_MAC_CSR20, 0); - } - break; - - case IEEE80211_S_RUN: - ni = vap->iv_bss; - - if (vap->iv_opmode != IEEE80211_M_MONITOR) { - ural_update_slot(ic->ic_ifp); - ural_set_txpreamble(sc); - ural_set_basicrates(sc, ic->ic_bsschan); - IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); - ural_set_bssid(sc, sc->sc_bssid); - } - - if (vap->iv_opmode == IEEE80211_M_HOSTAP || - vap->iv_opmode == IEEE80211_M_IBSS) { - m = ieee80211_beacon_alloc(ni, &uvp->bo); - if (m == NULL) { - device_printf(sc->sc_dev, - "could not allocate beacon\n"); - return; - } - - if (ural_tx_bcn(sc, m, ni) != 0) { - device_printf(sc->sc_dev, - "could not send beacon\n"); - return; - } - } - - /* make tx led blink on tx (controlled by ASIC) */ - ural_write(sc, RAL_MAC_CSR20, 1); - - if (vap->iv_opmode != IEEE80211_M_MONITOR) - ural_enable_tsf_sync(sc); - - /* enable automatic rate adaptation */ - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; - if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) - ural_amrr_start(sc, ni); - - break; - - default: - break; - } - - RAL_UNLOCK(sc); - IEEE80211_LOCK(ic); - uvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); - RAL_LOCK(sc); -} - -static void -ural_scantask(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - switch (sc->sc_scan_action) { - case URAL_SCAN_START: - /* abort TSF synchronization */ - DPRINTF("starting scan\n"); - ural_write(sc, RAL_TXRX_CSR19, 0); - ural_set_bssid(sc, ifp->if_broadcastaddr); - break; - - case URAL_SET_CHANNEL: - ural_set_chan(sc, ic->ic_curchan); - break; - - default: /* URAL_SCAN_END */ - DPRINTF("stopping scan\n"); - ural_enable_tsf_sync(sc); - ural_set_bssid(sc, sc->sc_bssid); - break; - } -} - -static int -ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct ural_vap *uvp = URAL_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct ural_softc *sc = ic->ic_ifp->if_softc; - - DPRINTF("%s -> %s\n", - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); - - RAL_LOCK(sc); - usb2_callout_stop(&uvp->amrr_ch); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - RAL_UNLOCK(sc); - - if (nstate == IEEE80211_S_INIT) { - uvp->newstate(vap, nstate, arg); - return 0; - } else { - RAL_LOCK(sc); - ural_queue_command(sc, ural_task, &sc->sc_task[0].hdr, - &sc->sc_task[1].hdr); - RAL_UNLOCK(sc); - return EINPROGRESS; - } -} - - -static void -ural_bulk_write_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_channel *c = ic->ic_curchan; - struct ural_tx_data *data; - struct mbuf *m; - unsigned int len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); - - /* free resources */ - data = xfer->priv_fifo; - ural_tx_free(data, 0); - xfer->priv_fifo = NULL; - - ifp->if_opackets++; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - data = STAILQ_FIRST(&sc->tx_q); - if (data) { - STAILQ_REMOVE_HEAD(&sc->tx_q, next); - m = data->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_copy_in(xfer->frbuffers, 0, &data->desc, - RAL_TX_DESC_SIZE); - usb2_m_copy_in(xfer->frbuffers, RAL_TX_DESC_SIZE, m, 0, - m->m_pkthdr.len); - - if (bpf_peers_present(ifp->if_bpf)) { - struct ural_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = data->rate; - tap->wt_chan_freq = htole16(c->ic_freq); - tap->wt_chan_flags = htole16(c->ic_flags); - tap->wt_antenna = sc->tx_ant; - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); - } - - /* xfer length needs to be a multiple of two! */ - len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; - if ((len % 64) == 0) - len += 2; - - DPRINTFN(11, "sending frame len=%u xferlen=%u\n", - m->m_pkthdr.len, len); - - xfer->frlengths[0] = len; - xfer->priv_fifo = data; - - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - DPRINTFN(11, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - data = xfer->priv_fifo; - if (data != NULL) { - ural_tx_free(data, xfer->error); - xfer->priv_fifo = NULL; - } - - if (xfer->error == USB_ERR_STALLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - if (xfer->error == USB_ERR_TIMEOUT) - device_printf(sc->sc_dev, "device timeout\n"); - break; - } -} - -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; - uint8_t rssi = 0; - unsigned int len; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); - - len = xfer->actlen; - if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) { - DPRINTF("%s: xfer too short %d\n", - device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; - goto tr_setup; - } - - len -= RAL_RX_DESC_SIZE; - /* rx descriptor is located at the end */ - usb2_copy_out(xfer->frbuffers, len, &sc->sc_rx_desc, - RAL_RX_DESC_SIZE); - - rssi = URAL_RSSI(sc->sc_rx_desc.rssi); - 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(5, "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; - } - usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *), len); - - /* finalize mbuf */ - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; - - if (bpf_peers_present(ifp->if_bpf)) { - struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; - - tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; - tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, - (flags & RAL_RX_OFDM) ? - IEEE80211_T_OFDM : IEEE80211_T_CCK); - tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); - tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); - tap->wr_antenna = sc->rx_ant; - tap->wr_antsignal = rssi; - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); - } - /* Strip trailing 802.11 MAC FCS. */ - m_adj(m, -IEEE80211_CRC_LEN); - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - 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) { - RAL_UNLOCK(sc); - ni = ieee80211_find_rxnode(ic, - mtod(m, struct ieee80211_frame_min *)); - if (ni != NULL) { - (void) ieee80211_input(ni, m, rssi, - RAL_NOISE_FLOOR, 0); - ieee80211_free_node(ni); - } else - (void) ieee80211_input_all(ic, m, rssi, - RAL_NOISE_FLOOR, 0); - RAL_LOCK(sc); - } - return; - - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - return; - } -} - -static uint8_t -ural_plcp_signal(int rate) -{ - switch (rate) { - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ - case 12: return 0xb; - case 18: return 0xf; - case 24: return 0xa; - case 36: return 0xe; - case 48: return 0x9; - case 72: return 0xd; - case 96: return 0x8; - case 108: return 0xc; - - /* CCK rates (NB: not IEEE std, device-specific) */ - case 2: return 0x0; - case 4: return 0x1; - case 11: return 0x2; - case 22: return 0x3; - } - return 0xff; /* XXX unsupported/unknown rate */ -} - -static void -ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, - uint32_t flags, int len, int rate) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint16_t plcp_length; - int remainder; - - desc->flags = htole32(flags); - desc->flags |= htole32(RAL_TX_NEWSEQ); - desc->flags |= htole32(len << 16); - - desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); - desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); - - /* setup PLCP fields */ - desc->plcp_signal = ural_plcp_signal(rate); - desc->plcp_service = 4; - - len += IEEE80211_CRC_LEN; - if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { - desc->flags |= htole32(RAL_TX_OFDM); - - plcp_length = len & 0xfff; - desc->plcp_length_hi = plcp_length >> 6; - desc->plcp_length_lo = plcp_length & 0x3f; - } else { - plcp_length = (16 * len + rate - 1) / rate; - if (rate == 22) { - remainder = (16 * len) % 22; - if (remainder != 0 && remainder < 7) - desc->plcp_service |= RAL_PLCP_LENGEXT; - } - desc->plcp_length_hi = plcp_length >> 8; - desc->plcp_length_lo = plcp_length & 0xff; - - if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) - desc->plcp_signal |= 0x08; - } - - desc->iv = 0; - desc->eiv = 0; -} - -#define RAL_TX_TIMEOUT 5000 - -static int -ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = sc->sc_ifp; - const struct ieee80211_txparam *tp; - struct ural_tx_data *data; - - if (sc->tx_nfree == 0) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - m_freem(m0); - ieee80211_free_node(ni); - return EIO; - } - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; - - data->m = m0; - data->ni = ni; - data->rate = tp->mgmtrate; - - ural_setup_tx_desc(sc, &data->desc, - RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, - tp->mgmtrate); - - DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", - m0->m_pkthdr.len, tp->mgmtrate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); - - return (0); -} - -static int -ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - const struct ieee80211_txparam *tp; - struct ural_tx_data *data; - struct ieee80211_frame *wh; - struct ieee80211_key *k; - uint32_t flags; - uint16_t dur; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; - - wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return ENOBUFS; - } - wh = mtod(m0, struct ieee80211_frame *); - } - - data->m = m0; - data->ni = ni; - data->rate = tp->mgmtrate; - - flags = 0; - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - flags |= RAL_TX_ACK; - - dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, - ic->ic_flags & IEEE80211_F_SHPREAMBLE); - *(uint16_t *)wh->i_dur = htole16(dur); - - /* tell hardware to add timestamp for probe responses */ - if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == - IEEE80211_FC0_TYPE_MGT && - (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == - IEEE80211_FC0_SUBTYPE_PROBE_RESP) - flags |= RAL_TX_TIMESTAMP; - } - - ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); - - DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", - m0->m_pkthdr.len, tp->mgmtrate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); - - return 0; -} - -static int -ural_sendprot(struct ural_softc *sc, - const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) -{ - struct ieee80211com *ic = ni->ni_ic; - const struct ieee80211_frame *wh; - struct ural_tx_data *data; - struct mbuf *mprot; - int protrate, ackrate, pktlen, flags, isshort; - uint16_t dur; - - KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, - ("protection %d", prot)); - - wh = mtod(m, const struct ieee80211_frame *); - pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; - - protrate = ieee80211_ctl_rate(sc->sc_rates, rate); - ackrate = ieee80211_ack_rate(sc->sc_rates, rate); - - isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; - dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); - + ieee80211_ack_duration(sc->sc_rates, rate, isshort); - flags = RAL_TX_RETRY(7); - if (prot == IEEE80211_PROT_RTSCTS) { - /* NB: CTS is the same size as an ACK */ - dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); - flags |= RAL_TX_ACK; - mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); - } else { - mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); - } - if (mprot == NULL) { - /* XXX stat + msg */ - return ENOBUFS; - } - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - data->m = mprot; - data->ni = ieee80211_ref_node(ni); - data->rate = protrate; - ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); - - return 0; -} - -static int -ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - const struct ieee80211_bpf_params *params) -{ - struct ural_tx_data *data; - uint32_t flags; - int error; - int rate; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - KASSERT(params != NULL, ("no raw xmit params")); - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - rate = params->ibp_rate0 & IEEE80211_RATE_VAL; - /* XXX validate */ - if (rate == 0) { - m_freem(m0); - return EINVAL; - } - flags = 0; - if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) - flags |= RAL_TX_ACK; - if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { - error = ural_sendprot(sc, m0, ni, - params->ibp_flags & IEEE80211_BPF_RTS ? - IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, - rate); - if (error) { - m_freem(m0); - return error; - } - flags |= RAL_TX_IFS_SIFS; - } - - data->m = m0; - data->ni = ni; - data->rate = rate; - - /* XXX need to setup descriptor ourself */ - ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); - - DPRINTFN(10, "sending raw frame len=%u rate=%u\n", - m0->m_pkthdr.len, rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); - - return 0; -} - -static int -ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct ural_tx_data *data; - struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - uint32_t flags = 0; - uint16_t dur; - int error, rate; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - wh = mtod(m0, struct ieee80211_frame *); - - tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; - if (IEEE80211_IS_MULTICAST(wh->i_addr1)) - rate = tp->mcastrate; - else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) - rate = tp->ucastrate; - else - rate = ni->ni_txrate; - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return ENOBUFS; - } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - int prot = IEEE80211_PROT_NONE; - if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) - prot = IEEE80211_PROT_RTSCTS; - else if ((ic->ic_flags & IEEE80211_F_USEPROT) && - ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) - prot = ic->ic_protmode; - if (prot != IEEE80211_PROT_NONE) { - error = ural_sendprot(sc, m0, ni, prot, rate); - if (error) { - m_freem(m0); - return error; - } - flags |= RAL_TX_IFS_SIFS; - } - } - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - - data->m = m0; - data->ni = ni; - data->rate = rate; - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - flags |= RAL_TX_ACK; - flags |= RAL_TX_RETRY(7); - - dur = ieee80211_ack_duration(sc->sc_rates, rate, - ic->ic_flags & IEEE80211_F_SHPREAMBLE); - *(uint16_t *)wh->i_dur = htole16(dur); - } - - ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); - - DPRINTFN(10, "sending data frame len=%u rate=%u\n", - m0->m_pkthdr.len, rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); - - return 0; -} - -static void -ural_start(struct ifnet *ifp) -{ - struct ural_softc *sc = ifp->if_softc; - struct ieee80211_node *ni; - struct mbuf *m; - - RAL_LOCK(sc); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - RAL_UNLOCK(sc); - return; - } - for (;;) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - if (sc->tx_nfree == 0) { - IFQ_DRV_PREPEND(&ifp->if_snd, m); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - m = ieee80211_encap(ni, m); - if (m == NULL) { - ieee80211_free_node(ni); - continue; - } - if (ural_tx_data(sc, m, ni) != 0) { - ieee80211_free_node(ni); - ifp->if_oerrors++; - break; - } - } - RAL_UNLOCK(sc); -} - -static int -ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct ural_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - RAL_LOCK(sc); - if (ifp->if_flags & IFF_UP) { - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - ural_queue_command(sc, ural_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - startall = 1; - } else - ural_queue_command(sc, ural_promisctask, - &sc->sc_promisctask[0].hdr, - &sc->sc_promisctask[1].hdr); - } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - ural_queue_command(sc, ural_stop_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - } - } - RAL_UNLOCK(sc); - if (startall) - ieee80211_start_all(ic); - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - default: - error = ether_ioctl(ifp, cmd, data); - break; - } - return error; -} - -static void -ural_set_testmode(struct ural_softc *sc) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RAL_VENDOR_REQUEST; - USETW(req.wValue, 4); - USETW(req.wIndex, 1); - USETW(req.wLength, 0); - - error = ural_do_request(sc, &req, NULL); - if (error != 0) { - device_printf(sc->sc_dev, "could not set test mode: %s\n", - usb2_errstr(error)); - } -} - -static void -ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = RAL_READ_EEPROM; - USETW(req.wValue, 0); - USETW(req.wIndex, addr); - USETW(req.wLength, len); - - error = ural_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, "could not read EEPROM: %s\n", - usb2_errstr(error)); - } -} - -static uint16_t -ural_read(struct ural_softc *sc, uint16_t reg) -{ - struct usb2_device_request req; - usb2_error_t error; - uint16_t val; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = RAL_READ_MAC; - USETW(req.wValue, 0); - USETW(req.wIndex, reg); - USETW(req.wLength, sizeof (uint16_t)); - - error = ural_do_request(sc, &req, &val); - if (error != 0) { - device_printf(sc->sc_dev, "could not read MAC register: %s\n", - usb2_errstr(error)); - return 0; - } - - return le16toh(val); -} - -static void -ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = RAL_READ_MULTI_MAC; - USETW(req.wValue, 0); - USETW(req.wIndex, reg); - USETW(req.wLength, len); - - error = ural_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, "could not read MAC register: %s\n", - usb2_errstr(error)); - } -} - -static void -ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RAL_WRITE_MAC; - USETW(req.wValue, val); - USETW(req.wIndex, reg); - USETW(req.wLength, 0); - - error = ural_do_request(sc, &req, NULL); - if (error != 0) { - device_printf(sc->sc_dev, "could not write MAC register: %s\n", - usb2_errstr(error)); - } -} - -static void -ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = RAL_WRITE_MULTI_MAC; - USETW(req.wValue, 0); - USETW(req.wIndex, reg); - USETW(req.wLength, len); - - error = ural_do_request(sc, &req, buf); - if (error != 0) { - device_printf(sc->sc_dev, "could not write MAC register: %s\n", - usb2_errstr(error)); - } -} - -static void -ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) -{ - uint16_t tmp; - int ntries; - - for (ntries = 0; ntries < 100; ntries++) { - if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) - break; - if (ural_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not write to BBP\n"); - return; - } - - tmp = reg << 8 | val; - ural_write(sc, RAL_PHY_CSR7, tmp); -} - -static uint8_t -ural_bbp_read(struct ural_softc *sc, uint8_t reg) -{ - uint16_t val; - int ntries; - - val = RAL_BBP_WRITE | reg << 8; - ural_write(sc, RAL_PHY_CSR7, val); - - for (ntries = 0; ntries < 100; ntries++) { - if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) - break; - if (ural_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not read BBP\n"); - return 0; - } - - return ural_read(sc, RAL_PHY_CSR7) & 0xff; -} - -static void -ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) -{ - uint32_t tmp; - int ntries; - - for (ntries = 0; ntries < 100; ntries++) { - if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) - break; - if (ural_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "could not write to RF\n"); - return; - } - - tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); - ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); - ural_write(sc, RAL_PHY_CSR10, tmp >> 16); - - /* remember last written value in sc */ - sc->rf_regs[reg] = val; - - DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); -} - -/* ARGUSED */ -static struct ieee80211_node * -ural_node_alloc(struct ieee80211vap *vap __unused, - const uint8_t mac[IEEE80211_ADDR_LEN] __unused) -{ - struct ural_node *un; - - un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); - return un != NULL ? &un->ni : NULL; -} - -static void -ural_newassoc(struct ieee80211_node *ni, int isnew) -{ - struct ieee80211vap *vap = ni->ni_vap; - - ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); -} - -static void -ural_scan_start(struct ieee80211com *ic) -{ - struct ural_softc *sc = ic->ic_ifp->if_softc; - - RAL_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = URAL_SCAN_START; - ural_queue_command(sc, ural_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - RAL_UNLOCK(sc); - -} - -static void -ural_scan_end(struct ieee80211com *ic) -{ - struct ural_softc *sc = ic->ic_ifp->if_softc; - - RAL_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = URAL_SCAN_END; - ural_queue_command(sc, ural_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - RAL_UNLOCK(sc); - -} - -static void -ural_set_channel(struct ieee80211com *ic) -{ - struct ural_softc *sc = ic->ic_ifp->if_softc; - - RAL_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = URAL_SET_CHANNEL; - ural_queue_command(sc, ural_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - - sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); - RAL_UNLOCK(sc); -} - -static void -ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint8_t power, tmp; - int i, chan; - - chan = ieee80211_chan2ieee(ic, c); - if (chan == 0 || chan == IEEE80211_CHAN_ANY) - return; - - if (IEEE80211_IS_CHAN_2GHZ(c)) - power = min(sc->txpow[chan - 1], 31); - else - power = 31; - - /* adjust txpower using ifconfig settings */ - power -= (100 - ic->ic_txpowlimit) / 8; - - DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); - - switch (sc->rf_rev) { - case RAL_RF_2522: - ural_rf_write(sc, RAL_RF1, 0x00814); - ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); - break; - - case RAL_RF_2523: - ural_rf_write(sc, RAL_RF1, 0x08804); - ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); - ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); - break; - - case RAL_RF_2524: - ural_rf_write(sc, RAL_RF1, 0x0c808); - ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); - ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); - break; - - case RAL_RF_2525: - ural_rf_write(sc, RAL_RF1, 0x08808); - ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); - ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); - - ural_rf_write(sc, RAL_RF1, 0x08808); - ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); - ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); - break; - - case RAL_RF_2525E: - ural_rf_write(sc, RAL_RF1, 0x08808); - ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); - ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); - break; - - case RAL_RF_2526: - ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); - ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); - ural_rf_write(sc, RAL_RF1, 0x08804); - - ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); - ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); - break; - - /* dual-band RF */ - case RAL_RF_5222: - for (i = 0; ural_rf5222[i].chan != chan; i++); - - ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); - ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); - ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); - ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); - break; - } - - if (ic->ic_opmode != IEEE80211_M_MONITOR && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) { - /* set Japan filter bit for channel 14 */ - tmp = ural_bbp_read(sc, 70); - - tmp &= ~RAL_JAPAN_FILTER; - if (chan == 14) - tmp |= RAL_JAPAN_FILTER; - - ural_bbp_write(sc, 70, tmp); - - /* clear CRC errors */ - ural_read(sc, RAL_STA_CSR0); - - ural_pause(sc, hz / 100); - ural_disable_rf_tune(sc); - } - - /* XXX doesn't belong here */ - /* update basic rate set */ - ural_set_basicrates(sc, c); -} - -/* - * Disable RF auto-tuning. - */ -static void -ural_disable_rf_tune(struct ural_softc *sc) -{ - uint32_t tmp; - - if (sc->rf_rev != RAL_RF_2523) { - tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; - ural_rf_write(sc, RAL_RF1, tmp); - } - - tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; - ural_rf_write(sc, RAL_RF3, tmp); - - DPRINTFN(2, "disabling RF autotune\n"); -} - -/* - * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF - * synchronization. - */ -static void -ural_enable_tsf_sync(struct ural_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - uint16_t logcwmin, preload, tmp; - - /* first, disable TSF synchronization */ - ural_write(sc, RAL_TXRX_CSR19, 0); - - tmp = (16 * vap->iv_bss->ni_intval) << 4; - ural_write(sc, RAL_TXRX_CSR18, tmp); - - logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; - preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; - tmp = logcwmin << 12 | preload; - ural_write(sc, RAL_TXRX_CSR20, tmp); - - /* finally, enable TSF synchronization */ - tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; - if (ic->ic_opmode == IEEE80211_M_STA) - tmp |= RAL_ENABLE_TSF_SYNC(1); - else - tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; - ural_write(sc, RAL_TXRX_CSR19, tmp); - - DPRINTF("enabling TSF synchronization\n"); -} - -#define RAL_RXTX_TURNAROUND 5 /* us */ -static void -ural_update_slot(struct ifnet *ifp) -{ - struct ural_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - uint16_t slottime, sifs, eifs; - - slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; - - /* - * These settings may sound a bit inconsistent but this is what the - * reference driver does. - */ - if (ic->ic_curmode == IEEE80211_MODE_11B) { - sifs = 16 - RAL_RXTX_TURNAROUND; - eifs = 364; - } else { - sifs = 10 - RAL_RXTX_TURNAROUND; - eifs = 64; - } - - ural_write(sc, RAL_MAC_CSR10, slottime); - ural_write(sc, RAL_MAC_CSR11, sifs); - ural_write(sc, RAL_MAC_CSR12, eifs); -} - -static void -ural_set_txpreamble(struct ural_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint16_t tmp; - - tmp = ural_read(sc, RAL_TXRX_CSR10); - - tmp &= ~RAL_SHORT_PREAMBLE; - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - tmp |= RAL_SHORT_PREAMBLE; - - ural_write(sc, RAL_TXRX_CSR10, tmp); -} - -static void -ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) -{ - /* XXX wrong, take from rate set */ - /* update basic rate set */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - /* 11a basic rates: 6, 12, 24Mbps */ - ural_write(sc, RAL_TXRX_CSR11, 0x150); - } else if (IEEE80211_IS_CHAN_ANYG(c)) { - /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ - ural_write(sc, RAL_TXRX_CSR11, 0x15f); - } else { - /* 11b basic rates: 1, 2Mbps */ - ural_write(sc, RAL_TXRX_CSR11, 0x3); - } -} - -static void -ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) -{ - uint16_t tmp; - - tmp = bssid[0] | bssid[1] << 8; - ural_write(sc, RAL_MAC_CSR5, tmp); - - tmp = bssid[2] | bssid[3] << 8; - ural_write(sc, RAL_MAC_CSR6, tmp); - - tmp = bssid[4] | bssid[5] << 8; - ural_write(sc, RAL_MAC_CSR7, tmp); - - DPRINTF("setting BSSID to %6D\n", bssid, ":"); -} - -static void -ural_set_macaddr(struct ural_softc *sc, uint8_t *addr) -{ - uint16_t tmp; - - tmp = addr[0] | addr[1] << 8; - ural_write(sc, RAL_MAC_CSR2, tmp); - - tmp = addr[2] | addr[3] << 8; - ural_write(sc, RAL_MAC_CSR3, tmp); - - tmp = addr[4] | addr[5] << 8; - ural_write(sc, RAL_MAC_CSR4, tmp); - - DPRINTF("setting MAC address to %6D\n", addr, ":"); -} - -static void -ural_promisctask(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - - tmp = ural_read(sc, RAL_TXRX_CSR2); - - tmp &= ~RAL_DROP_NOT_TO_ME; - if (!(ifp->if_flags & IFF_PROMISC)) - tmp |= RAL_DROP_NOT_TO_ME; - - ural_write(sc, RAL_TXRX_CSR2, tmp); - - DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? - "entering" : "leaving"); -} - -static const char * -ural_get_rf(int rev) -{ - switch (rev) { - case RAL_RF_2522: return "RT2522"; - case RAL_RF_2523: return "RT2523"; - case RAL_RF_2524: return "RT2524"; - case RAL_RF_2525: return "RT2525"; - case RAL_RF_2525E: return "RT2525e"; - case RAL_RF_2526: return "RT2526"; - case RAL_RF_5222: return "RT5222"; - default: return "unknown"; - } -} - -static void -ural_read_eeprom(struct ural_softc *sc) -{ - uint16_t val; - - ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); - val = le16toh(val); - sc->rf_rev = (val >> 11) & 0x7; - sc->hw_radio = (val >> 10) & 0x1; - sc->led_mode = (val >> 6) & 0x7; - sc->rx_ant = (val >> 4) & 0x3; - sc->tx_ant = (val >> 2) & 0x3; - sc->nb_ant = val & 0x3; - - /* read MAC address */ - ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_bssid, 6); - - /* read default values for BBP registers */ - ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); - - /* read Tx power for all b/g channels */ - ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); -} - -static int -ural_bbp_init(struct ural_softc *sc) -{ -#define N(a) (sizeof (a) / sizeof ((a)[0])) - int i, ntries; - - /* wait for BBP to be ready */ - for (ntries = 0; ntries < 100; ntries++) { - if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) - break; - if (ural_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, "timeout waiting for BBP\n"); - return EIO; - } - - /* initialize BBP registers to default values */ - for (i = 0; i < N(ural_def_bbp); i++) - ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); - -#if 0 - /* initialize BBP registers to values stored in EEPROM */ - for (i = 0; i < 16; i++) { - if (sc->bbp_prom[i].reg == 0xff) - continue; - ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); - } -#endif - - return 0; -#undef N -} - -static void -ural_set_txantenna(struct ural_softc *sc, int antenna) -{ - uint16_t tmp; - uint8_t tx; - - tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; - if (antenna == 1) - tx |= RAL_BBP_ANTA; - else if (antenna == 2) - tx |= RAL_BBP_ANTB; - else - tx |= RAL_BBP_DIVERSITY; - - /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ - if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || - sc->rf_rev == RAL_RF_5222) - tx |= RAL_BBP_FLIPIQ; - - ural_bbp_write(sc, RAL_BBP_TX, tx); - - /* update values in PHY_CSR5 and PHY_CSR6 */ - tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; - ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); - - tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; - ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); -} - -static void -ural_set_rxantenna(struct ural_softc *sc, int antenna) -{ - uint8_t rx; - - rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; - if (antenna == 1) - rx |= RAL_BBP_ANTA; - else if (antenna == 2) - rx |= RAL_BBP_ANTB; - else - rx |= RAL_BBP_DIVERSITY; - - /* need to force no I/Q flip for RF 2525e and 2526 */ - if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) - rx &= ~RAL_BBP_FLIPIQ; - - ural_bbp_write(sc, RAL_BBP_RX, rx); -} - -static void -ural_init_task(struct usb2_proc_msg *pm) -{ -#define N(a) (sizeof (a) / sizeof ((a)[0])) - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint16_t tmp; - int i, ntries; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - ural_set_testmode(sc); - ural_write(sc, 0x308, 0x00f0); /* XXX magic */ - - ural_stop_task(pm); - - /* initialize MAC registers to default values */ - for (i = 0; i < N(ural_def_mac); i++) - ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); - - /* wait for BBP and RF to wake up (this can take a long time!) */ - for (ntries = 0; ntries < 100; ntries++) { - tmp = ural_read(sc, RAL_MAC_CSR17); - if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == - (RAL_BBP_AWAKE | RAL_RF_AWAKE)) - break; - if (ural_pause(sc, hz / 100)) - break; - } - if (ntries == 100) { - device_printf(sc->sc_dev, - "timeout waiting for BBP/RF to wakeup\n"); - goto fail; - } - - /* we're ready! */ - ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); - - /* set basic rate set (will be updated later) */ - ural_write(sc, RAL_TXRX_CSR11, 0x15f); - - if (ural_bbp_init(sc) != 0) - goto fail; - - ural_set_chan(sc, ic->ic_curchan); - - /* clear statistic registers (STA_CSR0 to STA_CSR10) */ - ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); - - ural_set_txantenna(sc, sc->tx_ant); - ural_set_rxantenna(sc, sc->rx_ant); - - IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); - ural_set_macaddr(sc, ic->ic_myaddr); - - /* - * Allocate Tx and Rx xfer queues. - */ - ural_setup_tx_list(sc); - - /* kick Rx */ - tmp = RAL_DROP_PHY | RAL_DROP_CRC; - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; - if (ic->ic_opmode != IEEE80211_M_HOSTAP) - tmp |= RAL_DROP_TODS; - if (!(ifp->if_flags & IFF_PROMISC)) - tmp |= RAL_DROP_NOT_TO_ME; - } - ural_write(sc, RAL_TXRX_CSR2, tmp); - - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - usb2_transfer_start(sc->sc_xfer[URAL_BULK_RD]); - return; - -fail: ural_stop_task(pm); -#undef N -} - -static void -ural_init(void *priv) -{ - struct ural_softc *sc = priv; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - RAL_LOCK(sc); - ural_queue_command(sc, ural_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - RAL_UNLOCK(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vap's */ -} - -static void -ural_stop_task(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - /* - * Drain all the transfers, if not already drained: - */ - RAL_UNLOCK(sc); - usb2_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); - usb2_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); - RAL_LOCK(sc); - - ural_unsetup_tx_list(sc); - - /* disable Rx */ - ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); - /* reset ASIC and BBP (but won't reset MAC registers!) */ - ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); - /* wait a little */ - ural_pause(sc, hz / 10); - ural_write(sc, RAL_MAC_CSR1, 0); -} - -static int -ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ural_softc *sc = ifp->if_softc; - - RAL_LOCK(sc); - /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - RAL_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } - if (sc->tx_nfree == 0) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - RAL_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return EIO; - } - - ifp->if_opackets++; - - if (params == NULL) { - /* - * Legacy path; interpret frame contents to decide - * precisely how to send the frame. - */ - if (ural_tx_mgt(sc, m, ni) != 0) - goto bad; - } else { - /* - * Caller supplied explicit parameters to use in - * sending the frame. - */ - if (ural_tx_raw(sc, m, ni, params) != 0) - goto bad; - } - RAL_UNLOCK(sc); - return 0; -bad: - ifp->if_oerrors++; - RAL_UNLOCK(sc); - ieee80211_free_node(ni); - return EIO; /* XXX */ -} - -static void -ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ural_vap *uvp = URAL_VAP(vap); - - /* clear statistic registers (STA_CSR0 to STA_CSR10) */ - ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); - - ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni); - - usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); -} - -static void -ural_amrr_timeout(void *arg) -{ - struct ural_vap *uvp = arg; - struct ural_softc *sc = uvp->sc; - - ural_queue_command(sc, ural_amrr_task, - &uvp->amrr_task[0].hdr, &uvp->amrr_task[1].hdr); -} - -static void -ural_amrr_task(struct usb2_proc_msg *pm) -{ - struct ural_task *task = (struct ural_task *)pm; - struct ural_softc *sc = task->sc; - 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); - struct ieee80211_node *ni = vap->iv_bss; - int ok, fail; - - /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ - ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); - - ok = sc->sta[7] + /* TX ok w/o retry */ - sc->sta[8]; /* TX ok w/ retry */ - fail = sc->sta[9]; /* TX retry-fail count */ - - ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, - ok+fail, ok, sc->sta[8] + fail); - (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn); - - ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ - - usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); -} - -static int -ural_pause(struct ural_softc *sc, int timeout) -{ - if (usb2_proc_is_gone(&sc->sc_tq)) - return (1); - - usb2_pause_mtx(&sc->sc_mtx, timeout); - return (0); -} - -static void -ural_queue_command(struct ural_softc *sc, usb2_proc_callback_t *fn, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) -{ - struct ural_task *task; - - RAL_LOCK_ASSERT(sc, MA_OWNED); - - if (usb2_proc_is_gone(&sc->sc_tq)) { - DPRINTF("proc is gone\n"); - return; /* nothing to do */ - } - /* - * NOTE: The task cannot get executed before we drop the - * "sc_mtx" mutex. It is safe to update fields in the message - * structure after that the message got queued. - */ - task = (struct ural_task *) - usb2_proc_msignal(&sc->sc_tq, t0, t1); - - /* Setup callback and softc pointers */ - task->hdr.pm_callback = fn; - task->sc = sc; - - /* - * Init and stop must be synchronous! - */ - if ((fn == ural_init_task) || (fn == ural_stop_task)) - usb2_proc_mwait(&sc->sc_tq, t0, t1); -} - diff --git a/sys/dev/usb2/wlan/if_uralreg.h b/sys/dev/usb2/wlan/if_uralreg.h deleted file mode 100644 index 042cf5a..0000000 --- a/sys/dev/usb2/wlan/if_uralreg.h +++ /dev/null @@ -1,211 +0,0 @@ -/* $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 */ - -#define RAL_CONFIG_NO 1 -#define RAL_IFACE_INDEX 0 - -#define RAL_VENDOR_REQUEST 0x01 -#define RAL_WRITE_MAC 0x02 -#define RAL_READ_MAC 0x03 -#define RAL_WRITE_MULTI_MAC 0x06 -#define RAL_READ_MULTI_MAC 0x07 -#define RAL_READ_EEPROM 0x09 - -/* - * MAC registers. - */ -#define RAL_MAC_CSR0 0x0400 /* ASIC Version */ -#define RAL_MAC_CSR1 0x0402 /* System control */ -#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */ -#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */ -#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */ -#define RAL_MAC_CSR5 0x040a /* BSSID0 */ -#define RAL_MAC_CSR6 0x040c /* BSSID1 */ -#define RAL_MAC_CSR7 0x040e /* BSSID2 */ -#define RAL_MAC_CSR8 0x0410 /* Max frame length */ -#define RAL_MAC_CSR9 0x0412 /* Timer control */ -#define RAL_MAC_CSR10 0x0414 /* Slot time */ -#define RAL_MAC_CSR11 0x0416 /* IFS */ -#define RAL_MAC_CSR12 0x0418 /* EIFS */ -#define RAL_MAC_CSR13 0x041a /* Power mode0 */ -#define RAL_MAC_CSR14 0x041c /* Power mode1 */ -#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */ -#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */ -#define RAL_MAC_CSR17 0x0422 /* Power state control */ -#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */ -#define RAL_MAC_CSR19 0x0426 /* GPIO control */ -#define RAL_MAC_CSR20 0x0428 /* LED control0 */ -#define RAL_MAC_CSR22 0x042c /* XXX not documented */ - -/* - * Tx/Rx Registers. - */ -#define RAL_TXRX_CSR0 0x0440 /* Security control */ -#define RAL_TXRX_CSR2 0x0444 /* Rx control */ -#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */ -#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */ -#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */ -#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */ -#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */ -#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */ -#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */ -#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */ -#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */ -#define RAL_TXRX_CSR21 0x046a /* XXX not documented */ - -/* - * Security registers. - */ -#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */ - -/* - * PHY registers. - */ -#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */ -#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */ -#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */ -#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */ -#define RAL_PHY_CSR7 0x04ce /* BBP serial control */ -#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */ -#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */ -#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */ - -/* - * Statistics registers. - */ -#define RAL_STA_CSR0 0x04e0 /* FCS error */ - - -#define RAL_DISABLE_RX (1 << 0) -#define RAL_DROP_CRC (1 << 1) -#define RAL_DROP_PHY (1 << 2) -#define RAL_DROP_CTL (1 << 3) -#define RAL_DROP_NOT_TO_ME (1 << 4) -#define RAL_DROP_TODS (1 << 5) -#define RAL_DROP_BAD_VERSION (1 << 6) -#define RAL_DROP_MULTICAST (1 << 9) -#define RAL_DROP_BROADCAST (1 << 10) - -#define RAL_SHORT_PREAMBLE (1 << 2) - -#define RAL_RESET_ASIC (1 << 0) -#define RAL_RESET_BBP (1 << 1) -#define RAL_HOST_READY (1 << 2) - -#define RAL_ENABLE_TSF (1 << 0) -#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1) -#define RAL_ENABLE_TBCN (1 << 3) -#define RAL_ENABLE_BEACON_GENERATOR (1 << 4) - -#define RAL_RF_AWAKE (3 << 7) -#define RAL_BBP_AWAKE (3 << 5) - -#define RAL_BBP_WRITE (1 << 15) -#define RAL_BBP_BUSY (1 << 0) - -#define RAL_RF1_AUTOTUNE 0x08000 -#define RAL_RF3_AUTOTUNE 0x00040 - -#define RAL_RF_2522 0x00 -#define RAL_RF_2523 0x01 -#define RAL_RF_2524 0x02 -#define RAL_RF_2525 0x03 -#define RAL_RF_2525E 0x04 -#define RAL_RF_2526 0x05 -/* dual-band RF */ -#define RAL_RF_5222 0x10 - -#define RAL_BBP_VERSION 0 -#define RAL_BBP_TX 2 -#define RAL_BBP_RX 14 - -#define RAL_BBP_ANTA 0x00 -#define RAL_BBP_DIVERSITY 0x01 -#define RAL_BBP_ANTB 0x02 -#define RAL_BBP_ANTMASK 0x03 -#define RAL_BBP_FLIPIQ 0x04 - -#define RAL_JAPAN_FILTER 0x08 - -struct ural_tx_desc { - uint32_t flags; -#define RAL_TX_RETRY(x) ((x) << 4) -#define RAL_TX_MORE_FRAG (1 << 8) -#define RAL_TX_ACK (1 << 9) -#define RAL_TX_TIMESTAMP (1 << 10) -#define RAL_TX_OFDM (1 << 11) -#define RAL_TX_NEWSEQ (1 << 12) - -#define RAL_TX_IFS_MASK 0x00006000 -#define RAL_TX_IFS_BACKOFF (0 << 13) -#define RAL_TX_IFS_SIFS (1 << 13) -#define RAL_TX_IFS_NEWBACKOFF (2 << 13) -#define RAL_TX_IFS_NONE (3 << 13) - - uint16_t wme; -#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12) -#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8) -#define RAL_AIFSN(x) (((x) & 0x3) << 6) -#define RAL_IVOFFSET(x) (((x) & 0x3f)) - - uint16_t reserved1; - uint8_t plcp_signal; - uint8_t plcp_service; -#define RAL_PLCP_LENGEXT 0x80 - - uint8_t plcp_length_lo; - uint8_t plcp_length_hi; - uint32_t iv; - uint32_t eiv; -} __packed; - -struct ural_rx_desc { - uint32_t flags; -#define RAL_RX_CRC_ERROR (1 << 5) -#define RAL_RX_OFDM (1 << 6) -#define RAL_RX_PHY_ERROR (1 << 7) - - uint8_t rssi; - uint8_t rate; - uint16_t reserved; - - uint32_t iv; - uint32_t eiv; -} __packed; - -#define RAL_RF_LOBUSY (1 << 15) -#define RAL_RF_BUSY (1 << 31) -#define RAL_RF_20BIT (20 << 24) - -#define RAL_RF1 0 -#define RAL_RF2 2 -#define RAL_RF3 1 -#define RAL_RF4 3 - -#define RAL_EEPROM_ADDRESS 0x0004 -#define RAL_EEPROM_TXPOWER 0x003c -#define RAL_EEPROM_CONFIG0 0x0016 -#define RAL_EEPROM_BBP_BASE 0x001c diff --git a/sys/dev/usb2/wlan/if_uralvar.h b/sys/dev/usb2/wlan/if_uralvar.h deleted file mode 100644 index c7e5469..0000000 --- a/sys/dev/usb2/wlan/if_uralvar.h +++ /dev/null @@ -1,155 +0,0 @@ -/* $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 RAL_TX_LIST_COUNT 8 - -#define URAL_SCAN_START 1 -#define URAL_SCAN_END 2 -#define URAL_SET_CHANNEL 3 - - -struct ural_rx_radiotap_header { - struct ieee80211_radiotap_header wr_ihdr; - uint8_t wr_flags; - uint8_t wr_rate; - uint16_t wr_chan_freq; - uint16_t wr_chan_flags; - uint8_t wr_antenna; - uint8_t wr_antsignal; -}; - -#define RAL_RX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA) | \ - (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) - -struct ural_tx_radiotap_header { - struct ieee80211_radiotap_header wt_ihdr; - uint8_t wt_flags; - uint8_t wt_rate; - uint16_t wt_chan_freq; - uint16_t wt_chan_flags; - uint8_t wt_antenna; -}; - -#define RAL_TX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA)) - -struct ural_softc; - -struct ural_task { - struct usb2_proc_msg hdr; - struct ural_softc *sc; -}; - -struct ural_tx_data { - STAILQ_ENTRY(ural_tx_data) next; - struct ural_softc *sc; - struct ural_tx_desc desc; - struct mbuf *m; - struct ieee80211_node *ni; - int rate; -}; -typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead; - -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 ural_softc *sc; - struct ieee80211_beacon_offsets bo; - struct ieee80211_amrr amrr; - struct usb2_callout amrr_ch; - struct ural_task amrr_task[2]; - - int (*newstate)(struct ieee80211vap *, - enum ieee80211_state, int); -}; -#define URAL_VAP(vap) ((struct ural_vap *)(vap)) - -enum { - URAL_BULK_WR, - URAL_BULK_RD, - URAL_N_TRANSFER = 2, -}; - -struct ural_softc { - struct ifnet *sc_ifp; - device_t sc_dev; - struct usb2_device *sc_udev; - struct usb2_process sc_tq; - - const struct ieee80211_rate_table *sc_rates; - - uint32_t asic_rev; - uint8_t rf_rev; - - struct usb2_xfer *sc_xfer[URAL_N_TRANSFER]; - - enum ieee80211_state sc_state; - int sc_arg; - int sc_scan_action; /* should be an enum */ - struct ural_task sc_synctask[2]; - struct ural_task sc_task[2]; - struct ural_task sc_promisctask[2]; - struct ural_task sc_scantask[2]; - - struct ural_tx_data tx_data[RAL_TX_LIST_COUNT]; - ural_txdhead tx_q; - ural_txdhead tx_free; - int tx_nfree; - struct ural_rx_desc sc_rx_desc; - - struct mtx sc_mtx; - - uint16_t sta[11]; - uint32_t rf_regs[4]; - uint8_t txpow[14]; - uint8_t sc_bssid[6]; - - struct { - uint8_t val; - uint8_t reg; - } __packed bbp_prom[16]; - - int led_mode; - int hw_radio; - int rx_ant; - int tx_ant; - int nb_ant; - - struct ural_rx_radiotap_header sc_rxtap; - int sc_rxtap_len; - - struct ural_tx_radiotap_header sc_txtap; - int sc_txtap_len; -}; - -#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) -#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) -#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/sys/dev/usb2/wlan/if_zyd2.c b/sys/dev/usb2/wlan/if_zyd2.c deleted file mode 100644 index a5b1e2e..0000000 --- a/sys/dev/usb2/wlan/if_zyd2.c +++ /dev/null @@ -1,3122 +0,0 @@ -/* $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. - */ - -#include "usbdevs.h" -#include -#include -#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"); - -enum { - ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ - ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */ - ZYD_DEBUG_RESET = 0x00000004, /* reset processing */ - ZYD_DEBUG_INIT = 0x00000008, /* device init */ - ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ - ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ - ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ - ZYD_DEBUG_STAT = 0x00000080, /* statistic */ - ZYD_DEBUG_FW = 0x00000100, /* firmware */ - ZYD_DEBUG_CMD = 0x00000200, /* fw commands */ - ZYD_DEBUG_ANY = 0xffffffff -}; -#define DPRINTF(sc, m, fmt, ...) do { \ - if (zyd_debug & (m)) \ - printf("%s: " fmt, __func__, ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(sc, m, fmt, ...) do { \ - (void) sc; \ -} while (0) -#endif - -#define zyd_do_request(sc,req,data) \ - usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000) - -static device_probe_t zyd_match; -static device_attach_t zyd_attach; -static device_detach_t zyd_detach; - -static usb2_callback_t zyd_intr_read_callback; -static usb2_callback_t zyd_intr_write_callback; -static usb2_callback_t zyd_bulk_read_callback; -static usb2_callback_t zyd_bulk_write_callback; - -static usb2_proc_callback_t zyd_attach_post; -static usb2_proc_callback_t zyd_task; -static usb2_proc_callback_t zyd_scantask; -static usb2_proc_callback_t zyd_multitask; -static usb2_proc_callback_t zyd_init_task; -static usb2_proc_callback_t zyd_stop_task; - -static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, - const char name[IFNAMSIZ], int unit, int opmode, - int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void zyd_vap_delete(struct ieee80211vap *); -static void zyd_tx_free(struct zyd_tx_data *, int); -static void zyd_setup_tx_list(struct zyd_softc *); -static void zyd_unsetup_tx_list(struct zyd_softc *); -static struct ieee80211_node *zyd_node_alloc(struct ieee80211vap *, - const uint8_t mac[IEEE80211_ADDR_LEN]); -static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int); -static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int, - void *, int, int); -static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *); -static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *); -static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t); -static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t); -static int zyd_rfwrite(struct zyd_softc *, uint32_t); -static int zyd_lock_phy(struct zyd_softc *); -static int zyd_unlock_phy(struct zyd_softc *); -static int zyd_rf_attach(struct zyd_softc *, uint8_t); -static const char *zyd_rf_name(uint8_t); -static int zyd_hw_init(struct zyd_softc *); -static int zyd_read_pod(struct zyd_softc *); -static int zyd_read_eeprom(struct zyd_softc *); -static int zyd_get_macaddr(struct zyd_softc *); -static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *); -static int zyd_set_bssid(struct zyd_softc *, const uint8_t *); -static int zyd_switch_radio(struct zyd_softc *, int); -static int zyd_set_led(struct zyd_softc *, int, int); -static void zyd_set_multi(struct zyd_softc *); -static void zyd_update_mcast(struct ifnet *); -static int zyd_set_rxfilter(struct zyd_softc *); -static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *); -static int zyd_set_beacon_interval(struct zyd_softc *, int); -static void zyd_rx_data(struct usb2_xfer *, int, uint16_t); -static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *, - struct ieee80211_node *); -static int zyd_tx_data(struct zyd_softc *, struct mbuf *, - struct ieee80211_node *); -static void zyd_start(struct ifnet *); -static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *, - const struct ieee80211_bpf_params *); -static int zyd_ioctl(struct ifnet *, u_long, caddr_t); -static void zyd_init(void *); -static int zyd_loadfirmware(struct zyd_softc *); -static void zyd_newassoc(struct ieee80211_node *, int); -static void zyd_scan_start(struct ieee80211com *); -static void zyd_scan_end(struct ieee80211com *); -static void zyd_set_channel(struct ieee80211com *); -static int zyd_rfmd_init(struct zyd_rf *); -static int zyd_rfmd_switch_radio(struct zyd_rf *, int); -static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t); -static int zyd_al2230_init(struct zyd_rf *); -static int zyd_al2230_switch_radio(struct zyd_rf *, int); -static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t); -static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t); -static int zyd_al2230_init_b(struct zyd_rf *); -static int zyd_al7230B_init(struct zyd_rf *); -static int zyd_al7230B_switch_radio(struct zyd_rf *, int); -static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t); -static int zyd_al2210_init(struct zyd_rf *); -static int zyd_al2210_switch_radio(struct zyd_rf *, int); -static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t); -static int zyd_gct_init(struct zyd_rf *); -static int zyd_gct_switch_radio(struct zyd_rf *, int); -static int zyd_gct_set_channel(struct zyd_rf *, uint8_t); -static int zyd_maxim_init(struct zyd_rf *); -static int zyd_maxim_switch_radio(struct zyd_rf *, int); -static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t); -static int zyd_maxim2_init(struct zyd_rf *); -static int zyd_maxim2_switch_radio(struct zyd_rf *, int); -static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t); -static void zyd_queue_command(struct zyd_softc *, usb2_proc_callback_t *, - struct usb2_proc_msg *, struct usb2_proc_msg *); - -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_BULK_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_BULK_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_INTR_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_INTR_RD] = { - .type = UE_INTERRUPT, - .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, - }, -}; -#define zyd_read16_m(sc, val, data) do { \ - error = zyd_read16(sc, val, data); \ - if (error != 0) \ - goto fail; \ -} while (0) -#define zyd_write16_m(sc, val, data) do { \ - error = zyd_write16(sc, val, data); \ - if (error != 0) \ - goto fail; \ -} while (0) -#define zyd_read32_m(sc, val, data) do { \ - error = zyd_read32(sc, val, data); \ - if (error != 0) \ - goto fail; \ -} while (0) -#define zyd_write32_m(sc, val, data) do { \ - error = zyd_write32(sc, val, data); \ - if (error != 0) \ - goto fail; \ -} while (0) - -static int -zyd_match(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 != ZYD_CONFIG_INDEX) - return (ENXIO); - if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) - return (ENXIO); - - return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); -} - -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 (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); - sc->sc_dev = dev; - sc->sc_udev = uaa->device; - sc->sc_macrev = USB_GET_DRIVER_INFO(uaa); - - mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), - MTX_NETWORK_LOCK, MTX_DEF); - - STAILQ_INIT(&sc->sc_rqh); - - 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, " - "err=%s\n", usb2_errstr(error)); - goto detach; - } - error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx, - device_get_nameunit(dev), USB_PRI_MED); - if (error) { - device_printf(dev, "could not setup config thread!\n"); - goto detach; - } - - /* fork rest of the attach code */ - ZYD_LOCK(sc); - zyd_queue_command(sc, zyd_attach_post, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - ZYD_UNLOCK(sc); - return (0); - -detach: - zyd_detach(dev); - return (ENXIO); /* failure */ -} - -static void -zyd_attach_post(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - struct ifnet *ifp; - struct ieee80211com *ic; - int error; - uint8_t bands; - - if ((error = zyd_get_macaddr(sc)) != 0) { - device_printf(sc->sc_dev, "could not read EEPROM\n"); - return; - } - - ZYD_UNLOCK(sc); - - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(sc->sc_dev, "can not if_alloc()\n"); - ZYD_LOCK(sc); - return; - } - ifp->if_softc = sc; - if_initname(ifp, "zyd", device_get_unit(sc->sc_dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = zyd_init; - ifp->if_ioctl = zyd_ioctl; - ifp->if_start = zyd_start; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - IFQ_SET_READY(&ifp->if_snd); - - ic = ifp->if_l2com; - ic->ic_ifp = ifp; - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ - ic->ic_opmode = IEEE80211_M_STA; - IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); - - /* set device capabilities */ - ic->ic_caps = - IEEE80211_C_STA /* station mode */ - | IEEE80211_C_MONITOR /* monitor mode */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_BGSCAN /* capable of bg scanning */ - | IEEE80211_C_WPA /* 802.11i */ - ; - - bands = 0; - setbit(&bands, IEEE80211_MODE_11B); - setbit(&bands, IEEE80211_MODE_11G); - ieee80211_init_channels(ic, NULL, &bands); - - ieee80211_ifattach(ic); - ic->ic_newassoc = zyd_newassoc; - ic->ic_raw_xmit = zyd_raw_xmit; - ic->ic_node_alloc = zyd_node_alloc; - ic->ic_scan_start = zyd_scan_start; - ic->ic_scan_end = zyd_scan_end; - ic->ic_set_channel = zyd_set_channel; - - ic->ic_vap_create = zyd_vap_create; - ic->ic_vap_delete = zyd_vap_delete; - ic->ic_update_mcast = zyd_update_mcast; - ic->ic_update_promisc = zyd_update_mcast; - - bpfattach(ifp, DLT_IEEE802_11_RADIO, - sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); - sc->sc_rxtap_len = sizeof(sc->sc_rxtap); - sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); - sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); - sc->sc_txtap_len = sizeof(sc->sc_txtap); - sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); - sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); - - if (bootverbose) - ieee80211_announce(ic); - - ZYD_LOCK(sc); -} - -static int -zyd_detach(device_t dev) -{ - struct zyd_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic; - - /* wait for any post attach or other command to complete */ - usb2_proc_drain(&sc->sc_tq); - - /* stop all USB transfers */ - usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); - usb2_proc_free(&sc->sc_tq); - - /* free TX list, if any */ - zyd_unsetup_tx_list(sc); - - if (ifp) { - ic = ifp->if_l2com; - bpfdetach(ifp); - ieee80211_ifdetach(ic); - if_free(ifp); - } - - mtx_destroy(&sc->sc_mtx); - - return (0); -} - -static struct ieee80211vap * -zyd_vap_create(struct ieee80211com *ic, - const char name[IFNAMSIZ], int unit, int opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct zyd_vap *zvp; - struct ieee80211vap *vap; - - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return (NULL); - zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap), - M_80211_VAP, M_NOWAIT | M_ZERO); - if (zvp == NULL) - return (NULL); - vap = &zvp->vap; - /* enable s/w bmiss handling for sta mode */ - ieee80211_vap_setup(ic, vap, name, unit, opmode, - flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); - - /* override state transition machine */ - zvp->newstate = vap->iv_newstate; - vap->iv_newstate = zyd_newstate; - - ieee80211_amrr_init(&zvp->amrr, vap, - IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, - IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, - 1000 /* 1 sec */); - - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, - ieee80211_media_status); - ic->ic_opmode = opmode; - return (vap); -} - -static void -zyd_vap_delete(struct ieee80211vap *vap) -{ - struct zyd_vap *zvp = ZYD_VAP(vap); - - ieee80211_amrr_cleanup(&zvp->amrr); - ieee80211_vap_detach(vap); - free(zvp, M_80211_VAP); -} - -static void -zyd_tx_free(struct zyd_tx_data *data, int txerr) -{ - struct zyd_softc *sc = data->sc; - - if (data->m != NULL) { - if (data->m->m_flags & M_TXCB) - ieee80211_process_callback(data->ni, data->m, - txerr ? ETIMEDOUT : 0); - m_freem(data->m); - data->m = NULL; - - ieee80211_free_node(data->ni); - data->ni = NULL; - } - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; -} - -static void -zyd_setup_tx_list(struct zyd_softc *sc) -{ - struct zyd_tx_data *data; - int i; - - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - for (i = 0; i < ZYD_TX_LIST_CNT; i++) { - data = &sc->tx_data[i]; - - data->sc = sc; - STAILQ_INSERT_TAIL(&sc->tx_free, data, next); - sc->tx_nfree++; - } -} - -static void -zyd_unsetup_tx_list(struct zyd_softc *sc) -{ - struct zyd_tx_data *data; - int i; - - /* make sure any subsequent use of the queues will fail */ - sc->tx_nfree = 0; - STAILQ_INIT(&sc->tx_q); - STAILQ_INIT(&sc->tx_free); - - /* free up all node references and mbufs */ - for (i = 0; i < ZYD_TX_LIST_CNT; i++) { - data = &sc->tx_data[i]; - - if (data->m != NULL) { - m_freem(data->m); - data->m = NULL; - } - if (data->ni != NULL) { - ieee80211_free_node(data->ni); - data->ni = NULL; - } - } -} - -/* ARGUSED */ -static struct ieee80211_node * -zyd_node_alloc(struct ieee80211vap *vap __unused, - const uint8_t mac[IEEE80211_ADDR_LEN] __unused) -{ - struct zyd_node *zn; - - zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO); - return (zn != NULL) ? (&zn->ni) : (NULL); -} - -static void -zyd_task(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct ieee80211_node *ni = vap->iv_bss; - struct zyd_vap *zvp = ZYD_VAP(vap); - int error; - - switch (sc->sc_state) { - case IEEE80211_S_AUTH: - zyd_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_RUN: - if (vap->iv_opmode == IEEE80211_M_MONITOR) - break; - - /* turn link LED on */ - error = zyd_set_led(sc, ZYD_LED1, 1); - if (error != 0) - goto fail; - - /* make data LED blink upon Tx */ - zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1); - - IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); - zyd_set_bssid(sc, sc->sc_bssid); - break; - default: - break; - } -fail: - ZYD_UNLOCK(sc); - IEEE80211_LOCK(ic); - zvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); - ZYD_LOCK(sc); -} - -static int -zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct zyd_vap *zvp = ZYD_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct zyd_softc *sc = ic->ic_ifp->if_softc; - - DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); - - ZYD_LOCK(sc); - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - ZYD_UNLOCK(sc); - - if (nstate == IEEE80211_S_INIT) { - zvp->newstate(vap, nstate, arg); - return (0); - } else { - ZYD_LOCK(sc); - zyd_queue_command(sc, zyd_task, &sc->sc_task[0].hdr, - &sc->sc_task[1].hdr); - ZYD_UNLOCK(sc); - return (EINPROGRESS); - } -} - -/* - * Callback handler for interrupt transfer - */ -static void -zyd_intr_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 ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct ieee80211_node *ni; - struct zyd_cmd *cmd = &sc->sc_ibuf; - int datalen; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - usb2_copy_out(xfer->frbuffers, 0, cmd, sizeof(*cmd)); - - switch (le16toh(cmd->code)) { - case ZYD_NOTIF_RETRYSTATUS: - { - struct zyd_notif_retry *retry = - (struct zyd_notif_retry *)cmd->data; - - DPRINTF(sc, ZYD_DEBUG_TX_PROC, - "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", - le16toh(retry->rate), ether_sprintf(retry->macaddr), - le16toh(retry->count)&0xff, le16toh(retry->count)); - - /* - * Find the node to which the packet was sent and - * update its retry statistics. In BSS mode, this node - * is the AP we're associated to so no lookup is - * actually needed. - */ - ni = ieee80211_find_txnode(vap, retry->macaddr); - if (ni != NULL) { - ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, - IEEE80211_AMRR_FAILURE, 1); - ieee80211_free_node(ni); - } - if (le16toh(retry->count) & 0x100) - ifp->if_oerrors++; /* too many retries */ - break; - } - case ZYD_NOTIF_IORD: - { - struct zyd_rq *rqp; - - if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT) - break; /* HMAC interrupt */ - - datalen = xfer->actlen - sizeof(cmd->code); - datalen -= 2; /* XXX: padding? */ - - STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { - int i, cnt; - - if (rqp->olen != datalen) - continue; - cnt = rqp->olen / sizeof(struct zyd_pair); - for (i = 0; i < cnt; i++) { - if (*(((const uint16_t *)rqp->idata) + i) != - (((struct zyd_pair *)cmd->data) + i)->reg) - break; - } - if (i != cnt) - continue; - /* copy answer into caller-supplied buffer */ - bcopy(cmd->data, rqp->odata, rqp->olen); - DPRINTF(sc, ZYD_DEBUG_CMD, - "command %p complete, data = %*D \n", - rqp, rqp->olen, rqp->odata, ":"); - wakeup(rqp); /* wakeup caller */ - break; - } - if (rqp == NULL) { - device_printf(sc->sc_dev, - "unexpected IORD notification %*D\n", - datalen, cmd->data, ":"); - } - break; - } - default: - device_printf(sc->sc_dev, "unknown notification %x\n", - le16toh(cmd->code)); - } - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - break; - - default: /* Error */ - DPRINTF(sc, ZYD_DEBUG_CMD, "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; - } - break; - } -} - -static void -zyd_intr_write_callback(struct usb2_xfer *xfer) -{ - struct zyd_softc *sc = xfer->priv_sc; - struct zyd_rq *rqp; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - rqp = xfer->priv_fifo; - DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", rqp); - if ((rqp->flags & ZYD_CMD_FLAG_READ) == 0) - wakeup(rqp); /* wakeup caller */ - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { - if (rqp->flags & ZYD_CMD_FLAG_SENT) - continue; - - usb2_copy_in(xfer->frbuffers, 0, rqp->cmd, rqp->ilen); - - xfer->frlengths[0] = rqp->ilen; - xfer->priv_fifo = rqp; - rqp->flags |= ZYD_CMD_FLAG_SENT; - usb2_start_hardware(xfer); - break; - } - break; - - default: /* Error */ - DPRINTF(sc, ZYD_DEBUG_ANY, "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; - } - break; - } -} - -static int -zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen, - void *odata, int olen, int flags) -{ - struct zyd_cmd cmd; - struct zyd_rq rq; - int error; - - if (ilen > sizeof(cmd.data)) - return (EINVAL); - - if (usb2_proc_is_gone(&sc->sc_tq)) - return (ENXIO); - - cmd.code = htole16(code); - bcopy(idata, cmd.data, ilen); - DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n", - &rq, ilen, idata, ":"); - - rq.cmd = &cmd; - rq.idata = idata; - rq.odata = odata; - rq.ilen = sizeof(uint16_t) + ilen; - rq.olen = olen; - rq.flags = flags; - STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq); - usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); - usb2_transfer_start(sc->sc_xfer[ZYD_INTR_WR]); - - /* wait at most one second for command reply */ - error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz); - if (error) - device_printf(sc->sc_dev, "command timeout\n"); - STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq); - DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n", - &rq, error); - - return (error); -} - -static int -zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) -{ - struct zyd_pair tmp; - int error; - - reg = htole16(reg); - error = zyd_cmd(sc, ZYD_CMD_IORD, ®, sizeof(reg), &tmp, sizeof(tmp), - ZYD_CMD_FLAG_READ); - if (error == 0) - *val = le16toh(tmp.val); - return (error); -} - -static int -zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) -{ - struct zyd_pair tmp[2]; - uint16_t regs[2]; - int error; - - regs[0] = htole16(ZYD_REG32_HI(reg)); - regs[1] = htole16(ZYD_REG32_LO(reg)); - error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), - ZYD_CMD_FLAG_READ); - if (error == 0) - *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val); - return (error); -} - -static int -zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) -{ - struct zyd_pair pair; - - pair.reg = htole16(reg); - pair.val = htole16(val); - - return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0); -} - -static int -zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) -{ - struct zyd_pair pair[2]; - - pair[0].reg = htole16(ZYD_REG32_HI(reg)); - pair[0].val = htole16(val >> 16); - pair[1].reg = htole16(ZYD_REG32_LO(reg)); - pair[1].val = htole16(val & 0xffff); - - return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); -} - -static int -zyd_rfwrite(struct zyd_softc *sc, uint32_t val) -{ - struct zyd_rf *rf = &sc->sc_rf; - struct zyd_rfwrite_cmd req; - uint16_t cr203; - int error, i; - - zyd_read16_m(sc, ZYD_CR203, &cr203); - cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); - - req.code = htole16(2); - req.width = htole16(rf->width); - for (i = 0; i < rf->width; i++) { - req.bit[i] = htole16(cr203); - if (val & (1 << (rf->width - 1 - i))) - req.bit[i] |= htole16(ZYD_RF_DATA); - } - error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0); -fail: - return (error); -} - -static int -zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val) -{ - int error; - - zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff); - zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff); - zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff); -fail: - return (error); -} - -static int -zyd_lock_phy(struct zyd_softc *sc) -{ - int error; - uint32_t tmp; - - zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); - tmp &= ~ZYD_UNLOCK_PHY_REGS; - zyd_write32_m(sc, ZYD_MAC_MISC, tmp); -fail: - return (error); -} - -static int -zyd_unlock_phy(struct zyd_softc *sc) -{ - int error; - uint32_t tmp; - - zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); - tmp |= ZYD_UNLOCK_PHY_REGS; - zyd_write32_m(sc, ZYD_MAC_MISC, tmp); -fail: - return (error); -} - -/* - * RFMD RF methods. - */ -static int -zyd_rfmd_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; - static const uint32_t rfini[] = ZYD_RFMD_RF; - int i, error; - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) { - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - } - - /* init RFMD radio */ - for (i = 0; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } -fail: - return (error); -#undef N -} - -static int -zyd_rfmd_switch_radio(struct zyd_rf *rf, int on) -{ - int error; - struct zyd_softc *sc = rf->rf_sc; - - zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15); - zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81); -fail: - return (error); -} - -static int -zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan) -{ - int error; - struct zyd_softc *sc = rf->rf_sc; - static const struct { - uint32_t r1, r2; - } rfprog[] = ZYD_RFMD_CHANTABLE; - - error = zyd_rfwrite(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - -fail: - return (error); -} - -/* - * AL2230 RF methods. - */ -static int -zyd_al2230_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; - static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; - static const struct zyd_phy_pair phypll[] = { - { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, - { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } - }; - static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; - static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; - static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; - int i, error; - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { - for (i = 0; i < N(phy2230s); i++) - zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); - } - - /* init AL2230 radio */ - for (i = 0; i < N(rfini1); i++) { - error = zyd_rfwrite(sc, rfini1[i]); - if (error != 0) - goto fail; - } - - if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) - error = zyd_rfwrite(sc, 0x000824); - else - error = zyd_rfwrite(sc, 0x0005a4); - if (error != 0) - goto fail; - - for (i = 0; i < N(rfini2); i++) { - error = zyd_rfwrite(sc, rfini2[i]); - if (error != 0) - goto fail; - } - - for (i = 0; i < N(phypll); i++) - zyd_write16_m(sc, phypll[i].reg, phypll[i].val); - - for (i = 0; i < N(rfini3); i++) { - error = zyd_rfwrite(sc, rfini3[i]); - if (error != 0) - goto fail; - } -fail: - return (error); -#undef N -} - -static int -zyd_al2230_fini(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - int error, i; - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; - - for (i = 0; i < N(phy); i++) - zyd_write16_m(sc, phy[i].reg, phy[i].val); - - if (sc->sc_newphy != 0) - zyd_write16_m(sc, ZYD_CR9, 0xe1); - - zyd_write16_m(sc, ZYD_CR203, 0x6); -fail: - return (error); -#undef N -} - -static int -zyd_al2230_init_b(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; - static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; - static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; - static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; - static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; - static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; - static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; - static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; - static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; - int i, error; - - for (i = 0; i < N(phy1); i++) - zyd_write16_m(sc, phy1[i].reg, phy1[i].val); - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { - for (i = 0; i < N(phy2230s); i++) - zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); - } - - for (i = 0; i < 3; i++) { - error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); - if (error != 0) - return (error); - } - - for (i = 0; i < N(rfini_part1); i++) { - error = zyd_rfwrite_cr(sc, rfini_part1[i]); - if (error != 0) - return (error); - } - - if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) - error = zyd_rfwrite(sc, 0x241000); - else - error = zyd_rfwrite(sc, 0x25a000); - if (error != 0) - goto fail; - - for (i = 0; i < N(rfini_part2); i++) { - error = zyd_rfwrite_cr(sc, rfini_part2[i]); - if (error != 0) - return (error); - } - - for (i = 0; i < N(phy2); i++) - zyd_write16_m(sc, phy2[i].reg, phy2[i].val); - - for (i = 0; i < N(rfini_part3); i++) { - error = zyd_rfwrite_cr(sc, rfini_part3[i]); - if (error != 0) - return (error); - } - - for (i = 0; i < N(phy3); i++) - zyd_write16_m(sc, phy3[i].reg, phy3[i].val); - - error = zyd_al2230_fini(rf); -fail: - return (error); -#undef N -} - -static int -zyd_al2230_switch_radio(struct zyd_rf *rf, int on) -{ - struct zyd_softc *sc = rf->rf_sc; - int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f; - - zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); - zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f); -fail: - return (error); -} - -static int -zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - int error, i; - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phy1[] = { - { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }, - }; - static const struct { - uint32_t r1, r2, r3; - } rfprog[] = ZYD_AL2230_CHANTABLE; - - error = zyd_rfwrite(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r3); - if (error != 0) - goto fail; - - for (i = 0; i < N(phy1); i++) - zyd_write16_m(sc, phy1[i].reg, phy1[i].val); -fail: - return (error); -#undef N -} - -static int -zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - int error, i; - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; - static const struct { - uint32_t r1, r2, r3; - } rfprog[] = ZYD_AL2230_CHANTABLE_B; - - for (i = 0; i < N(phy1); i++) - zyd_write16_m(sc, phy1[i].reg, phy1[i].val); - - error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3); - if (error != 0) - goto fail; - error = zyd_al2230_fini(rf); -fail: - return (error); -#undef N -} - -#define ZYD_AL2230_PHY_BANDEDGE6 \ -{ \ - { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ - { ZYD_CR47, 0x1e } \ -} - -static int -zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - int error = 0, i; - struct zyd_softc *sc = rf->rf_sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; - int chan = ieee80211_chan2ieee(ic, c); - - if (chan == 1 || chan == 11) - r[0].val = 0x12; - - for (i = 0; i < N(r); i++) - zyd_write16_m(sc, r[i].reg, r[i].val); -fail: - return (error); -#undef N -} - -/* - * AL7230B RF methods. - */ -static int -zyd_al7230B_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; - static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; - static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; - static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; - static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; - int i, error; - - /* for AL7230B, PHY and RF need to be initialized in "phases" */ - - /* init RF-dependent PHY registers, part one */ - for (i = 0; i < N(phyini_1); i++) - zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val); - - /* init AL7230B radio, part one */ - for (i = 0; i < N(rfini_1); i++) { - if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0) - return (error); - } - /* init RF-dependent PHY registers, part two */ - for (i = 0; i < N(phyini_2); i++) - zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val); - - /* init AL7230B radio, part two */ - for (i = 0; i < N(rfini_2); i++) { - if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0) - return (error); - } - /* init RF-dependent PHY registers, part three */ - for (i = 0; i < N(phyini_3); i++) - zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val); -fail: - return (error); -#undef N -} - -static int -zyd_al7230B_switch_radio(struct zyd_rf *rf, int on) -{ - int error; - struct zyd_softc *sc = rf->rf_sc; - - zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); - zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f); -fail: - return (error); -} - -static int -zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct { - uint32_t r1, r2; - } rfprog[] = ZYD_AL7230B_CHANTABLE; - static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; - int i, error; - - zyd_write16_m(sc, ZYD_CR240, 0x57); - zyd_write16_m(sc, ZYD_CR251, 0x2f); - - for (i = 0; i < N(rfsc); i++) { - if ((error = zyd_rfwrite(sc, rfsc[i])) != 0) - return (error); - } - - zyd_write16_m(sc, ZYD_CR128, 0x14); - zyd_write16_m(sc, ZYD_CR129, 0x12); - zyd_write16_m(sc, ZYD_CR130, 0x10); - zyd_write16_m(sc, ZYD_CR38, 0x38); - zyd_write16_m(sc, ZYD_CR136, 0xdf); - - error = zyd_rfwrite(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, 0x3c9000); - if (error != 0) - goto fail; - - zyd_write16_m(sc, ZYD_CR251, 0x3f); - zyd_write16_m(sc, ZYD_CR203, 0x06); - zyd_write16_m(sc, ZYD_CR240, 0x08); -fail: - return (error); -#undef N -} - -/* - * AL2210 RF methods. - */ -static int -zyd_al2210_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; - static const uint32_t rfini[] = ZYD_AL2210_RF; - uint32_t tmp; - int i, error; - - zyd_write32_m(sc, ZYD_CR18, 2); - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - /* init AL2210 radio */ - for (i = 0; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } - zyd_write16_m(sc, ZYD_CR47, 0x1e); - zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); - zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); - zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); - zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); - zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); - zyd_write16_m(sc, ZYD_CR47, 0x1e); - zyd_write32_m(sc, ZYD_CR18, 3); -fail: - return (error); -#undef N -} - -static int -zyd_al2210_switch_radio(struct zyd_rf *rf, int on) -{ - /* vendor driver does nothing for this RF chip */ - - return (0); -} - -static int -zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan) -{ - int error; - struct zyd_softc *sc = rf->rf_sc; - static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; - uint32_t tmp; - - zyd_write32_m(sc, ZYD_CR18, 2); - zyd_write16_m(sc, ZYD_CR47, 0x1e); - zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); - zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); - zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); - zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); - zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); - zyd_write16_m(sc, ZYD_CR47, 0x1e); - - /* actually set the channel */ - error = zyd_rfwrite(sc, rfprog[chan - 1]); - if (error != 0) - goto fail; - - zyd_write32_m(sc, ZYD_CR18, 3); -fail: - return (error); -} - -/* - * GCT RF methods. - */ -static int -zyd_gct_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; - static const uint32_t rfini[] = ZYD_GCT_RF; - int i, error; - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - /* init cgt radio */ - for (i = 0; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } -fail: - return (error); -#undef N -} - -static int -zyd_gct_switch_radio(struct zyd_rf *rf, int on) -{ - /* vendor driver does nothing for this RF chip */ - - return (0); -} - -static int -zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan) -{ - int error; - struct zyd_softc *sc = rf->rf_sc; - static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; - - error = zyd_rfwrite(sc, 0x1c0000); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1]); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, 0x1c0008); -fail: - return (error); -} - -/* - * Maxim RF methods. - */ -static int -zyd_maxim_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; - static const uint32_t rfini[] = ZYD_MAXIM_RF; - uint16_t tmp; - int i, error; - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); - - /* init maxim radio */ - for (i = 0; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); -fail: - return (error); -#undef N -} - -static int -zyd_maxim_switch_radio(struct zyd_rf *rf, int on) -{ - - /* vendor driver does nothing for this RF chip */ - return (0); -} - -static int -zyd_maxim_set_channel(struct zyd_rf *rf, uint8_t chan) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; - static const uint32_t rfini[] = ZYD_MAXIM_RF; - static const struct { - uint32_t r1, r2; - } rfprog[] = ZYD_MAXIM_CHANTABLE; - uint16_t tmp; - int i, error; - - /* - * Do the same as we do when initializing it, except for the channel - * values coming from the two channel tables. - */ - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); - - /* first two values taken from the chantables */ - error = zyd_rfwrite(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - - /* init maxim radio - skipping the two first values */ - for (i = 2; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); -fail: - return (error); -#undef N -} - -/* - * Maxim2 RF methods. - */ -static int -zyd_maxim2_init(struct zyd_rf *rf) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; - static const uint32_t rfini[] = ZYD_MAXIM2_RF; - uint16_t tmp; - int i, error; - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); - - /* init maxim2 radio */ - for (i = 0; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); -fail: - return (error); -#undef N -} - -static int -zyd_maxim2_switch_radio(struct zyd_rf *rf, int on) -{ - - /* vendor driver does nothing for this RF chip */ - return (0); -} - -static int -zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan) -{ -#define N(a) (sizeof(a) / sizeof((a)[0])) - struct zyd_softc *sc = rf->rf_sc; - static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; - static const uint32_t rfini[] = ZYD_MAXIM2_RF; - static const struct { - uint32_t r1, r2; - } rfprog[] = ZYD_MAXIM2_CHANTABLE; - uint16_t tmp; - int i, error; - - /* - * Do the same as we do when initializing it, except for the channel - * values coming from the two channel tables. - */ - - /* init RF-dependent PHY registers */ - for (i = 0; i < N(phyini); i++) - zyd_write16_m(sc, phyini[i].reg, phyini[i].val); - - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); - - /* first two values taken from the chantables */ - error = zyd_rfwrite(sc, rfprog[chan - 1].r1); - if (error != 0) - goto fail; - error = zyd_rfwrite(sc, rfprog[chan - 1].r2); - if (error != 0) - goto fail; - - /* init maxim2 radio - skipping the two first values */ - for (i = 2; i < N(rfini); i++) { - if ((error = zyd_rfwrite(sc, rfini[i])) != 0) - return (error); - } - zyd_read16_m(sc, ZYD_CR203, &tmp); - zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); -fail: - return (error); -#undef N -} - -static int -zyd_rf_attach(struct zyd_softc *sc, uint8_t type) -{ - struct zyd_rf *rf = &sc->sc_rf; - - rf->rf_sc = sc; - - switch (type) { - case ZYD_RF_RFMD: - rf->init = zyd_rfmd_init; - rf->switch_radio = zyd_rfmd_switch_radio; - rf->set_channel = zyd_rfmd_set_channel; - rf->width = 24; /* 24-bit RF values */ - break; - case ZYD_RF_AL2230: - case ZYD_RF_AL2230S: - if (sc->sc_macrev == ZYD_ZD1211B) { - rf->init = zyd_al2230_init_b; - rf->set_channel = zyd_al2230_set_channel_b; - } else { - rf->init = zyd_al2230_init; - rf->set_channel = zyd_al2230_set_channel; - } - rf->switch_radio = zyd_al2230_switch_radio; - rf->bandedge6 = zyd_al2230_bandedge6; - rf->width = 24; /* 24-bit RF values */ - break; - case ZYD_RF_AL7230B: - rf->init = zyd_al7230B_init; - rf->switch_radio = zyd_al7230B_switch_radio; - rf->set_channel = zyd_al7230B_set_channel; - rf->width = 24; /* 24-bit RF values */ - break; - case ZYD_RF_AL2210: - rf->init = zyd_al2210_init; - rf->switch_radio = zyd_al2210_switch_radio; - rf->set_channel = zyd_al2210_set_channel; - rf->width = 24; /* 24-bit RF values */ - break; - case ZYD_RF_GCT: - rf->init = zyd_gct_init; - rf->switch_radio = zyd_gct_switch_radio; - rf->set_channel = zyd_gct_set_channel; - rf->width = 21; /* 21-bit RF values */ - break; - case ZYD_RF_MAXIM_NEW: - rf->init = zyd_maxim_init; - rf->switch_radio = zyd_maxim_switch_radio; - rf->set_channel = zyd_maxim_set_channel; - rf->width = 18; /* 18-bit RF values */ - break; - case ZYD_RF_MAXIM_NEW2: - rf->init = zyd_maxim2_init; - rf->switch_radio = zyd_maxim2_switch_radio; - rf->set_channel = zyd_maxim2_set_channel; - rf->width = 18; /* 18-bit RF values */ - break; - default: - device_printf(sc->sc_dev, - "sorry, radio \"%s\" is not supported yet\n", - zyd_rf_name(type)); - return (EINVAL); - } - return (0); -} - -static const char * -zyd_rf_name(uint8_t type) -{ - static const char * const zyd_rfs[] = { - "unknown", "unknown", "UW2451", "UCHIP", "AL2230", - "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", - "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", - "PHILIPS" - }; - - return zyd_rfs[(type > 15) ? 0 : type]; -} - -static int -zyd_hw_init(struct zyd_softc *sc) -{ - int error; - const struct zyd_phy_pair *phyp; - struct zyd_rf *rf = &sc->sc_rf; - uint16_t val; - - /* specify that the plug and play is finished */ - zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); - zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase); - DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n", - sc->sc_fwbase); - - /* retrieve firmware revision number */ - zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev); - zyd_write32_m(sc, ZYD_CR_GPI_EN, 0); - zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); - /* set mandatory rates - XXX assumes 802.11b/g */ - zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f); - - /* disable interrupts */ - zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); - - if ((error = zyd_read_pod(sc)) != 0) { - device_printf(sc->sc_dev, "could not read EEPROM\n"); - goto fail; - } - - /* PHY init (resetting) */ - error = zyd_lock_phy(sc); - if (error != 0) - goto fail; - phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; - for (; phyp->reg != 0; phyp++) - zyd_write16_m(sc, phyp->reg, phyp->val); - if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) { - zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val); - zyd_write32_m(sc, ZYD_CR157, val >> 8); - } - error = zyd_unlock_phy(sc); - if (error != 0) - goto fail; - - /* HMAC init */ - zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020); - zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); - zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000); - zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000); - zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000); - zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000); - zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4); - zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); - zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401); - zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); - zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080); - zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); - zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); - zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); - zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000); - zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); - zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); - zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); - zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); - zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3); - - if (sc->sc_macrev == ZYD_ZD1211) { - zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002); - zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); - } else { - zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202); - zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); - zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); - zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); - zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); - zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); - zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); - zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824); - zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); - } - - /* init beacon interval to 100ms */ - if ((error = zyd_set_beacon_interval(sc, 100)) != 0) - goto fail; - - if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) { - device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n", - sc->sc_rfrev); - goto fail; - } - - /* RF chip init */ - error = zyd_lock_phy(sc); - if (error != 0) - goto fail; - error = (*rf->init)(rf); - if (error != 0) { - device_printf(sc->sc_dev, - "radio initialization failed, error %d\n", error); - goto fail; - } - error = zyd_unlock_phy(sc); - if (error != 0) - goto fail; - - if ((error = zyd_read_eeprom(sc)) != 0) { - device_printf(sc->sc_dev, "could not read EEPROM\n"); - goto fail; - } - -fail: return (error); -} - -static int -zyd_read_pod(struct zyd_softc *sc) -{ - int error; - uint32_t tmp; - - zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp); - sc->sc_rfrev = tmp & 0x0f; - sc->sc_ledtype = (tmp >> 4) & 0x01; - sc->sc_al2230s = (tmp >> 7) & 0x01; - sc->sc_cckgain = (tmp >> 8) & 0x01; - sc->sc_fix_cr157 = (tmp >> 13) & 0x01; - sc->sc_parev = (tmp >> 16) & 0x0f; - sc->sc_bandedge6 = (tmp >> 21) & 0x01; - sc->sc_newphy = (tmp >> 31) & 0x01; - sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; -fail: - return (error); -} - -static int -zyd_read_eeprom(struct zyd_softc *sc) -{ - uint16_t val; - int error, i; - - /* read Tx power calibration tables */ - for (i = 0; i < 7; i++) { - zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val); - sc->sc_pwrcal[i * 2] = val >> 8; - sc->sc_pwrcal[i * 2 + 1] = val & 0xff; - zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val); - sc->sc_pwrint[i * 2] = val >> 8; - sc->sc_pwrint[i * 2 + 1] = val & 0xff; - zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val); - sc->sc_ofdm36_cal[i * 2] = val >> 8; - sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff; - zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val); - sc->sc_ofdm48_cal[i * 2] = val >> 8; - sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff; - zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val); - sc->sc_ofdm54_cal[i * 2] = val >> 8; - sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff; - } -fail: - return (error); -} - -static int -zyd_get_macaddr(struct zyd_softc *sc) -{ - struct usb2_device_request req; - usb2_error_t error; - - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = ZYD_READFWDATAREQ; - USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); - USETW(req.wIndex, 0); - USETW(req.wLength, IEEE80211_ADDR_LEN); - - error = zyd_do_request(sc, &req, sc->sc_bssid); - if (error != 0) { - device_printf(sc->sc_dev, "could not read EEPROM: %s\n", - usb2_errstr(error)); - } - - return (error); -} - -static int -zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) -{ - int error; - uint32_t tmp; - - tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; - zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp); - tmp = addr[5] << 8 | addr[4]; - zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp); -fail: - return (error); -} - -static int -zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr) -{ - int error; - uint32_t tmp; - - tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; - zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp); - tmp = addr[5] << 8 | addr[4]; - zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp); -fail: - return (error); -} - -static int -zyd_switch_radio(struct zyd_softc *sc, int on) -{ - struct zyd_rf *rf = &sc->sc_rf; - int error; - - error = zyd_lock_phy(sc); - if (error != 0) - goto fail; - error = (*rf->switch_radio)(rf, on); - if (error != 0) - goto fail; - error = zyd_unlock_phy(sc); -fail: - return (error); -} - -static int -zyd_set_led(struct zyd_softc *sc, int which, int on) -{ - int error; - uint32_t tmp; - - zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); - tmp &= ~which; - if (on) - tmp |= which; - zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp); -fail: - return (error); -} - -static void -zyd_multitask(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - - zyd_set_multi(sc); -} - -static void -zyd_set_multi(struct zyd_softc *sc) -{ - int error; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ifmultiaddr *ifma; - uint32_t low, high; - uint8_t v; - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - low = 0x00000000; - high = 0x80000000; - - if (ic->ic_opmode == IEEE80211_M_MONITOR || - (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { - low = 0xffffffff; - high = 0xffffffff; - } else { - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - v = ((uint8_t *)LLADDR((struct sockaddr_dl *) - ifma->ifma_addr))[5] >> 2; - if (v < 32) - low |= 1 << v; - else - high |= 1 << (v - 32); - } - IF_ADDR_UNLOCK(ifp); - } - - /* reprogram multicast global hash table */ - zyd_write32_m(sc, ZYD_MAC_GHTBL, low); - zyd_write32_m(sc, ZYD_MAC_GHTBH, high); -fail: - if (error != 0) - device_printf(sc->sc_dev, - "could not set multicast hash table\n"); -} - -static void -zyd_update_mcast(struct ifnet *ifp) -{ - struct zyd_softc *sc = ifp->if_softc; - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; - - ZYD_LOCK(sc); - zyd_queue_command(sc, zyd_multitask, - &sc->sc_mcasttask[0].hdr, &sc->sc_mcasttask[1].hdr); - ZYD_UNLOCK(sc); -} - -static int -zyd_set_rxfilter(struct zyd_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t rxfilter; - - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - rxfilter = ZYD_FILTER_BSS; - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_HOSTAP: - rxfilter = ZYD_FILTER_HOSTAP; - break; - case IEEE80211_M_MONITOR: - rxfilter = ZYD_FILTER_MONITOR; - break; - default: - /* should not get there */ - return (EINVAL); - } - return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter); -} - -static void -zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c) -{ - int error; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct zyd_rf *rf = &sc->sc_rf; - uint32_t tmp; - int chan; - - chan = ieee80211_chan2ieee(ic, c); - if (chan == 0 || chan == IEEE80211_CHAN_ANY) { - /* XXX should NEVER happen */ - device_printf(sc->sc_dev, - "%s: invalid channel %x\n", __func__, chan); - return; - } - - error = zyd_lock_phy(sc); - if (error != 0) - goto fail; - - error = (*rf->set_channel)(rf, chan); - if (error != 0) - goto fail; - - /* update Tx power */ - zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]); - - if (sc->sc_macrev == ZYD_ZD1211B) { - zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); - zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); - zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); - zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]); - zyd_write16_m(sc, ZYD_CR69, 0x28); - zyd_write16_m(sc, ZYD_CR69, 0x2a); - } - if (sc->sc_cckgain) { - /* set CCK baseband gain from EEPROM */ - if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) - zyd_write16_m(sc, ZYD_CR47, tmp & 0xff); - } - if (sc->sc_bandedge6 && rf->bandedge6 != NULL) { - error = (*rf->bandedge6)(rf, c); - if (error != 0) - goto fail; - } - zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0); - - error = zyd_unlock_phy(sc); - if (error != 0) - goto fail; - - sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = - htole16(c->ic_freq); - sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = - htole16(c->ic_flags); -fail: - return; -} - -static int -zyd_set_beacon_interval(struct zyd_softc *sc, int bintval) -{ - int error; - uint32_t val; - - zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val); - sc->sc_atim_wnd = val; - zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val); - sc->sc_pre_tbtt = val; - sc->sc_bcn_int = bintval; - - if (sc->sc_bcn_int <= 5) - sc->sc_bcn_int = 5; - if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) - sc->sc_pre_tbtt = sc->sc_bcn_int - 1; - if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) - sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; - - zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); - zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); - zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); -fail: - return (error); -} - -static void -zyd_rx_data(struct usb2_xfer *xfer, int offset, uint16_t 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; - int rlen, rssi; - - if (len < ZYD_MIN_FRAGSZ) { - DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", - device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; - return; - } - 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(sc, ZYD_DEBUG_RECV, - "%s: RX status indicated error (%x)\n", - device_get_nameunit(sc->sc_dev), stat.flags); - ifp->if_ierrors++; - return; - } - - /* compute actual frame length */ - rlen = len - sizeof(struct zyd_plcphdr) - - sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN; - - /* allocate a mbuf to store the frame */ - if (rlen > MCLBYTES) { - DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n", - device_get_nameunit(sc->sc_dev), rlen); - ifp->if_ierrors++; - return; - } else if (rlen > MHLEN) - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - else - m = m_gethdr(M_DONTWAIT, MT_DATA); - if (m == NULL) { - DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", - device_get_nameunit(sc->sc_dev)); - ifp->if_ierrors++; - return; - } - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = rlen; - usb2_copy_out(xfer->frbuffers, offset + sizeof(plcp), - mtod(m, uint8_t *), rlen); - - if (bpf_peers_present(ifp->if_bpf)) { - struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; - - tap->wr_flags = 0; - if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) - tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; - /* XXX toss, no way to express errors */ - if (stat.flags & ZYD_RX_DECRYPTERR) - tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; - tap->wr_rate = ieee80211_plcp2rate(plcp.signal, - (stat.flags & ZYD_RX_OFDM) ? - IEEE80211_T_OFDM : IEEE80211_T_CCK); - tap->wr_antsignal = stat.rssi + -95; - tap->wr_antnoise = -95; /* XXX */ - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); - } - rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi; - - sc->sc_rx_data[sc->sc_rx_count].rssi = rssi; - sc->sc_rx_data[sc->sc_rx_count].m = m; - sc->sc_rx_count++; -} - -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 desc; - struct mbuf *m; - uint32_t offset; - uint8_t rssi; - int8_t nf; - int i; - - sc->sc_rx_count = 0; - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(desc), - &desc, sizeof(desc)); - - offset = 0; - if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) { - DPRINTF(sc, ZYD_DEBUG_RECV, - "%s: received multi-frame transfer\n", __func__); - - for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) { - uint16_t len16 = UGETW(desc.len[i]); - - if (len16 == 0 || len16 > xfer->actlen) - break; - - zyd_rx_data(xfer, 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 { - DPRINTF(sc, ZYD_DEBUG_RECV, - "%s: received single-frame transfer\n", __func__); - - zyd_rx_data(xfer, 0, xfer->actlen); - } - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - 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! - */ - ZYD_UNLOCK(sc); - for (i = 0; i < sc->sc_rx_count; i++) { - rssi = sc->sc_rx_data[i].rssi; - m = sc->sc_rx_data[i].m; - sc->sc_rx_data[i].m = NULL; - - nf = -95; /* XXX */ - - ni = ieee80211_find_rxnode(ic, - mtod(m, struct ieee80211_frame_min *)); - if (ni != NULL) { - (void)ieee80211_input(ni, m, rssi, nf, 0); - ieee80211_free_node(ni); - } else - (void)ieee80211_input_all(ic, m, rssi, nf, 0); - } - ZYD_LOCK(sc); - break; - - default: /* Error */ - DPRINTF(sc, ZYD_DEBUG_ANY, "frame 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; - } - break; - } -} - -static uint8_t -zyd_plcp_signal(int rate) -{ - switch (rate) { - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ - case 12: - return (0xb); - case 18: - return (0xf); - case 24: - return (0xa); - case 36: - return (0xe); - case 48: - return (0x9); - case 72: - return (0xd); - case 96: - return (0x8); - case 108: - return (0xc); - /* CCK rates (NB: not IEEE std, device-specific) */ - case 2: - return (0x0); - case 4: - return (0x1); - case 11: - return (0x2); - case 22: - return (0x3); - } - return (0xff); /* XXX unsupported/unknown rate */ -} - -static int -zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = sc->sc_ifp; - struct zyd_tx_desc *desc; - struct zyd_tx_data *data; - struct ieee80211_frame *wh; - struct ieee80211_key *k; - int rate, totlen; - uint16_t pktlen; - - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - desc = &data->desc; - - rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2; - - wh = mtod(m0, struct ieee80211_frame *); - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return (ENOBUFS); - } - } - - data->ni = ni; - data->m = m0; - data->rate = rate; - - wh = mtod(m0, struct ieee80211_frame *); - - totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; - - /* fill Tx descriptor */ - desc->len = htole16(totlen); - - desc->flags = ZYD_TX_FLAG_BACKOFF; - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (totlen > vap->iv_rtsthreshold) { - desc->flags |= ZYD_TX_FLAG_RTS; - } else if (ZYD_RATE_IS_OFDM(rate) && - (ic->ic_flags & IEEE80211_F_USEPROT)) { - if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) - desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; - else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) - desc->flags |= ZYD_TX_FLAG_RTS; - } - } else - desc->flags |= ZYD_TX_FLAG_MULTICAST; - - if ((wh->i_fc[0] & - (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == - (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) - desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); - - desc->phy = zyd_plcp_signal(rate); - if (ZYD_RATE_IS_OFDM(rate)) { - desc->phy |= ZYD_TX_PHY_OFDM; - if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) - desc->phy |= ZYD_TX_PHY_5GHZ; - } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) - desc->phy |= ZYD_TX_PHY_SHPREAMBLE; - - /* actual transmit length (XXX why +10?) */ - pktlen = ZYD_TX_DESC_SIZE + 10; - if (sc->sc_macrev == ZYD_ZD1211) - pktlen += totlen; - desc->pktlen = htole16(pktlen); - - desc->plcp_length = (16 * totlen + rate - 1) / rate; - desc->plcp_service = 0; - if (rate == 22) { - const int remainder = (16 * totlen) % 22; - if (remainder != 0 && remainder < 7) - desc->plcp_service |= ZYD_PLCP_LENGEXT; - } - - if (bpf_peers_present(ifp->if_bpf)) { - struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = rate; - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); - } - - DPRINTF(sc, ZYD_DEBUG_XMIT, - "%s: sending mgt frame len=%zu rate=%u\n", - device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, - rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); - - return (0); -} - -static void -zyd_bulk_write_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_channel *c = ic->ic_curchan; - struct zyd_tx_data *data; - struct mbuf *m; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n", - xfer->actlen); - - /* free resources */ - data = xfer->priv_fifo; - zyd_tx_free(data, 0); - xfer->priv_fifo = NULL; - - ifp->if_opackets++; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - /* FALLTHROUGH */ - case USB_ST_SETUP: -tr_setup: - data = STAILQ_FIRST(&sc->tx_q); - if (data) { - STAILQ_REMOVE_HEAD(&sc->tx_q, next); - m = data->m; - - if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) { - DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n", - m->m_pkthdr.len); - m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; - } - usb2_copy_in(xfer->frbuffers, 0, &data->desc, - ZYD_TX_DESC_SIZE); - usb2_m_copy_in(xfer->frbuffers, ZYD_TX_DESC_SIZE, m, 0, - m->m_pkthdr.len); - - if (bpf_peers_present(ifp->if_bpf)) { - struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = data->rate; - tap->wt_chan_freq = htole16(c->ic_freq); - tap->wt_chan_flags = htole16(c->ic_flags); - - bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); - } - - xfer->frlengths[0] = ZYD_TX_DESC_SIZE + m->m_pkthdr.len; - xfer->priv_fifo = data; - usb2_start_hardware(xfer); - } - break; - - default: /* Error */ - DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n", - usb2_errstr(xfer->error)); - - ifp->if_oerrors++; - data = xfer->priv_fifo; - xfer->priv_fifo = NULL; - if (data != NULL) - zyd_tx_free(data, xfer->error); - - if (xfer->error == USB_ERR_STALLED) { - /* try to clear stall first */ - xfer->flags.stall_pipe = 1; - goto tr_setup; - } - if (xfer->error == USB_ERR_TIMEOUT) - device_printf(sc->sc_dev, "device timeout\n"); - break; - } -} - -static int -zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct zyd_tx_desc *desc; - struct zyd_tx_data *data; - struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - int rate, totlen; - uint16_t pktlen; - - wh = mtod(m0, struct ieee80211_frame *); - data = STAILQ_FIRST(&sc->tx_free); - STAILQ_REMOVE_HEAD(&sc->tx_free, next); - sc->tx_nfree--; - desc = &data->desc; - - desc->flags = ZYD_TX_FLAG_BACKOFF; - tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; - if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { - rate = tp->mcastrate; - desc->flags |= ZYD_TX_FLAG_MULTICAST; - } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { - rate = tp->ucastrate; - } else { - (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn); - rate = ni->ni_txrate; - } - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return (ENOBUFS); - } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - - data->ni = ni; - data->m = m0; - - totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; - - /* fill Tx descriptor */ - desc->len = htole16(totlen); - - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (totlen > vap->iv_rtsthreshold) { - desc->flags |= ZYD_TX_FLAG_RTS; - } else if (ZYD_RATE_IS_OFDM(rate) && - (ic->ic_flags & IEEE80211_F_USEPROT)) { - if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) - desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; - else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) - desc->flags |= ZYD_TX_FLAG_RTS; - } - } - - if ((wh->i_fc[0] & - (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == - (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) - desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); - - desc->phy = zyd_plcp_signal(rate); - if (ZYD_RATE_IS_OFDM(rate)) { - desc->phy |= ZYD_TX_PHY_OFDM; - if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) - desc->phy |= ZYD_TX_PHY_5GHZ; - } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) - desc->phy |= ZYD_TX_PHY_SHPREAMBLE; - - /* actual transmit length (XXX why +10?) */ - pktlen = sizeof(struct zyd_tx_desc) + 10; - if (sc->sc_macrev == ZYD_ZD1211) - pktlen += totlen; - desc->pktlen = htole16(pktlen); - - desc->plcp_length = (16 * totlen + rate - 1) / rate; - desc->plcp_service = 0; - if (rate == 22) { - const int remainder = (16 * totlen) % 22; - if (remainder != 0 && remainder < 7) - desc->plcp_service |= ZYD_PLCP_LENGEXT; - } - - DPRINTF(sc, ZYD_DEBUG_XMIT, - "%s: sending data frame len=%zu rate=%u\n", - device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, - rate); - - STAILQ_INSERT_TAIL(&sc->tx_q, data, next); - usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); - - return (0); -} - -static void -zyd_start(struct ifnet *ifp) -{ - struct zyd_softc *sc = ifp->if_softc; - struct ieee80211_node *ni; - struct mbuf *m; - - ZYD_LOCK(sc); - for (;;) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - if (sc->tx_nfree == 0) { - IFQ_DRV_PREPEND(&ifp->if_snd, m); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; - m = ieee80211_encap(ni, m); - if (m == NULL) { - ieee80211_free_node(ni); - ifp->if_oerrors++; - continue; - } - if (zyd_tx_data(sc, m, ni) != 0) { - ieee80211_free_node(ni); - ifp->if_oerrors++; - break; - } - } - ZYD_UNLOCK(sc); -} - -static int -zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct zyd_softc *sc = ifp->if_softc; - - ZYD_LOCK(sc); - /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - ZYD_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return (ENETDOWN); - } - if (sc->tx_nfree == 0) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - ZYD_UNLOCK(sc); - m_freem(m); - ieee80211_free_node(ni); - return (ENOBUFS); /* XXX */ - } - - /* - * Legacy path; interpret frame contents to decide - * precisely how to send the frame. - * XXX raw path - */ - if (zyd_tx_mgt(sc, m, ni) != 0) { - ZYD_UNLOCK(sc); - ifp->if_oerrors++; - ieee80211_free_node(ni); - return (EIO); - } - ZYD_UNLOCK(sc); - return (0); -} - -static int -zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct zyd_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - ZYD_LOCK(sc); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - if ((ifp->if_flags ^ sc->sc_if_flags) & - (IFF_ALLMULTI | IFF_PROMISC)) - zyd_set_multi(sc); - } else { - zyd_queue_command(sc, zyd_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - startall = 1; - } - } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - zyd_queue_command(sc, zyd_stop_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - } - } - sc->sc_if_flags = ifp->if_flags; - ZYD_UNLOCK(sc); - if (startall) - ieee80211_start_all(ic); - break; - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; - default: - error = EINVAL; - break; - } - return (error); -} - -static void -zyd_init_task(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct usb2_config_descriptor *cd; - int error; - uint32_t val; - - ZYD_LOCK_ASSERT(sc, MA_OWNED); - - if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) { - error = zyd_loadfirmware(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "could not load firmware (error=%d)\n", error); - goto fail; - } - - /* reset device */ - cd = usb2_get_config_descriptor(sc->sc_udev); - error = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, - cd->bConfigurationValue); - if (error) - device_printf(sc->sc_dev, "reset failed, continuing\n"); - - error = zyd_hw_init(sc); - if (error) { - device_printf(sc->sc_dev, - "hardware initialization failed\n"); - goto fail; - } - - device_printf(sc->sc_dev, - "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x " - "BE%x NP%x Gain%x F%x\n", - (sc->sc_macrev == ZYD_ZD1211) ? "": "B", - sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff, - zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev, - sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy, - sc->sc_cckgain, sc->sc_fix_cr157); - - /* read regulatory domain (currently unused) */ - zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val); - sc->sc_regdomain = val >> 16; - DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n", - sc->sc_regdomain); - - /* we'll do software WEP decryption for now */ - DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n", - __func__); - zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); - - sc->sc_flags |= ZYD_FLAG_INITONCE; - } - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - zyd_stop_task(pm); - - IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); - DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %s\n", - ether_sprintf(ic->ic_myaddr)); - error = zyd_set_macaddr(sc, ic->ic_myaddr); - if (error != 0) - return; - - /* set basic rates */ - if (ic->ic_curmode == IEEE80211_MODE_11B) - zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003); - else if (ic->ic_curmode == IEEE80211_MODE_11A) - zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500); - else /* assumes 802.11b/g */ - zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f); - - /* promiscuous mode */ - zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0); - /* multicast setup */ - zyd_set_multi(sc); - /* set RX filter */ - error = zyd_set_rxfilter(sc); - if (error != 0) - goto fail; - - /* switch radio transmitter ON */ - error = zyd_switch_radio(sc, 1); - if (error != 0) - goto fail; - /* set default BSS channel */ - zyd_set_chan(sc, ic->ic_curchan); - - /* - * Allocate Tx and Rx xfer queues. - */ - zyd_setup_tx_list(sc); - - /* enable interrupts */ - zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); - - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - usb2_transfer_start(sc->sc_xfer[ZYD_BULK_RD]); - usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); - - return; - -fail: zyd_stop_task(pm); - return; -} - -static void -zyd_init(void *priv) -{ - struct zyd_softc *sc = priv; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - ZYD_LOCK(sc); - zyd_queue_command(sc, zyd_init_task, - &sc->sc_synctask[0].hdr, - &sc->sc_synctask[1].hdr); - ZYD_UNLOCK(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vap's */ -} - -static void -zyd_stop_task(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - int error; - - ZYD_LOCK_ASSERT(sc, MA_OWNED); - - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - /* - * Drain all the transfers, if not already drained: - */ - ZYD_UNLOCK(sc); - usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]); - usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]); - ZYD_LOCK(sc); - - zyd_unsetup_tx_list(sc); - - /* Stop now if the device was never set up */ - if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) - return; - - /* switch radio transmitter OFF */ - error = zyd_switch_radio(sc, 0); - if (error != 0) - goto fail; - /* disable Rx */ - zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0); - /* disable interrupts */ - zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); - -fail: - return; -} - -static int -zyd_loadfirmware(struct zyd_softc *sc) -{ - struct usb2_device_request req; - size_t size; - u_char *fw; - uint8_t stat; - uint16_t addr; - - if (sc->sc_flags & ZYD_FLAG_FWLOADED) - return (0); - - if (sc->sc_macrev == ZYD_ZD1211) { - fw = (u_char *)zd1211_firmware; - size = sizeof(zd1211_firmware); - } else { - fw = (u_char *)zd1211b_firmware; - size = sizeof(zd1211b_firmware); - } - - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = ZYD_DOWNLOADREQ; - USETW(req.wIndex, 0); - - addr = ZYD_FIRMWARE_START_ADDR; - while (size > 0) { - /* - * When the transfer size is 4096 bytes, it is not - * likely to be able to transfer it. - * The cause is port or machine or chip? - */ - const int mlen = min(size, 64); - - DPRINTF(sc, ZYD_DEBUG_FW, - "loading firmware block: len=%d, addr=0x%x\n", mlen, addr); - - USETW(req.wValue, addr); - USETW(req.wLength, mlen); - if (zyd_do_request(sc, &req, fw) != 0) - return (EIO); - - addr += mlen / 2; - fw += mlen; - size -= mlen; - } - - /* check whether the upload succeeded */ - req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = ZYD_DOWNLOADSTS; - USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, sizeof(stat)); - if (zyd_do_request(sc, &req, &stat) != 0) - return (EIO); - - sc->sc_flags |= ZYD_FLAG_FWLOADED; - - return (stat & 0x80) ? (EIO) : (0); -} - -static void -zyd_newassoc(struct ieee80211_node *ni, int isnew) -{ - struct ieee80211vap *vap = ni->ni_vap; - - ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); -} - -static void -zyd_scan_start(struct ieee80211com *ic) -{ - struct zyd_softc *sc = ic->ic_ifp->if_softc; - - ZYD_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = ZYD_SCAN_START; - zyd_queue_command(sc, zyd_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - ZYD_UNLOCK(sc); -} - -static void -zyd_scan_end(struct ieee80211com *ic) -{ - struct zyd_softc *sc = ic->ic_ifp->if_softc; - - ZYD_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = ZYD_SCAN_END; - zyd_queue_command(sc, zyd_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - ZYD_UNLOCK(sc); -} - -static void -zyd_set_channel(struct ieee80211com *ic) -{ - struct zyd_softc *sc = ic->ic_ifp->if_softc; - - ZYD_LOCK(sc); - /* do it in a process context */ - sc->sc_scan_action = ZYD_SET_CHANNEL; - zyd_queue_command(sc, zyd_scantask, - &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr); - ZYD_UNLOCK(sc); -} - -static void -zyd_scantask(struct usb2_proc_msg *pm) -{ - struct zyd_task *task = (struct zyd_task *)pm; - struct zyd_softc *sc = task->sc; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - ZYD_LOCK_ASSERT(sc, MA_OWNED); - - switch (sc->sc_scan_action) { - case ZYD_SCAN_START: - /* want broadcast address while scanning */ - zyd_set_bssid(sc, ifp->if_broadcastaddr); - break; - - case ZYD_SET_CHANNEL: - zyd_set_chan(sc, ic->ic_curchan); - break; - - default: /* ZYD_SCAN_END */ - /* restore previous bssid */ - zyd_set_bssid(sc, sc->sc_bssid); - break; - } -} - -static void -zyd_queue_command(struct zyd_softc *sc, usb2_proc_callback_t *fn, - struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) -{ - struct zyd_task *task; - - ZYD_LOCK_ASSERT(sc, MA_OWNED); - - if (usb2_proc_is_gone(&sc->sc_tq)) { - DPRINTF(sc, ZYD_DEBUG_STATE, "proc is gone\n"); - return; /* nothing to do */ - } - /* - * NOTE: The task cannot get executed before we drop the - * "sc_mtx" mutex. It is safe to update fields in the message - * structure after that the message got queued. - */ - task = (struct zyd_task *) - usb2_proc_msignal(&sc->sc_tq, t0, t1); - - /* Setup callback and softc pointers */ - task->hdr.pm_callback = fn; - task->sc = sc; - - /* - * Init and stop must be synchronous! - */ - if ((fn == zyd_init_task) || (fn == zyd_stop_task)) - usb2_proc_mwait(&sc->sc_tq, t0, t1); -} - -static device_method_t zyd_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, zyd_match), - DEVMETHOD(device_attach, zyd_attach), - DEVMETHOD(device_detach, zyd_detach), - - { 0, 0 } -}; - -static driver_t zyd_driver = { - "zyd", - zyd_methods, - sizeof(struct zyd_softc) -}; - -static devclass_t zyd_devclass; - -DRIVER_MODULE(zyd, 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); diff --git a/sys/dev/usb2/wlan/if_zydfw.h b/sys/dev/usb2/wlan/if_zydfw.h deleted file mode 100644 index 46f5c2a..0000000 --- a/sys/dev/usb2/wlan/if_zydfw.h +++ /dev/null @@ -1,1144 +0,0 @@ -/* - * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted provided - * that the following conditions are met: - * 1. Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* $FreeBSD$ */ - -uint8_t zd1211_firmware[] = { - 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE, - 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96, - 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99, - 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE, - 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A, - 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44, - 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE, - 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC, - 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC, - 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8, - 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, - 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A, - 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90, - 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0, - 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, - 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, - 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A, - 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90, - 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, - 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, - 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02, - 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86, - 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80, - 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65, - 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, - 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, - 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4, - 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4, - 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A, - 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2, - 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98, - 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86, - 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2, - 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, - 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1, - 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92, - 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92, - 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8, - 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3, - 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, - 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63, - 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F, - 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, - 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95, - 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82, - 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC, - 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3, - 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3, - 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, - 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD, - 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7, - 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0, - 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE, - 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6, - 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, - 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE, - 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6, - 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, - 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE, - 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6, - 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65, - 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, - 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F, - 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96, - 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96, - 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, - 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96, - 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04, - 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, - 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97, - 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93, - 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97, - 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65, - 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, - 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2, - 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, - 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, - 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, - 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0, - 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3, - 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65, - 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, - 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0, - 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92, - 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC, - 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC, - 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC, - 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC, - 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94, - 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08, - 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF, - 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF, - 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07, - 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC, - 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95, - 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02, - 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48, - 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93, - 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2, - 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF, - 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93, - 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93, - 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, - 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10, - 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9, - 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44, - 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3, - 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05, - 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03, - 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC, - 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5, - 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05, - 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92, - 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96, - 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93, - 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93, - 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4, - 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95, - 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93, - 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93, - 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3, - 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93, - 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4, - 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2, - 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95, - 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2, - 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97, - 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, - 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0, - 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99, - 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93, - 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44, - 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93, - 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF, - 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02, - 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95, - 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95, - 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96, - 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2, - 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC, - 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80, - 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F, - 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04, - 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3, - 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0, - 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93, - 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, - 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93, - 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9, - 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22, - 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC, - 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, - 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D, - 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE, - 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC, - 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95, - 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF, - 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0, - 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, - 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E, - 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82, - 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82, - 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, - 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, - 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2, - 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, - 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, - 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96, - 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E, - 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1, - 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9, - 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4, - 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D, - 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03, - 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9, - 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82, - 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0, - 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94, - 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5, - 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, - 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0, - 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1, - 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3, - 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94, - 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC, - 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0, - 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1, - 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3, - 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92, - 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC, - 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F, - 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7, - 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2, - 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3, - 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94, - 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB, - 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7, - 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3, - 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC, - 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97, - 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1, - 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3, - 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0, - 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96, - 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3, - 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2, - 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44, - 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4, - 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95, - 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80, - 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3, - 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, - 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44, - 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, - 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, - 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43, - 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, - 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, - 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, - 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, - 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, - 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, - 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43, - 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, - 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, - 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, - 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, - 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, - 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09, - 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2, - 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2, - 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, - 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, - 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, - 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F, - 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2, - 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, - 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, - 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93, - 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, - 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, - 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6, - 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC, - 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80, - 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, - 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, - 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46, - 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95, - 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92, - 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05, - 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2, - 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94, - 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC, - 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06, - 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95, - 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96, - 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E, - 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC, - 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92, - 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92, - 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E, - 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E, - 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF, - 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93, - 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93, - 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3, - 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5, - 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E, - 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7, - 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, - 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3, - 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95, - 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96, - 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3, - 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC, - 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3, - 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93, - 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, - 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00, - 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, - 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, - 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, - 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3, - 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, - 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0, - 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92, - 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4, - 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93, - 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC, - 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F, - 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, - 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC, - 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93, - 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, - 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08, - 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC, - 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC, - 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, - 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3, - 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC, - 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, - 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, - 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65, - 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83, - 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80, - 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7, - 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, - 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3, - 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3, - 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5, - 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, - 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94, - 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD, - 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, - 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3, - 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, - 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, - 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4, - 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95, - 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97, - 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92, - 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC, - 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95, - 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92, - 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3, - 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00, - 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42, - 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44, - 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93, - 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, - 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E, - 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82, - 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, - 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94, - 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96, - 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4, - 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E, - 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03, - 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F, - 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92, - 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46, - 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07, - 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9, - 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC, - 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC, - 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4, - 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2, - 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC, - 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F, - 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, - 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94, - 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4, - 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4, - 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96, - 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99, - 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2, - 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, - 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E, - 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3, - 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, - 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82, - 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6, - 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00, - 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44, - 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98, - 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98, - 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2, - 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44, - 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65, - 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2, - 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93, - 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03, - 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4, - 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC, - 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D, - 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2, - 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44, - 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93, - 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, - 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, - 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96, - 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, - 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93, - 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, - 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3, - 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97, - 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, - 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F, - 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC, - 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F, - 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, - 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, - 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80, - 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08, - 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5, - 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3, - 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45, - 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92, - 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, - 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, - 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, - 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00, - 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63, - 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99, - 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96, - 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6, - 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96, - 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, - 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F, - 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95, - 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95, - 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, - 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC, - 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94, - 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2, - 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F, - 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC, - 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93, - 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4, - 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC, - 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6, - 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F, - 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, - 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3, - 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97, - 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, - 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, - 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B, - 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65, - 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99, - 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03, - 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96, - 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5, - 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC, - 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, - 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00, - 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E, - 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F, - 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, - 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65, - 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, - 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43, - 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, - 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, - 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95, - 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6, - 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6, - 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99, - 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC, - 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8, - 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F, - 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E, - 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F, - 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F, - 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F, - 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99, - 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96, - 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6, - 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47, - 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5, - 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99, - 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, - 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, - 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95, - 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E, - 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, - 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, - 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06, - 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0, - 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, - 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E, - 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, - 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92, - 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0, - 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B, - 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94, - 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, - 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2, - 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF, - 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF, - 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42, - 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94, - 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4, - 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4, - 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF, - 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF, - 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6, - 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96, - 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92, - 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6, - 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93, - 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, - 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5, - 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7, - 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7, - 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC, - 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65, - 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, - 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65, - 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, - 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, - 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, - 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC, - 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92, - 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, - 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, - 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94, - 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC, - 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7, - 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43, - 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98, - 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC, - 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC, - 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7, - 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC, - 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2, - 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97, - 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, - 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, - 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93, - 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65, - 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2, - 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96, - 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92, - 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93, - 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93, - 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, - 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, - 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A, - 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94, - 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90, - 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00, - 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, - 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2, - 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7, - 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7, - 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* - * current zd1211b firmware version. - */ -#define ZD1211B_FIRMWARE_VER 4705 - -uint8_t zd1211b_firmware[] = { - 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11, - 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99, - 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4, - 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92, - 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41, - 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00, - 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8, - 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a, - 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef, - 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0, - 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90, - 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98, - 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90, - 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40, - 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93, - 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40, - 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92, - 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f, - 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19, - 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82, - 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07, - 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42, - 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04, - 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, - 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0, - 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b, - 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4, - 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1, - 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee, - 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e, - 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95, - 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3, - 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f, - 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, - 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41, - 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94, - 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, - 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec, - 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c, - 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65, - 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11, - 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97, - 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, - 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, - 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, - 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42, - 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41, - 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42, - 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02, - 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f, - 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02, - 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec, - 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95, - 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65, - 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19, - 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94, - 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, - 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f, - 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09, - 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97, - 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46, - 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e, - 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5, - 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef, - 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09, - 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef, - 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, - 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7, - 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40, - 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00, - 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07, - 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03, - 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a, - 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef, - 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01, - 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff, - 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11, - 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96, - 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59, - 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93, - 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04, - 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96, - 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1, - 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3, - 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02, - 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95, - 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, - 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99, - 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f, - 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0, - 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1, - 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96, - 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3, - 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec, - 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02, - 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec, - 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57, - 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5, - 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00, - 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f, - 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, - 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec, - 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48, - 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22, - 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47, - 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, - 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, - 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, - 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf, - 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82, - 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a, - 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0, - 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, - 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec, - 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09, - 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7, - 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, - 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0, - 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, - 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02, - 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00, - 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2, - 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0, - 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98, - 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40, - 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92, - 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c, - 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69, - 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3, - 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, - 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42, - 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, - 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02, - 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94, - 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff, - 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00, - 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa, - 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, - 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b, - 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1, - 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, - 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb, - 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, - 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3, - 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02, - 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7, - 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, - 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, - 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1, - 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00, - 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c, - 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, - 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10, - 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92, - 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d, - 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec, - 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, - 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3, - 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40, - 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42, - 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40, - 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7, - 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a, - 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1, - 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3, - 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3, - 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, - 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, - 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba, - 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, - 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90, - 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, - 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, - 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, - 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, - 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, - 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, - 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43, - 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, - 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, - 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, - 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02, - 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, - 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe, - 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93, - 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, - 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a, - 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, - 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02, - 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, - 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, - 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd, - 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02, - 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, - 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00, - 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3, - 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41, - 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40, - 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec, - 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96, - 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2, - 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5, - 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98, - 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02, - 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, - 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96, - 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2, - 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43, - 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e, - 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab, - 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4, - 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a, - 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06, - 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a, - 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec, - 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11, - 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7, - 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, - 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80, - 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b, - 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80, - 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6, - 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, - 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41, - 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0, - 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, - 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec, - 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, - 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45, - 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a, - 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, - 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92, - 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63, - 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90, - 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3, - 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40, - 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, - 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02, - 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97, - 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03, - 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, - 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, - 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3, - 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12, - 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, - 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a, - 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a, - 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, - 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, - 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, - 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4, - 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb, - 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99, - 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03, - 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3, - 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01, - 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82, - 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5, - 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44, - 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40, - 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08, - 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11, - 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93, - 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03, - 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93, - 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5, - 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f, - 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9, - 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3, - 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8, - 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff, - 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9, - 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05, - 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a, - 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f, - 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f, - 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f, - 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11, - 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, - 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02, - 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65, - 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, - 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e, - 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40, - 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93, - 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11, - 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4, - 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8, - 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96, - 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68, - 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f, - 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, - 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f, - 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80, - 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2, - 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c, - 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec, - 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b, - 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92, - 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40, - 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec, - 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8, - 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96, - 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c, - 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, - 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02, - 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, - 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f, - 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94, - 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea, - 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2, - 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, - 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42, - 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40, - 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec, - 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01, - 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f, - 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41, - 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, - 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e, - 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5, - 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4, - 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc, - 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2, - 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f, - 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b, - 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95, - 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2, - 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8, - 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11, - 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94, - 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11, - 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5, - 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19, - 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, - 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40, - 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec, - 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3, - 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99, - 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00, - 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f, - 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03, - 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01, - 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99, - 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, - 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0, - 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, - 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, - 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42, - 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65, - 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, - 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, - 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63, - 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2, - 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01, - 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04, - 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1, - 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc, - 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03, - 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42, - 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4, - 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f, - 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f, - 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec, - 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1, - 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47, - 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a, - 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00, - 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, - 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03, - 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02, - 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2, - 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3, - 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0, - 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22, - 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f, - 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8, - 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00, - 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08, - 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4, - 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1, - 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff, - 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c, - 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6, - 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02, - 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93, - 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff, - 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63, - 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1, - 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6, - 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, - 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40, - 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2, - 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91, - 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f, - 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, - 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, - 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, - 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a, - 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, - 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, - 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4, - 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, - 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43, - 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1, - 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3, - 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19, - 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff, - 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02, - 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, - 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15, - 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65, - 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92, - 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09, - 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e, - 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90, - 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe, - 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88, - 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94, - 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90, - 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, - 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12, - 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00, - 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00, - 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f, - 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/sys/dev/usb2/wlan/if_zydreg.h b/sys/dev/usb2/wlan/if_zydreg.h deleted file mode 100644 index 8ef34e3..0000000 --- a/sys/dev/usb2/wlan/if_zydreg.h +++ /dev/null @@ -1,1338 +0,0 @@ -/* $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_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */ -#define ZYD_MACB_TXPWR_CTL1 0x9b00 -#define ZYD_MACB_TXPWR_CTL2 0x9b04 -#define ZYD_MACB_TXPWR_CTL3 0x9b08 -#define ZYD_MACB_TXPWR_CTL4 0x9b0c -#define ZYD_MACB_AIFS_CTL1 0x9b10 -#define ZYD_MACB_AIFS_CTL2 0x9b14 -#define ZYD_MACB_TXOP 0x9b20 -#define ZYD_MACB_MAX_RETRY 0x9b28 - -/* - * Miscellanous registers. - */ -#define ZYD_FIRMWARE_START_ADDR 0xee00 -#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */ - -/* - * EEPROM registers. - */ -#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */ -#define ZYD_EEPROM_SUBID 0xf817 -#define ZYD_EEPROM_POD 0xf819 -#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */ -#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */ -#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */ -#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */ -#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */ -#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ -#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */ -#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */ -#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */ -#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */ -#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */ -#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */ -#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */ - -/* - * Firmware registers offsets (relative to fwbase). - */ -#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */ -#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */ -#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */ -#define ZYD_FW_LINK_STATUS 0x0003 -#define ZYD_FW_SOFT_RESET 0x0004 -#define ZYD_FW_FLASH_CHK 0x0005 - -/* possible flags for register ZYD_FW_LINK_STATUS */ -#define ZYD_LED1 (1 << 8) -#define ZYD_LED2 (1 << 9) - -/* - * RF IDs. - */ -#define ZYD_RF_UW2451 0x2 /* not supported yet */ -#define ZYD_RF_UCHIP 0x3 /* not supported yet */ -#define ZYD_RF_AL2230 0x4 -#define ZYD_RF_AL7230B 0x5 -#define ZYD_RF_THETA 0x6 /* not supported yet */ -#define ZYD_RF_AL2210 0x7 -#define ZYD_RF_MAXIM_NEW 0x8 -#define ZYD_RF_GCT 0x9 -#define ZYD_RF_AL2230S 0xa /* not supported yet */ -#define ZYD_RF_RALINK 0xb /* not supported yet */ -#define ZYD_RF_INTERSIL 0xc /* not supported yet */ -#define ZYD_RF_RFMD 0xd -#define ZYD_RF_MAXIM_NEW2 0xe -#define ZYD_RF_PHILIPS 0xf /* not supported yet */ - -/* - * PHY registers (8 bits, not documented). - */ -#define ZYD_CR0 0x9000 -#define ZYD_CR1 0x9004 -#define ZYD_CR2 0x9008 -#define ZYD_CR3 0x900c -#define ZYD_CR5 0x9010 -#define ZYD_CR6 0x9014 -#define ZYD_CR7 0x9018 -#define ZYD_CR8 0x901c -#define ZYD_CR4 0x9020 -#define ZYD_CR9 0x9024 -#define ZYD_CR10 0x9028 -#define ZYD_CR11 0x902c -#define ZYD_CR12 0x9030 -#define ZYD_CR13 0x9034 -#define ZYD_CR14 0x9038 -#define ZYD_CR15 0x903c -#define ZYD_CR16 0x9040 -#define ZYD_CR17 0x9044 -#define ZYD_CR18 0x9048 -#define ZYD_CR19 0x904c -#define ZYD_CR20 0x9050 -#define ZYD_CR21 0x9054 -#define ZYD_CR22 0x9058 -#define ZYD_CR23 0x905c -#define ZYD_CR24 0x9060 -#define ZYD_CR25 0x9064 -#define ZYD_CR26 0x9068 -#define ZYD_CR27 0x906c -#define ZYD_CR28 0x9070 -#define ZYD_CR29 0x9074 -#define ZYD_CR30 0x9078 -#define ZYD_CR31 0x907c -#define ZYD_CR32 0x9080 -#define ZYD_CR33 0x9084 -#define ZYD_CR34 0x9088 -#define ZYD_CR35 0x908c -#define ZYD_CR36 0x9090 -#define ZYD_CR37 0x9094 -#define ZYD_CR38 0x9098 -#define ZYD_CR39 0x909c -#define ZYD_CR40 0x90a0 -#define ZYD_CR41 0x90a4 -#define ZYD_CR42 0x90a8 -#define ZYD_CR43 0x90ac -#define ZYD_CR44 0x90b0 -#define ZYD_CR45 0x90b4 -#define ZYD_CR46 0x90b8 -#define ZYD_CR47 0x90bc -#define ZYD_CR48 0x90c0 -#define ZYD_CR49 0x90c4 -#define ZYD_CR50 0x90c8 -#define ZYD_CR51 0x90cc -#define ZYD_CR52 0x90d0 -#define ZYD_CR53 0x90d4 -#define ZYD_CR54 0x90d8 -#define ZYD_CR55 0x90dc -#define ZYD_CR56 0x90e0 -#define ZYD_CR57 0x90e4 -#define ZYD_CR58 0x90e8 -#define ZYD_CR59 0x90ec -#define ZYD_CR60 0x90f0 -#define ZYD_CR61 0x90f4 -#define ZYD_CR62 0x90f8 -#define ZYD_CR63 0x90fc -#define ZYD_CR64 0x9100 -#define ZYD_CR65 0x9104 -#define ZYD_CR66 0x9108 -#define ZYD_CR67 0x910c -#define ZYD_CR68 0x9110 -#define ZYD_CR69 0x9114 -#define ZYD_CR70 0x9118 -#define ZYD_CR71 0x911c -#define ZYD_CR72 0x9120 -#define ZYD_CR73 0x9124 -#define ZYD_CR74 0x9128 -#define ZYD_CR75 0x912c -#define ZYD_CR76 0x9130 -#define ZYD_CR77 0x9134 -#define ZYD_CR78 0x9138 -#define ZYD_CR79 0x913c -#define ZYD_CR80 0x9140 -#define ZYD_CR81 0x9144 -#define ZYD_CR82 0x9148 -#define ZYD_CR83 0x914c -#define ZYD_CR84 0x9150 -#define ZYD_CR85 0x9154 -#define ZYD_CR86 0x9158 -#define ZYD_CR87 0x915c -#define ZYD_CR88 0x9160 -#define ZYD_CR89 0x9164 -#define ZYD_CR90 0x9168 -#define ZYD_CR91 0x916c -#define ZYD_CR92 0x9170 -#define ZYD_CR93 0x9174 -#define ZYD_CR94 0x9178 -#define ZYD_CR95 0x917c -#define ZYD_CR96 0x9180 -#define ZYD_CR97 0x9184 -#define ZYD_CR98 0x9188 -#define ZYD_CR99 0x918c -#define ZYD_CR100 0x9190 -#define ZYD_CR101 0x9194 -#define ZYD_CR102 0x9198 -#define ZYD_CR103 0x919c -#define ZYD_CR104 0x91a0 -#define ZYD_CR105 0x91a4 -#define ZYD_CR106 0x91a8 -#define ZYD_CR107 0x91ac -#define ZYD_CR108 0x91b0 -#define ZYD_CR109 0x91b4 -#define ZYD_CR110 0x91b8 -#define ZYD_CR111 0x91bc -#define ZYD_CR112 0x91c0 -#define ZYD_CR113 0x91c4 -#define ZYD_CR114 0x91c8 -#define ZYD_CR115 0x91cc -#define ZYD_CR116 0x91d0 -#define ZYD_CR117 0x91d4 -#define ZYD_CR118 0x91d8 -#define ZYD_CR119 0x91dc -#define ZYD_CR120 0x91e0 -#define ZYD_CR121 0x91e4 -#define ZYD_CR122 0x91e8 -#define ZYD_CR123 0x91ec -#define ZYD_CR124 0x91f0 -#define ZYD_CR125 0x91f4 -#define ZYD_CR126 0x91f8 -#define ZYD_CR127 0x91fc -#define ZYD_CR128 0x9200 -#define ZYD_CR129 0x9204 -#define ZYD_CR130 0x9208 -#define ZYD_CR131 0x920c -#define ZYD_CR132 0x9210 -#define ZYD_CR133 0x9214 -#define ZYD_CR134 0x9218 -#define ZYD_CR135 0x921c -#define ZYD_CR136 0x9220 -#define ZYD_CR137 0x9224 -#define ZYD_CR138 0x9228 -#define ZYD_CR139 0x922c -#define ZYD_CR140 0x9230 -#define ZYD_CR141 0x9234 -#define ZYD_CR142 0x9238 -#define ZYD_CR143 0x923c -#define ZYD_CR144 0x9240 -#define ZYD_CR145 0x9244 -#define ZYD_CR146 0x9248 -#define ZYD_CR147 0x924c -#define ZYD_CR148 0x9250 -#define ZYD_CR149 0x9254 -#define ZYD_CR150 0x9258 -#define ZYD_CR151 0x925c -#define ZYD_CR152 0x9260 -#define ZYD_CR153 0x9264 -#define ZYD_CR154 0x9268 -#define ZYD_CR155 0x926c -#define ZYD_CR156 0x9270 -#define ZYD_CR157 0x9274 -#define ZYD_CR158 0x9278 -#define ZYD_CR159 0x927c -#define ZYD_CR160 0x9280 -#define ZYD_CR161 0x9284 -#define ZYD_CR162 0x9288 -#define ZYD_CR163 0x928c -#define ZYD_CR164 0x9290 -#define ZYD_CR165 0x9294 -#define ZYD_CR166 0x9298 -#define ZYD_CR167 0x929c -#define ZYD_CR168 0x92a0 -#define ZYD_CR169 0x92a4 -#define ZYD_CR170 0x92a8 -#define ZYD_CR171 0x92ac -#define ZYD_CR172 0x92b0 -#define ZYD_CR173 0x92b4 -#define ZYD_CR174 0x92b8 -#define ZYD_CR175 0x92bc -#define ZYD_CR176 0x92c0 -#define ZYD_CR177 0x92c4 -#define ZYD_CR178 0x92c8 -#define ZYD_CR179 0x92cc -#define ZYD_CR180 0x92d0 -#define ZYD_CR181 0x92d4 -#define ZYD_CR182 0x92d8 -#define ZYD_CR183 0x92dc -#define ZYD_CR184 0x92e0 -#define ZYD_CR185 0x92e4 -#define ZYD_CR186 0x92e8 -#define ZYD_CR187 0x92ec -#define ZYD_CR188 0x92f0 -#define ZYD_CR189 0x92f4 -#define ZYD_CR190 0x92f8 -#define ZYD_CR191 0x92fc -#define ZYD_CR192 0x9300 -#define ZYD_CR193 0x9304 -#define ZYD_CR194 0x9308 -#define ZYD_CR195 0x930c -#define ZYD_CR196 0x9310 -#define ZYD_CR197 0x9314 -#define ZYD_CR198 0x9318 -#define ZYD_CR199 0x931c -#define ZYD_CR200 0x9320 -#define ZYD_CR201 0x9324 -#define ZYD_CR202 0x9328 -#define ZYD_CR203 0x932c -#define ZYD_CR204 0x9330 -#define ZYD_CR205 0x9334 -#define ZYD_CR206 0x9338 -#define ZYD_CR207 0x933c -#define ZYD_CR208 0x9340 -#define ZYD_CR209 0x9344 -#define ZYD_CR210 0x9348 -#define ZYD_CR211 0x934c -#define ZYD_CR212 0x9350 -#define ZYD_CR213 0x9354 -#define ZYD_CR214 0x9358 -#define ZYD_CR215 0x935c -#define ZYD_CR216 0x9360 -#define ZYD_CR217 0x9364 -#define ZYD_CR218 0x9368 -#define ZYD_CR219 0x936c -#define ZYD_CR220 0x9370 -#define ZYD_CR221 0x9374 -#define ZYD_CR222 0x9378 -#define ZYD_CR223 0x937c -#define ZYD_CR224 0x9380 -#define ZYD_CR225 0x9384 -#define ZYD_CR226 0x9388 -#define ZYD_CR227 0x938c -#define ZYD_CR228 0x9390 -#define ZYD_CR229 0x9394 -#define ZYD_CR230 0x9398 -#define ZYD_CR231 0x939c -#define ZYD_CR232 0x93a0 -#define ZYD_CR233 0x93a4 -#define ZYD_CR234 0x93a8 -#define ZYD_CR235 0x93ac -#define ZYD_CR236 0x93b0 -#define ZYD_CR240 0x93c0 -#define ZYD_CR241 0x93c4 -#define ZYD_CR242 0x93c8 -#define ZYD_CR243 0x93cc -#define ZYD_CR244 0x93d0 -#define ZYD_CR245 0x93d4 -#define ZYD_CR251 0x93ec -#define ZYD_CR252 0x93f0 -#define ZYD_CR253 0x93f4 -#define ZYD_CR254 0x93f8 -#define ZYD_CR255 0x93fc - -/* copied nearly verbatim from the Linux driver rewrite */ -#define ZYD_DEF_PHY \ -{ \ - { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ - { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \ - { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \ - { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ - { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ - { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \ - { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ - { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \ - { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ - { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ - { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ - { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ - { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ - { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ - { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ - { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ - { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ - { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ - { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ - { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ - { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ - { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \ - { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \ - { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \ - { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \ - { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \ - { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \ - { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \ - { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \ - { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ - { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \ - { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \ - { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \ - { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ - { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ - { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ - { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \ - { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ - { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ - { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ - { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \ - { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \ - { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \ - { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \ - { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ - { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ - { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ - { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ - { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \ - { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \ - { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \ - { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ - { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \ - { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \ - { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \ - { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \ - { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \ - { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \ - { ZYD_CR203, 0x30 }, { 0, 0} \ -} - -#define ZYD_DEF_PHYB \ -{ \ - { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ - { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \ - { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \ - { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ - { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ - { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \ - { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ - { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \ - { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ - { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ - { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ - { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ - { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ - { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \ - { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ - { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ - { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ - { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ - { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ - { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ - { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ - { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \ - { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \ - { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \ - { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \ - { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \ - { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \ - { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \ - { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ - { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \ - { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \ - { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \ - { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \ - { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \ - { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \ - { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \ - { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \ - { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ - { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \ - { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \ - { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ - { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ - { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ - { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ - { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \ - { 0, 0 } \ -} - -#define ZYD_RFMD_PHY \ -{ \ - { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \ - { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \ - { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \ - { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \ - { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \ - { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \ - { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \ - { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \ - { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \ - { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \ - { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \ - { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \ - { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \ - { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \ - { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ - { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \ - { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \ - { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \ - { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \ - { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \ - { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \ - { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \ - { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \ -} - -#define ZYD_RFMD_RF \ -{ \ - 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \ - 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \ - 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \ -} - -#define ZYD_RFMD_CHANTABLE \ -{ \ - { 0x181979, 0x1e6666 }, \ - { 0x181989, 0x1e6666 }, \ - { 0x181999, 0x1e6666 }, \ - { 0x1819a9, 0x1e6666 }, \ - { 0x1819b9, 0x1e6666 }, \ - { 0x1819c9, 0x1e6666 }, \ - { 0x1819d9, 0x1e6666 }, \ - { 0x1819e9, 0x1e6666 }, \ - { 0x1819f9, 0x1e6666 }, \ - { 0x181a09, 0x1e6666 }, \ - { 0x181a19, 0x1e6666 }, \ - { 0x181a29, 0x1e6666 }, \ - { 0x181a39, 0x1e6666 }, \ - { 0x181a60, 0x1c0000 } \ -} - -#define ZYD_AL2230_PHY \ -{ \ - { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ - { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ - { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \ - { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \ - { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \ - { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \ - { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \ - { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \ - { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \ - { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \ - { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \ - { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \ - { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \ - { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \ - { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \ - { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \ -} - -#define ZYD_AL2230_PHY_B \ -{ \ - { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \ - { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ - { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \ - { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \ - { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \ - { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \ - { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \ - { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \ - { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ - { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ - { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \ - { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \ - { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \ - { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \ - { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ - { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \ - { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \ - { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \ -} - -#define ZYD_AL2230_PHY_PART1 \ -{ \ - { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \ -} - -#define ZYD_AL2230_PHY_PART2 \ -{ \ - { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \ -} - -#define ZYD_AL2230_PHY_PART3 \ -{ \ - { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ -} - -#define ZYD_AL2230S_PHY_INIT \ -{ \ - { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \ - { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \ - { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \ - { ZYD_CR130, 0x10 } \ -} - -#define ZYD_AL2230_PHY_FINI_PART1 \ -{ \ - { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \ - { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \ - { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \ -} - -#define ZYD_AL2230_RF_PART1 \ -{ \ - 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \ -} - -#define ZYD_AL2230_RF_PART2 \ -{ \ - 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ - 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \ -} - -#define ZYD_AL2230_RF_PART3 \ -{ \ - 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ -} - -#define ZYD_AL2230_RF_B \ -{ \ - 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ - 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \ - 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \ -} - -#define ZYD_AL2230_RF_B_PART1 \ -{ \ - 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \ -} - -#define ZYD_AL2230_RF_B_PART2 \ -{ \ - 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \ - 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \ -} - -#define ZYD_AL2230_RF_B_PART3 \ -{ \ - 0xf01b00, 0xf01e00, 0xf01a00 \ -} - -#define ZYD_AL2230_CHANTABLE \ -{ \ - { 0x03f790, 0x033331, 0x00000d }, \ - { 0x03f790, 0x0b3331, 0x00000d }, \ - { 0x03e790, 0x033331, 0x00000d }, \ - { 0x03e790, 0x0b3331, 0x00000d }, \ - { 0x03f7a0, 0x033331, 0x00000d }, \ - { 0x03f7a0, 0x0b3331, 0x00000d }, \ - { 0x03e7a0, 0x033331, 0x00000d }, \ - { 0x03e7a0, 0x0b3331, 0x00000d }, \ - { 0x03f7b0, 0x033331, 0x00000d }, \ - { 0x03f7b0, 0x0b3331, 0x00000d }, \ - { 0x03e7b0, 0x033331, 0x00000d }, \ - { 0x03e7b0, 0x0b3331, 0x00000d }, \ - { 0x03f7c0, 0x033331, 0x00000d }, \ - { 0x03e7c0, 0x066661, 0x00000d } \ -} - -#define ZYD_AL2230_CHANTABLE_B \ -{ \ - { 0x09efc0, 0x8cccc0, 0xb00000 }, \ - { 0x09efc0, 0x8cccd0, 0xb00000 }, \ - { 0x09e7c0, 0x8cccc0, 0xb00000 }, \ - { 0x09e7c0, 0x8cccd0, 0xb00000 }, \ - { 0x05efc0, 0x8cccc0, 0xb00000 }, \ - { 0x05efc0, 0x8cccd0, 0xb00000 }, \ - { 0x05e7c0, 0x8cccc0, 0xb00000 }, \ - { 0x05e7c0, 0x8cccd0, 0xb00000 }, \ - { 0x0defc0, 0x8cccc0, 0xb00000 }, \ - { 0x0defc0, 0x8cccd0, 0xb00000 }, \ - { 0x0de7c0, 0x8cccc0, 0xb00000 }, \ - { 0x0de7c0, 0x8cccd0, 0xb00000 }, \ - { 0x03efc0, 0x8cccc0, 0xb00000 }, \ - { 0x03e7c0, 0x866660, 0xb00000 } \ -} - -#define ZYD_AL7230B_PHY_1 \ -{ \ - { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \ - { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \ - { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \ - { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \ - { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \ - { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \ - { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \ - { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \ - { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ - { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \ - { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \ - { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \ - { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \ - { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \ - { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \ - { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \ - { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \ - { ZYD_CR251, 0x2f } \ -} - -#define ZYD_AL7230B_PHY_2 \ -{ \ - { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \ - { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \ -} - -#define ZYD_AL7230B_PHY_3 \ -{ \ - { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \ -} - -#define ZYD_AL7230B_RF_1 \ -{ \ - 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \ - 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \ - 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \ -} - -#define ZYD_AL7230B_RF_2 \ -{ \ - 0xf15d59, 0xf15d5c, 0xf15d58 \ -} - -#define ZYD_AL7230B_RF_SETCHANNEL \ -{ \ - 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \ - 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \ -} - -#define ZYD_AL7230B_CHANTABLE \ -{ \ - { 0x09ec00, 0x8cccc8 }, \ - { 0x09ec00, 0x8cccd8 }, \ - { 0x09ec00, 0x8cccc0 }, \ - { 0x09ec00, 0x8cccd0 }, \ - { 0x05ec00, 0x8cccc8 }, \ - { 0x05ec00, 0x8cccd8 }, \ - { 0x05ec00, 0x8cccc0 }, \ - { 0x05ec00, 0x8cccd0 }, \ - { 0x0dec00, 0x8cccc8 }, \ - { 0x0dec00, 0x8cccd8 }, \ - { 0x0dec00, 0x8cccc0 }, \ - { 0x0dec00, 0x8cccd0 }, \ - { 0x03ec00, 0x8cccc8 }, \ - { 0x03ec00, 0x866660 } \ -} - -#define ZYD_AL2210_PHY \ -{ \ - { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \ - { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \ - { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \ - { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \ - { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \ - { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \ - { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \ - { ZYD_CR127, 0x03 } \ -} - -#define ZYD_AL2210_RF \ -{ \ - 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \ - 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \ -} - -#define ZYD_AL2210_CHANTABLE \ -{ \ - 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \ - 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \ - 0x019a80, 0x019b40 \ -} - -#define ZYD_GCT_PHY \ -{ \ - { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \ - { ZYD_CR20, 0x0c }, { ZYD_CR17, 0x65 }, { ZYD_CR34, 0x04 }, \ - { ZYD_CR35, 0x35 }, { ZYD_CR24, 0x20 }, { ZYD_CR9, 0xe0 }, \ - { ZYD_CR127, 0x02 }, { ZYD_CR10, 0x91 }, { ZYD_CR23, 0x7f }, \ - { ZYD_CR27, 0x10 }, { ZYD_CR28, 0x7a }, { ZYD_CR79, 0xb5 }, \ - { ZYD_CR64, 0x80 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 } \ -} - -#define ZYD_GCT_RF \ -{ \ - 0x1f0000, 0x1f0000, 0x1f0200, 0x1f0600, 0x1f8600, 0x1f8600, \ - 0x002050, 0x1f8000, 0x1f8200, 0x1f8600, 0x1c0000, 0x10c458, \ - 0x088e92, 0x187b82, 0x0401b4, 0x140816, 0x0c7000, 0x1c0000, \ - 0x02ccae, 0x128023, 0x0a0000, 0x1a0000, 0x06e380, 0x16cb94, \ - 0x0e1740, 0x014980, 0x116240, 0x090000, 0x192304, 0x05112f, \ - 0x0d54a8, 0x0f8000, 0x1c0008, 0x1c0000, 0x1a0000, 0x1c0008, \ - 0x150000, 0x0c7000, 0x150800, 0x150000 \ -} - -#define ZYD_GCT_CHANTABLE \ -{ \ - 0x1a0000, 0x1a8000, 0x1a4000, 0x1ac000, 0x1a2000, 0x1aa000, \ - 0x1a6000, 0x1ae000, 0x1a1000, 0x1a9000, 0x1a5000, 0x1ad000, \ - 0x1a3000, 0x1ab000 \ -} - -#define ZYD_MAXIM_PHY \ -{ \ - { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ - { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ - { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ - { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ - { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ - { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ - { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ - { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ - { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ - { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ - { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ - { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x13 }, \ - { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x13 }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ - { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe }, \ - { ZYD_CR150, 0x0d } \ -} - -#define ZYD_MAXIM_RF \ -{ \ - 0x00ccd4, 0x030a03, 0x000400, 0x000ca1, 0x010072, 0x018645, \ - 0x004006, 0x0000a7, 0x008258, 0x003fc9, 0x00040a, 0x00000b, \ - 0x00026c \ -} - -#define ZYD_MAXIM_CHANTABLE \ -{ \ - { 0x0ccd4, 0x30a03 }, \ - { 0x22224, 0x00a13 }, \ - { 0x37774, 0x10a13 }, \ - { 0x0ccd4, 0x30a13 }, \ - { 0x22224, 0x00a23 }, \ - { 0x37774, 0x10a23 }, \ - { 0x0ccd4, 0x30a23 }, \ - { 0x22224, 0x00a33 }, \ - { 0x37774, 0x10a33 }, \ - { 0x0ccd4, 0x30a33 }, \ - { 0x22224, 0x00a43 }, \ - { 0x37774, 0x10a43 }, \ - { 0x0ccd4, 0x30a43 }, \ - { 0x199a4, 0x20a53 } \ -} - -#define ZYD_MAXIM2_PHY \ -{ \ - { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ - { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ - { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ - { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ - { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ - { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ - { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ - { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ - { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ - { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ - { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ - { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \ - { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \ - { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ - { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ - { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ - { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ - { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ - { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \ -} - -#define ZYD_MAXIM2_RF \ -{ \ - 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \ - 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \ -} - -#define ZYD_MAXIM2_CHANTABLE_F \ -{ \ - 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \ - 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \ -} - -#define ZYD_MAXIM2_CHANTABLE \ -{ \ - { 0x33334, 0x10a03 }, \ - { 0x08884, 0x20a13 }, \ - { 0x1ddd4, 0x30a13 }, \ - { 0x33334, 0x10a13 }, \ - { 0x08884, 0x20a23 }, \ - { 0x1ddd4, 0x30a23 }, \ - { 0x33334, 0x10a23 }, \ - { 0x08884, 0x20a33 }, \ - { 0x1ddd4, 0x30a33 }, \ - { 0x33334, 0x10a33 }, \ - { 0x08884, 0x20a43 }, \ - { 0x1ddd4, 0x30a43 }, \ - { 0x33334, 0x10a43 }, \ - { 0x26664, 0x20a53 } \ -} - -/* - * Control pipe requests. - */ -#define ZYD_DOWNLOADREQ 0x30 -#define ZYD_DOWNLOADSTS 0x31 -#define ZYD_READFWDATAREQ 0x32 - -/* possible values for register ZYD_CR_INTERRUPT */ -#define ZYD_HWINT_MASK 0x004f0000 - -/* possible values for register ZYD_MAC_MISC */ -#define ZYD_UNLOCK_PHY_REGS 0x80 - -/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */ -#define ZYD_ENC_SNIFFER 8 - -/* flags for register ZYD_MAC_RXFILTER */ -#define ZYD_FILTER_ASS_REQ (1 << 0) -#define ZYD_FILTER_ASS_RSP (1 << 1) -#define ZYD_FILTER_REASS_REQ (1 << 2) -#define ZYD_FILTER_REASS_RSP (1 << 3) -#define ZYD_FILTER_PRB_REQ (1 << 4) -#define ZYD_FILTER_PRB_RSP (1 << 5) -#define ZYD_FILTER_BCN (1 << 8) -#define ZYD_FILTER_ATIM (1 << 9) -#define ZYD_FILTER_DEASS (1 << 10) -#define ZYD_FILTER_AUTH (1 << 11) -#define ZYD_FILTER_DEAUTH (1 << 12) -#define ZYD_FILTER_PS_POLL (1 << 26) -#define ZYD_FILTER_RTS (1 << 27) -#define ZYD_FILTER_CTS (1 << 28) -#define ZYD_FILTER_ACK (1 << 29) -#define ZYD_FILTER_CFE (1 << 30) -#define ZYD_FILTER_CFE_A (1 << 31) - -/* helpers for register ZYD_MAC_RXFILTER */ -#define ZYD_FILTER_MONITOR 0xffffffff -#define ZYD_FILTER_BSS \ - (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \ - ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \ - ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \ - (0x3 << 6) | \ - ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \ - ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \ - (0x7 << 13) | \ - ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK) -#define ZYD_FILTER_HOSTAP \ - (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \ - ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \ - ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL) - -struct zyd_tx_desc { - uint8_t phy; -#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf) -#define ZYD_TX_PHY_OFDM (1 << 4) -#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */ -#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */ - uint16_t len; - uint8_t flags; -#define ZYD_TX_FLAG_BACKOFF (1 << 0) -#define ZYD_TX_FLAG_MULTICAST (1 << 1) -#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2) -#define ZYD_TX_TYPE_DATA 0 -#define ZYD_TX_TYPE_PS_POLL 1 -#define ZYD_TX_TYPE_MGMT 2 -#define ZYD_TX_TYPE_CTL 3 -#define ZYD_TX_FLAG_WAKEUP (1 << 4) -#define ZYD_TX_FLAG_RTS (1 << 5) -#define ZYD_TX_FLAG_ENCRYPT (1 << 6) -#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7) - uint16_t pktlen; - uint16_t plcp_length; - uint8_t plcp_service; -#define ZYD_PLCP_LENGEXT 0x80 - uint16_t nextlen; -} __packed; - -struct zyd_plcphdr { - uint8_t signal; - uint8_t reserved[2]; - uint16_t service; /* unaligned! */ -} __packed; - -struct zyd_rx_stat { - uint8_t signal_cck; - uint8_t rssi; - uint8_t signal_ofdm; - uint8_t cipher; -#define ZYD_RX_CIPHER_WEP64 1 -#define ZYD_RX_CIPHER_TKIP 2 -#define ZYD_RX_CIPHER_AES 4 -#define ZYD_RX_CIPHER_WEP128 5 -#define ZYD_RX_CIPHER_WEP256 6 -#define ZYD_RX_CIPHER_WEP \ - (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256) - uint8_t flags; -#define ZYD_RX_OFDM (1 << 0) -#define ZYD_RX_TIMEOUT (1 << 1) -#define ZYD_RX_OVERRUN (1 << 2) -#define ZYD_RX_DECRYPTERR (1 << 3) -#define ZYD_RX_BADCRC32 (1 << 4) -#define ZYD_RX_NOT2ME (1 << 5) -#define ZYD_RX_BADCRC16 (1 << 6) -#define ZYD_RX_ERROR (1 << 7) -} __packed; - -/* this structure may be unaligned */ -struct zyd_rx_desc { -#define ZYD_MAX_RXFRAMECNT 3 - uWord len[ZYD_MAX_RXFRAMECNT]; - uWord tag; -#define ZYD_TAG_MULTIFRAME 0x697e -} __packed; - -/* I2C bus alike */ -struct zyd_rfwrite_cmd { - uint16_t code; - uint16_t width; - uint16_t bit[32]; -#define ZYD_RF_IF_LE (1 << 1) -#define ZYD_RF_CLK (1 << 2) -#define ZYD_RF_DATA (1 << 3) -} __packed; - -struct zyd_cmd { - uint16_t code; -#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */ -#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */ -#define ZYD_CMD_RFCFG 0x0023 /* write RF register */ -#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */ -#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */ -#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */ - uint8_t data[64]; -} __packed; - -/* structure for command ZYD_CMD_IOWR */ -struct zyd_pair { - uint16_t reg; -/* helpers macros to read/write 32-bit registers */ -#define ZYD_REG32_LO(reg) (reg) -#define ZYD_REG32_HI(reg) \ - ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1)) - uint16_t val; -} __packed; - -/* structure for notification ZYD_NOTIF_RETRYSTATUS */ -struct zyd_notif_retry { - uint16_t rate; - uint8_t macaddr[IEEE80211_ADDR_LEN]; - uint16_t count; -} __packed; - -#define ZYD_CONFIG_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 ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ -#define ZYX_MAX_RXBUFSZ \ - ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \ - sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \ - sizeof (struct zyd_rx_desc)) -#define ZYD_TX_DESC_SIZE (sizeof (struct zyd_tx_desc)) - -#define ZYD_RX_LIST_CNT 1 -#define ZYD_TX_LIST_CNT 5 -#define ZYD_CMD_FLAG_READ (1 << 0) -#define ZYD_CMD_FLAG_SENT (1 << 1) - -/* 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_task { - struct usb2_proc_msg hdr; - struct zyd_softc *sc; -}; - -struct zyd_tx_data { - STAILQ_ENTRY(zyd_tx_data) next; - struct zyd_softc *sc; - struct zyd_tx_desc desc; - struct mbuf *m; - struct ieee80211_node *ni; - int rate; -}; -typedef STAILQ_HEAD(, zyd_tx_data) zyd_txdhead; - -struct zyd_rx_data { - struct mbuf *m; - int rssi; -}; - -struct zyd_node { - struct ieee80211_node ni; /* must be the first */ - struct ieee80211_amrr_node amn; -}; -#define ZYD_NODE(ni) ((struct zyd_node *)(ni)) - -struct zyd_rx_radiotap_header { - struct ieee80211_radiotap_header wr_ihdr; - uint8_t wr_flags; - uint8_t wr_rate; - uint16_t wr_chan_freq; - uint16_t wr_chan_flags; - int8_t wr_antsignal; - int8_t wr_antnoise; -} __packed; - -#define ZYD_RX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL)) - -struct zyd_tx_radiotap_header { - struct ieee80211_radiotap_header wt_ihdr; - uint8_t wt_flags; - uint8_t wt_rate; - uint16_t wt_chan_freq; - uint16_t wt_chan_flags; -} __packed; - -#define ZYD_TX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL)) - -struct zyd_softc; /* forward declaration */ - -struct zyd_rf { - /* RF methods */ - int (*init)(struct zyd_rf *); - int (*switch_radio)(struct zyd_rf *, int); - int (*set_channel)(struct zyd_rf *, uint8_t); - int (*bandedge6)(struct zyd_rf *, - struct ieee80211_channel *); - /* RF attributes */ - struct zyd_softc *rf_sc; /* back-pointer */ - int width; -}; - -struct zyd_rq { - struct zyd_cmd *cmd; - const uint16_t *idata; - struct zyd_pair *odata; - int ilen; - int olen; - int flags; - STAILQ_ENTRY(zyd_rq) rq; -}; - -struct zyd_vap { - struct ieee80211vap vap; - int (*newstate)(struct ieee80211vap *, - enum ieee80211_state, int); - struct ieee80211_amrr amrr; -}; -#define ZYD_VAP(vap) ((struct zyd_vap *)(vap)) - -enum { - ZYD_BULK_WR, - ZYD_BULK_RD, - ZYD_INTR_WR, - ZYD_INTR_RD, - ZYD_N_TRANSFER = 4, -}; - -struct zyd_softc { - struct ifnet *sc_ifp; - device_t sc_dev; - struct usb2_device *sc_udev; - struct usb2_process sc_tq; - - struct usb2_xfer *sc_xfer[ZYD_N_TRANSFER]; - - enum ieee80211_state sc_state; - int sc_arg; - int sc_flags; -#define ZYD_FLAG_FWLOADED (1 << 0) -#define ZYD_FLAG_INITONCE (1 << 1) -#define ZYD_FLAG_INITDONE (1 << 2) - int sc_if_flags; - - struct zyd_task sc_synctask[2]; - struct zyd_task sc_mcasttask[2]; - struct zyd_task sc_scantask[2]; - int sc_scan_action; -#define ZYD_SCAN_START 0 -#define ZYD_SCAN_END 1 -#define ZYD_SET_CHANNEL 2 - struct zyd_task sc_task[2]; - - struct zyd_rf sc_rf; - - STAILQ_HEAD(, zyd_rq) sc_rtx; - STAILQ_HEAD(, zyd_rq) sc_rqh; - - uint8_t sc_bssid[IEEE80211_ADDR_LEN]; - uint16_t sc_fwbase; - uint8_t sc_regdomain; - uint8_t sc_macrev; - uint16_t sc_fwrev; - uint8_t sc_rfrev; - uint8_t sc_parev; - uint8_t sc_al2230s; - uint8_t sc_bandedge6; - uint8_t sc_newphy; - uint8_t sc_cckgain; - uint8_t sc_fix_cr157; - uint8_t sc_ledtype; - uint8_t sc_txled; - - uint32_t sc_atim_wnd; - uint32_t sc_pre_tbtt; - uint32_t sc_bcn_int; - - uint8_t sc_pwrcal[14]; - uint8_t sc_pwrint[14]; - uint8_t sc_ofdm36_cal[14]; - uint8_t sc_ofdm48_cal[14]; - uint8_t sc_ofdm54_cal[14]; - - struct mtx sc_mtx; - struct cv sc_intr_cv; - struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT]; - zyd_txdhead tx_q; - zyd_txdhead tx_free; - int tx_nfree; - struct zyd_rx_desc sc_rx_desc; - struct zyd_rx_data sc_rx_data[ZYD_MAX_RXFRAMECNT]; - int sc_rx_count; - - struct zyd_cmd sc_ibuf; - - struct zyd_rx_radiotap_header sc_rxtap; - int sc_rxtap_len; - struct zyd_tx_radiotap_header sc_txtap; - int sc_txtap_len; -}; - -#define ZYD_LOCK(sc) mtx_lock(&(sc)->sc_mtx) -#define ZYD_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) -#define ZYD_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) - diff --git a/sys/dev/usb2/wlan/usb2_wlan.c b/sys/dev/usb2/wlan/usb2_wlan.c deleted file mode 100644 index 35c9cbb..0000000 --- a/sys/dev/usb2/wlan/usb2_wlan.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $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 deleted file mode 100644 index 9db120e..0000000 --- a/sys/dev/usb2/wlan/usb2_wlan.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $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_ */ -- cgit v1.1