diff options
author | thompsa <thompsa@FreeBSD.org> | 2009-02-23 18:31:00 +0000 |
---|---|---|
committer | thompsa <thompsa@FreeBSD.org> | 2009-02-23 18:31:00 +0000 |
commit | 111a707d99983acd73c4212036000aa3e88425b6 (patch) | |
tree | 691e9b9009214e6138d3913e4c022c897e386667 /sys/dev/usb/net | |
parent | 124e83aa64b26f6b00736a45c3e25dfda8c7060e (diff) | |
download | FreeBSD-src-111a707d99983acd73c4212036000aa3e88425b6.zip FreeBSD-src-111a707d99983acd73c4212036000aa3e88425b6.tar.gz |
Move the new USB stack into its new home.
Diffstat (limited to 'sys/dev/usb/net')
-rw-r--r-- | sys/dev/usb/net/if_aue.c | 1054 | ||||
-rw-r--r-- | sys/dev/usb/net/if_auereg.h | 220 | ||||
-rw-r--r-- | sys/dev/usb/net/if_axe.c | 1076 | ||||
-rw-r--r-- | sys/dev/usb/net/if_axereg.h | 196 | ||||
-rw-r--r-- | sys/dev/usb/net/if_cdce.c | 771 | ||||
-rw-r--r-- | sys/dev/usb/net/if_cdcereg.h | 68 | ||||
-rw-r--r-- | sys/dev/usb/net/if_cue.c | 645 | ||||
-rw-r--r-- | sys/dev/usb/net/if_cuereg.h | 132 | ||||
-rw-r--r-- | sys/dev/usb/net/if_kue.c | 704 | ||||
-rw-r--r-- | sys/dev/usb/net/if_kuefw.h | 685 | ||||
-rw-r--r-- | sys/dev/usb/net/if_kuereg.h | 141 | ||||
-rw-r--r-- | sys/dev/usb/net/if_rue.c | 913 | ||||
-rw-r--r-- | sys/dev/usb/net/if_ruereg.h | 183 | ||||
-rw-r--r-- | sys/dev/usb/net/if_udav.c | 856 | ||||
-rw-r--r-- | sys/dev/usb/net/if_udavreg.h | 166 | ||||
-rw-r--r-- | sys/dev/usb/net/usb_ethernet.c | 587 | ||||
-rw-r--r-- | sys/dev/usb/net/usb_ethernet.h | 122 |
17 files changed, 8519 insertions, 0 deletions
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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Copyright (c) 2006 + * Alfred Perlstein <alfred@freebsd.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver. + * Datasheet is available from http://www.admtek.com.tw. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + * + * SMP locking by Alfred Perlstein <alfred@freebsd.org>. + * RED Inc. + */ + +/* + * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet + * support: the control endpoint for reading/writing registers, burst + * read endpoint for packet reception, burst write for packet transmission + * and one for "interrupts." The chip uses the same RX filter scheme + * as the other ADMtek ethernet parts: one perfect filter entry for the + * the station address and a 64-bit multicast hash table. The chip supports + * both MII and HomePNA attachments. + * + * Since the maximum data transfer speed of USB is supposed to be 12Mbps, + * you're never really going to get 100Mbps speeds from this device. I + * think the idea is to allow the device to connect to 10 or 100Mbps + * networks, not necessarily to provide 100Mbps performance. Also, since + * the controller uses an external PHY chip, it's possible that board + * designers might simply choose a 10Mbps PHY. + * + * Registers are accessed using usb2_ether_do_request(). Packet + * transfers are done using usb2_transfer() and friends. + */ + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR aue_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_auereg.h> + +#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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * 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 <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. + * Used in the LinkSys USB200M and various other adapters. + * + * Manuals available from: + * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF + * Note: you need the manual for the AX88170 chip (USB 1.x ethernet + * controller) to find the definitions for the RX control register. + * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF + * + * Written by Bill Paul <wpaul@windriver.com> + * Senior Engineer + * Wind River Systems + */ + +/* + * The AX88172 provides USB ethernet supports at 10 and 100Mbps. + * It uses an external PHY (reference designs use a RealTek chip), + * and has a 64-bit multicast hash filter. There is some information + * missing from the manual which one needs to know in order to make + * the chip function: + * + * - You must set bit 7 in the RX control register, otherwise the + * chip won't receive any packets. + * - You must initialize all 3 IPG registers, or you won't be able + * to send any packets. + * + * Note that this device appears to only support loading the station + * address via autload from the EEPROM (i.e. there's no way to manaully + * set it). + * + * (Adam Weinberger wanted me to name this driver if_gir.c.) + */ + +/* + * 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 <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR axe_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_axereg.h> + +/* + * AXE_178_MAX_FRAME_BURST + * max frame burst size for Ax88178 and Ax88772 + * 0 2048 bytes + * 1 4096 bytes + * 2 8192 bytes + * 3 16384 bytes + * use the largest your system can handle without USB stalling. + * + * NB: 88772 parts appear to generate lots of input errors with + * a 2K rx buffer and 8K is only slightly faster than 4K on an + * EHCI port on a T42 so change at your own risk. + */ +#define AXE_178_MAX_FRAME_BURST 1 + +#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 <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the ASIX Electronics AX88172, 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 <wpaul@windriver.com> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> +#include <dev/usb/usb_cdc.h> +#include <dev/usb/usb_defs.h> + +#define USB_DEBUG_VAR cdce_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> +#include <dev/usb/usb_parse.h> +#include <dev/usb/usb_device.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_cdcereg.h> + +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; + } + } + + /* + * <quote> + * + * The Data Class interface of a networking device shall have + * a minimum of two interface settings. The first setting + * (the default interface setting) includes no endpoints and + * therefore no networking traffic is exchanged whenever the + * default interface setting is selected. One or more + * additional interface settings are used for normal + * operation, and therefore each includes a pair of endpoints + * (one IN, and one OUT) to exchange network traffic. Select + * an alternate interface setting to initialize the network + * aspects of the device and to enable the exchange of + * network traffic. + * + * </quote> + * + * Some devices, most notably cable modems, include interface + * settings that have no IN or OUT endpoint, therefore loop + * through the list of all available interface settings + * looking for one with both IN and OUT endpoints. + */ + +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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate + * adapters and others. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The + * RX filter uses a 512-bit multicast hash table, single perfect entry + * for the station address, and promiscuous mode. Unlike the ADMtek + * and KLSI chips, the CATC ASIC supports read and write combining + * mode where multiple packets can be transfered using a single bulk + * transaction, which helps performance a great deal. + */ + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR cue_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_cuereg.h> + +/* + * 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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the CATC Netmate II USB to ethernet controller. + */ + +/* Vendor specific control commands. */ +#define CUE_CMD_RESET 0xF4 +#define CUE_CMD_GET_MACADDR 0xF2 +#define CUE_CMD_WRITEREG 0xFA +#define CUE_CMD_READREG 0xFB +#define CUE_CMD_READSRAM 0xF1 +#define CUE_CMD_WRITESRAM 0xFC +/* Internal registers. */ +#define CUE_TX_BUFCNT 0x20 +#define CUE_RX_BUFCNT 0x21 +#define CUE_ADVANCED_OPMODES 0x22 +#define CUE_TX_BUFPKTS 0x23 +#define CUE_RX_BUFPKTS 0x24 +#define CUE_RX_MAXCHAIN 0x25 +#define CUE_ETHCTL 0x60 +#define CUE_ETHSTS 0x61 +#define CUE_PAR5 0x62 +#define CUE_PAR4 0x63 +#define CUE_PAR3 0x64 +#define CUE_PAR2 0x65 +#define CUE_PAR1 0x66 +#define CUE_PAR0 0x67 +/* Error counters, all 16 bits wide. */ +#define CUE_TX_SINGLECOLL 0x69 +#define CUE_TX_MULTICOLL 0x6B +#define CUE_TX_EXCESSCOLL 0x6D +#define CUE_RX_FRAMEERR 0x6F +#define CUE_LEDCTL 0x81 +/* Advenced operating mode register. */ +#define CUE_AOP_SRAMWAITS 0x03 +#define CUE_AOP_EMBED_RXLEN 0x08 +#define CUE_AOP_RXCOMBINE 0x10 +#define CUE_AOP_TXCOMBINE 0x20 +#define CUE_AOP_EVEN_PKT_READS 0x40 +#define CUE_AOP_LOOPBK 0x80 +/* Ethernet control register. */ +#define CUE_ETHCTL_RX_ON 0x01 +#define CUE_ETHCTL_LINK_POLARITY 0x02 +#define CUE_ETHCTL_LINK_FORCE_OK 0x04 +#define CUE_ETHCTL_MCAST_ON 0x08 +#define CUE_ETHCTL_PROMISC 0x10 +/* Ethernet status register. */ +#define CUE_ETHSTS_NO_CARRIER 0x01 +#define CUE_ETHSTS_LATECOLL 0x02 +#define CUE_ETHSTS_EXCESSCOLL 0x04 +#define CUE_ETHSTS_TXBUF_AVAIL 0x08 +#define CUE_ETHSTS_BAD_POLARITY 0x10 +#define CUE_ETHSTS_LINK_OK 0x20 +/* LED control register. */ +#define CUE_LEDCTL_BLINK_1X 0x00 +#define CUE_LEDCTL_BLINK_2X 0x01 +#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02 +#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03 +#define CUE_LEDCTL_OFF 0x04 +#define CUE_LEDCTL_FOLLOW_LINK 0x08 + +/* + * Address in ASIC's internal SRAM where the multicast hash table lives. + * The table is 64 bytes long, giving us a 512-bit table. We have to set + * the bit that corresponds to the broadcast address in order to enable + * reception of broadcast frames. + */ +#define CUE_MCAST_TABLE_ADDR 0xFA80 + +#define CUE_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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The KLSI USB to ethernet adapter chip contains an USB serial interface, + * ethernet MAC and embedded microcontroller (called the QT Engine). + * The chip must have firmware loaded into it before it will operate. + * Packets are passed between the chip and host via bulk transfers. + * There is an interrupt endpoint mentioned in the software spec, however + * it's currently unused. This device is 10Mbps half-duplex only, hence + * there is no media selection logic. The MAC supports a 128 entry + * multicast filter, though the exact size of the filter can depend + * on the firmware. Curiously, while the software spec describes various + * ethernet statistics counters, my sample adapter and firmware combination + * claims not to support any statistics counters at all. + * + * Note that once we load the firmware in the device, we have to be + * careful not to load it again: if you restart your computer but + * leave the adapter attached to the USB controller, it may remain + * powered on and retain its firmware. In this case, we don't need + * to load the firmware a second time. + * + * Special thanks to Rob Furr for providing an ADS Technologies + * adapter for development and testing. No monkeys were harmed during + * the development of this driver. + */ + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR kue_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_kuereg.h> +#include <dev/usb/net/if_kuefw.h> + +/* + * 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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file contains the firmware needed to make the KLSI chip work, + * along with a few constants related to the QT Engine microcontroller + * embedded in the KLSI part. + * + * Firmware is loaded using the vendor-specific 'send scan data' + * command (0xFF). The basic operation is that we must load the + * firmware, then issue some trigger commands to fix it up and start + * it running. There are three transfers: load the binary code, + * load the 'fixup' (data segment?), then issue a command to + * start the code firmware running. The data itself is prefixed by + * a 16-bit signature word, a 16-bit length value, a type byte + * and an interrupt (command) byte. The code segment is of type + * 0x02 (replacement interrupt vector data) and the fixup segment + * is of type 0x03 (replacement interrupt fixup data). The interrupt + * code is 0x64 (load new code). The length word is the total length + * of the segment minus 7. I precomputed the values and stuck them + * into the appropriate locations within the segments to save some + * work in the driver. + */ + +/* QT controller data block types. */ +/* Write data into specific memory location. */ +#define KUE_QTBTYPE_WRITE_DATA 0x00 +/* Write data into interrupt vector location */ +#define KUE_QTBTYPE_WRITE_INTVEC 0x01 +/* Replace interrupt vector with this data */ +#define KUE_QTBTYPE_REPL_INTVEC 0x02 +/* Fixup interrupt vector code with this data */ +#define KUE_QTBTYPE_FIXUP_INTVEC 0x03 +/* Force jump to location */ +#define KUE_QTBTYPE_JUMP 0x04 +/* Force call to location */ +#define KUE_QTBTYPE_CALL 0x05 +/* Force interrupt call */ +#define KUE_QTBTYPE_CALLINTR 0x06 +/* + * Cause data to be written using the specified QT engine + * interrupt, from starting location in memory for a specified + * number of bytes. + */ +#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07 +/* Cause data from stream to be written using specified QT interrupt. */ +#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08 +/* Cause data to be written to config locations. */ +/* Addresses assume 0xc000 offset. */ +#define KUE_QTBTYPE_WRITE_CONFIG 0x09 + +#define KUE_QTINTR_LOAD_CODE 0x64 +#define KUE_QTINTR_TRIGGER_CODE 0x3B +#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C + +/* Firmware code segment */ +static unsigned char kue_code_seg[] = +{ + /******************************************/ + /* NOTE: B6/C3 is data header signature */ + /* 0xAA/0xBB is data length = total */ + /* bytes - 7, 0xCC is type, 0xDD is */ + /* interrupt to use. */ + /******************************************/ + 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64, + 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00, + 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f, + 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0, + 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf, + 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09, + 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08, + 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08, + 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1, + 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00, + 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf, + 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09, + 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0, + 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08, + 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08, + 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57, + 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, + 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09, + 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59, + 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00, + 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1, + 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03, + 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1, + 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08, + 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04, + 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09, + 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00, + 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08, + 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08, + 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00, + 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, + 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf, + 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17, + 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08, + 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0, + 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37, + 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60, + 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9, + 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17, + 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00, + 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, + 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf, + 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf, + 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf, + 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57, + 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, + 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, + 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02, + 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1, + 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02, + 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07, + 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00, + 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77, + 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda, + 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, + 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf, + 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8, + 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77, + 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50, + 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00, + 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02, + 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0, + 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, + 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07, + 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, + 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, + 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07, + 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08, + 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09, + 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00, + 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf, + 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08, + 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05, + 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67, + 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0, + 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00, + 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08, + 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80, + 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08, + 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf, + 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0, + 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, + 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00, + 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17, + 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07, + 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03, + 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0, + 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07, + 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, + 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, + 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, + 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, + 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00, + 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, + 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52, + 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08, + 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17, + 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05, + 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04, + 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, + 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77, + 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9, + 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1, + 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00, + 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff, + 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17, + 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, + 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1, + 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59, + 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf, + 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07, + 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77, + 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00, + 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00, + 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00, + 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0, + 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77, + 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf, + 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1, + 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1, + 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf, + 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, + 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53, + 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8, + 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, + 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, + 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, + 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, + 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, + 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09, + 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0, + 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, + 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, + 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37, + 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08, + 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08, + 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, + 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00, + 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02, + 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, + 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00, + 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04, + 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09, + 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00, + 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05, + 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf, + 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0, + 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09, + 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00, + 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf, + 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57, + 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77, + 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00, + 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17, + 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1, + 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02, + 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77, + 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, + 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf, + 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57, + 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8, + 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08, + 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09, + 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77, + 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57, + 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19, + 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, + 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf, + 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b, + 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04, + 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf, + 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80, + 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e, + 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00, + 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09, + 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00, + 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07, + 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13, + 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08, + 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09, + 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09, + 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b, + 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00, + 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90, + 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b, + 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00, + 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07, + 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00, + 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00, + 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, + 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf, + 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08, + 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf, + 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94, + 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09, + 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf, + 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00, + 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00, + 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00, + 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00, + 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0, + 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0, + 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a, + 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a, + 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b, + 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02, + 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, + 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57, + 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37, + 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2, + 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00, + 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57, + 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b, + 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07, + 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08, + 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08, + 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0, + 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, + 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, + 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09, + 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08, + 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c, + 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf, + 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2, + 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08, + 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07, + 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb, + 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, + 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57, + 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07, + 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08, + 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07, + 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf, + 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00, + 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf, + 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1, + 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf, + 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf, + 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07, + 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02, + 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80, + 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, + 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08, + 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf, + 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07, + 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1, + 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00, + 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, + 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, + 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08, + 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, + 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08, + 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, + 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08, + 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, + 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, + 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00, + 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, + 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, + 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00, + 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08, + 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00, + 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00, + 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09, + 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07, + 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1, + 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, + 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07, + 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00, + 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00, + 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09, + 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02, + 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda, + 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08, + 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08, + 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02, + 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1, + 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08, + 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, + 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08, + 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08, + 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07, + 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00, + 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09, + 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00, + 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0, + 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf, + 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf, + 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00, + 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d, + 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1, + 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87, + 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08, + 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0, + 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0, + 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87, + 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, + 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, + 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, + 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09, + 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2, + 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, + 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08, + 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77, + 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf, + 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e, + 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0, + 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77, + 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57, + 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb, + 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00, + 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09, + 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08, + 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09, + 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02, + 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05, + 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1, + 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02, + 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, + 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, + 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf, + 0, 0 +}; + +/* Firmware fixup (data?) segment */ +static unsigned char kue_fix_seg[] = +{ + /******************************************/ + /* NOTE: B6/C3 is data header signature */ + /* 0xAA/0xBB is data length = total */ + /* bytes - 7, 0xCC is type, 0xDD is */ + /* interrupt to use. */ + /******************************************/ + 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64, + 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00, + 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00, + 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00, + 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00, + 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00, + 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00, + 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00, + 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00, + 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01, + 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01, + 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01, + 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01, + 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01, + 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01, + 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02, + 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02, + 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02, + 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02, + 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03, + 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03, + 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03, + 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03, + 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03, + 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03, + 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03, + 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04, + 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04, + 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04, + 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04, + 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04, + 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05, + 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05, + 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05, + 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05, + 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06, + 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06, + 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06, + 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06, + 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06, + 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06, + 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06, + 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07, + 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07, + 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08, + 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08, + 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08, + 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08, + 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09, + 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09, + 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09, + 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09, + 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09, + 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09, + 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09, + 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09, + 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a, + 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a, + 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a, + 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a, + 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a, + 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a, + 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a, + 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a, + 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a, + 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a, + 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b, + 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b, + 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b, + 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b, + 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b, + 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b, + 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c, + 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c, + 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c, + 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c, + 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c, + 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c, + 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c, + 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c, + 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d, + 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d, + 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d, + 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d, + 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d, + 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e, + 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e, + 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e, + 0, 0 +}; + +/* Fixup command. */ +#define KUE_TRIGCMD_OFFSET 5 +static unsigned char kue_trig_seg[] = { + 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; diff --git a/sys/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 <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the KLSI KL5KUSB101B USB to ethernet controller. + * The KLSI part is controlled via vendor control requests, the structure + * of which depend a bit on the firmware running on the internal + * microcontroller. The one exception is the 'send scan data' command, + * which is used to load the firmware. + */ + +#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00 +#define KUE_CMD_SET_MCAST_FILTERS 0x01 +#define KUE_CMD_SET_PKT_FILTER 0x02 +#define KUE_CMD_GET_ETHERSTATS 0x03 +#define KUE_CMD_GET_GPIO 0x04 +#define KUE_CMD_SET_GPIO 0x05 +#define KUE_CMD_SET_MAC 0x06 +#define KUE_CMD_GET_MAC 0x07 +#define KUE_CMD_SET_URB_SIZE 0x08 +#define KUE_CMD_SET_SOFS 0x09 +#define KUE_CMD_SET_EVEN_PKTS 0x0A +#define KUE_CMD_SEND_SCAN 0xFF + +struct kue_ether_desc { + 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 <akiyama@FreeBSD.org>. + * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul <wpaul@ee.columbia.edu>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * RealTek RTL8150 USB to fast ethernet controller driver. + * Datasheet is available from + * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. + */ + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR rue_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_ruereg.h> + +#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 <akiyama@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#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 <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) + * The spec can be found at the following url. + * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf + */ + +/* + * TODO: + * Interrupt Endpoint support + * External PHYs + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> + +#define USB_DEBUG_VAR udav_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_udavreg.h> + +/* 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 <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define UDAV_IFACE_INDEX 0 +#define UDAV_CONFIG_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 <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> +#include <dev/usb/usb_endian.h> +#include <dev/usb/usb.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/net/usb_ethernet.h> + +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, "<USB Ethernet> 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/limits.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/bpf.h> +#include <net/ethernet.h> + +#include "miibus_if.h" + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +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_ */ |