102ac6454SAndrew Thompson /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
402ac6454SAndrew Thompson * Copyright (c) 1997, 1998, 1999, 2000
502ac6454SAndrew Thompson * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
602ac6454SAndrew Thompson *
702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
902ac6454SAndrew Thompson * are met:
1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
1502ac6454SAndrew Thompson * 3. All advertising materials mentioning features or use of this software
1602ac6454SAndrew Thompson * must display the following acknowledgement:
1702ac6454SAndrew Thompson * This product includes software developed by Bill Paul.
1802ac6454SAndrew Thompson * 4. Neither the name of the author nor the names of any co-contributors
1902ac6454SAndrew Thompson * may be used to endorse or promote products derived from this software
2002ac6454SAndrew Thompson * without specific prior written permission.
2102ac6454SAndrew Thompson *
2202ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2302ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2402ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2502ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2602ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2702ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2802ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2902ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3002ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3102ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3202ac6454SAndrew Thompson * THE POSSIBILITY OF SUCH DAMAGE.
3302ac6454SAndrew Thompson */
3402ac6454SAndrew Thompson
3502ac6454SAndrew Thompson #include <sys/cdefs.h>
3602ac6454SAndrew Thompson /*
3702ac6454SAndrew Thompson * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate
3802ac6454SAndrew Thompson * adapters and others.
3902ac6454SAndrew Thompson *
4002ac6454SAndrew Thompson * Written by Bill Paul <wpaul@ee.columbia.edu>
4102ac6454SAndrew Thompson * Electrical Engineering Department
4202ac6454SAndrew Thompson * Columbia University, New York City
4302ac6454SAndrew Thompson */
4402ac6454SAndrew Thompson
4502ac6454SAndrew Thompson /*
4602ac6454SAndrew Thompson * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The
4702ac6454SAndrew Thompson * RX filter uses a 512-bit multicast hash table, single perfect entry
4802ac6454SAndrew Thompson * for the station address, and promiscuous mode. Unlike the ADMtek
4902ac6454SAndrew Thompson * and KLSI chips, the CATC ASIC supports read and write combining
5020733245SPedro F. Giffuni * mode where multiple packets can be transferred using a single bulk
5102ac6454SAndrew Thompson * transaction, which helps performance a great deal.
5202ac6454SAndrew Thompson */
5302ac6454SAndrew Thompson
54ed6d949aSAndrew Thompson #include <sys/stdint.h>
55ed6d949aSAndrew Thompson #include <sys/stddef.h>
56ed6d949aSAndrew Thompson #include <sys/param.h>
57ed6d949aSAndrew Thompson #include <sys/queue.h>
58ed6d949aSAndrew Thompson #include <sys/types.h>
59ed6d949aSAndrew Thompson #include <sys/systm.h>
6076039bc8SGleb Smirnoff #include <sys/socket.h>
61ed6d949aSAndrew Thompson #include <sys/kernel.h>
62ed6d949aSAndrew Thompson #include <sys/bus.h>
63ed6d949aSAndrew Thompson #include <sys/module.h>
64ed6d949aSAndrew Thompson #include <sys/lock.h>
65ed6d949aSAndrew Thompson #include <sys/mutex.h>
66ed6d949aSAndrew Thompson #include <sys/condvar.h>
67ed6d949aSAndrew Thompson #include <sys/sysctl.h>
68ed6d949aSAndrew Thompson #include <sys/sx.h>
69ed6d949aSAndrew Thompson #include <sys/unistd.h>
70ed6d949aSAndrew Thompson #include <sys/callout.h>
71ed6d949aSAndrew Thompson #include <sys/malloc.h>
72ed6d949aSAndrew Thompson #include <sys/priv.h>
73ed6d949aSAndrew Thompson
7476039bc8SGleb Smirnoff #include <net/if.h>
7576039bc8SGleb Smirnoff #include <net/if_var.h>
7676039bc8SGleb Smirnoff
7702ac6454SAndrew Thompson #include <dev/usb/usb.h>
78ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
79ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
80ed6d949aSAndrew Thompson #include "usbdevs.h"
8102ac6454SAndrew Thompson
8202ac6454SAndrew Thompson #define USB_DEBUG_VAR cue_debug
8302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
84ed6d949aSAndrew Thompson #include <dev/usb/usb_process.h>
8502ac6454SAndrew Thompson
8602ac6454SAndrew Thompson #include <dev/usb/net/usb_ethernet.h>
8702ac6454SAndrew Thompson #include <dev/usb/net/if_cuereg.h>
8802ac6454SAndrew Thompson
8902ac6454SAndrew Thompson /*
9002ac6454SAndrew Thompson * Various supported device vendors/products.
9102ac6454SAndrew Thompson */
9202ac6454SAndrew Thompson
9302ac6454SAndrew Thompson /* Belkin F5U111 adapter covered by NETMATE entry */
9402ac6454SAndrew Thompson
95f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID cue_devs[] = {
969e6b5313SAndrew Thompson #define CUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
979e6b5313SAndrew Thompson CUE_DEV(CATC, NETMATE),
989e6b5313SAndrew Thompson CUE_DEV(CATC, NETMATE2),
999e6b5313SAndrew Thompson CUE_DEV(SMARTBRIDGES, SMARTLINK),
1009e6b5313SAndrew Thompson #undef CUE_DEV
10102ac6454SAndrew Thompson };
10202ac6454SAndrew Thompson
10302ac6454SAndrew Thompson /* prototypes */
10402ac6454SAndrew Thompson
10502ac6454SAndrew Thompson static device_probe_t cue_probe;
10602ac6454SAndrew Thompson static device_attach_t cue_attach;
10702ac6454SAndrew Thompson static device_detach_t cue_detach;
10802ac6454SAndrew Thompson
109e0a69b51SAndrew Thompson static usb_callback_t cue_bulk_read_callback;
110e0a69b51SAndrew Thompson static usb_callback_t cue_bulk_write_callback;
11102ac6454SAndrew Thompson
112e0a69b51SAndrew Thompson static uether_fn_t cue_attach_post;
113e0a69b51SAndrew Thompson static uether_fn_t cue_init;
114e0a69b51SAndrew Thompson static uether_fn_t cue_stop;
115e0a69b51SAndrew Thompson static uether_fn_t cue_start;
116e0a69b51SAndrew Thompson static uether_fn_t cue_tick;
117e0a69b51SAndrew Thompson static uether_fn_t cue_setmulti;
118e0a69b51SAndrew Thompson static uether_fn_t cue_setpromisc;
11902ac6454SAndrew Thompson
12002ac6454SAndrew Thompson static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t);
12102ac6454SAndrew Thompson static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t);
12202ac6454SAndrew Thompson static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t);
12302ac6454SAndrew Thompson static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int);
12402ac6454SAndrew Thompson static int cue_getmac(struct cue_softc *, void *);
12502ac6454SAndrew Thompson static uint32_t cue_mchash(const uint8_t *);
12602ac6454SAndrew Thompson static void cue_reset(struct cue_softc *);
12702ac6454SAndrew Thompson
128b850ecc1SAndrew Thompson #ifdef USB_DEBUG
12902ac6454SAndrew Thompson static int cue_debug = 0;
13002ac6454SAndrew Thompson
131f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, cue, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
132f8d2b1f3SPawel Biernacki "USB cue");
133ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RWTUN, &cue_debug, 0,
13402ac6454SAndrew Thompson "Debug level");
13502ac6454SAndrew Thompson #endif
13602ac6454SAndrew Thompson
137760bc48eSAndrew Thompson static const struct usb_config cue_config[CUE_N_TRANSFER] = {
13802ac6454SAndrew Thompson [CUE_BULK_DT_WR] = {
13902ac6454SAndrew Thompson .type = UE_BULK,
14002ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY,
14102ac6454SAndrew Thompson .direction = UE_DIR_OUT,
1424eae601eSAndrew Thompson .bufsize = (MCLBYTES + 2),
1434eae601eSAndrew Thompson .flags = {.pipe_bof = 1,},
1444eae601eSAndrew Thompson .callback = cue_bulk_write_callback,
1454eae601eSAndrew Thompson .timeout = 10000, /* 10 seconds */
14602ac6454SAndrew Thompson },
14702ac6454SAndrew Thompson
14802ac6454SAndrew Thompson [CUE_BULK_DT_RD] = {
14902ac6454SAndrew Thompson .type = UE_BULK,
15002ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY,
15102ac6454SAndrew Thompson .direction = UE_DIR_IN,
1524eae601eSAndrew Thompson .bufsize = (MCLBYTES + 2),
1534eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
1544eae601eSAndrew Thompson .callback = cue_bulk_read_callback,
15502ac6454SAndrew Thompson },
15602ac6454SAndrew Thompson };
15702ac6454SAndrew Thompson
15802ac6454SAndrew Thompson static device_method_t cue_methods[] = {
15902ac6454SAndrew Thompson /* Device interface */
16002ac6454SAndrew Thompson DEVMETHOD(device_probe, cue_probe),
16102ac6454SAndrew Thompson DEVMETHOD(device_attach, cue_attach),
16202ac6454SAndrew Thompson DEVMETHOD(device_detach, cue_detach),
16302ac6454SAndrew Thompson
16461bfd867SSofian Brabez DEVMETHOD_END
16502ac6454SAndrew Thompson };
16602ac6454SAndrew Thompson
16702ac6454SAndrew Thompson static driver_t cue_driver = {
16802ac6454SAndrew Thompson .name = "cue",
16902ac6454SAndrew Thompson .methods = cue_methods,
17002ac6454SAndrew Thompson .size = sizeof(struct cue_softc),
17102ac6454SAndrew Thompson };
17202ac6454SAndrew Thompson
173bc9372d7SJohn Baldwin DRIVER_MODULE(cue, uhub, cue_driver, NULL, NULL);
17402ac6454SAndrew Thompson MODULE_DEPEND(cue, uether, 1, 1, 1);
17502ac6454SAndrew Thompson MODULE_DEPEND(cue, usb, 1, 1, 1);
17602ac6454SAndrew Thompson MODULE_DEPEND(cue, ether, 1, 1, 1);
177910cb8feSAndrew Thompson MODULE_VERSION(cue, 1);
178f809f280SWarner Losh USB_PNP_HOST_INFO(cue_devs);
17902ac6454SAndrew Thompson
180760bc48eSAndrew Thompson static const struct usb_ether_methods cue_ue_methods = {
18102ac6454SAndrew Thompson .ue_attach_post = cue_attach_post,
18202ac6454SAndrew Thompson .ue_start = cue_start,
18302ac6454SAndrew Thompson .ue_init = cue_init,
18402ac6454SAndrew Thompson .ue_stop = cue_stop,
18502ac6454SAndrew Thompson .ue_tick = cue_tick,
18602ac6454SAndrew Thompson .ue_setmulti = cue_setmulti,
18702ac6454SAndrew Thompson .ue_setpromisc = cue_setpromisc,
18802ac6454SAndrew Thompson };
18902ac6454SAndrew Thompson
19002ac6454SAndrew Thompson #define CUE_SETBIT(sc, reg, x) \
19102ac6454SAndrew Thompson cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x))
19202ac6454SAndrew Thompson
19302ac6454SAndrew Thompson #define CUE_CLRBIT(sc, reg, x) \
19402ac6454SAndrew Thompson cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x))
19502ac6454SAndrew Thompson
19602ac6454SAndrew Thompson static uint8_t
cue_csr_read_1(struct cue_softc * sc,uint16_t reg)19702ac6454SAndrew Thompson cue_csr_read_1(struct cue_softc *sc, uint16_t reg)
19802ac6454SAndrew Thompson {
199760bc48eSAndrew Thompson struct usb_device_request req;
20002ac6454SAndrew Thompson uint8_t val;
20102ac6454SAndrew Thompson
20202ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
20302ac6454SAndrew Thompson req.bRequest = CUE_CMD_READREG;
20402ac6454SAndrew Thompson USETW(req.wValue, 0);
20502ac6454SAndrew Thompson USETW(req.wIndex, reg);
20602ac6454SAndrew Thompson USETW(req.wLength, 1);
20702ac6454SAndrew Thompson
208a593f6b8SAndrew Thompson if (uether_do_request(&sc->sc_ue, &req, &val, 1000)) {
20902ac6454SAndrew Thompson /* ignore any errors */
21002ac6454SAndrew Thompson }
21102ac6454SAndrew Thompson return (val);
21202ac6454SAndrew Thompson }
21302ac6454SAndrew Thompson
21402ac6454SAndrew Thompson static uint16_t
cue_csr_read_2(struct cue_softc * sc,uint8_t reg)21502ac6454SAndrew Thompson cue_csr_read_2(struct cue_softc *sc, uint8_t reg)
21602ac6454SAndrew Thompson {
217760bc48eSAndrew Thompson struct usb_device_request req;
21802ac6454SAndrew Thompson uint16_t val;
21902ac6454SAndrew Thompson
22002ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
22102ac6454SAndrew Thompson req.bRequest = CUE_CMD_READREG;
22202ac6454SAndrew Thompson USETW(req.wValue, 0);
22302ac6454SAndrew Thompson USETW(req.wIndex, reg);
22402ac6454SAndrew Thompson USETW(req.wLength, 2);
22502ac6454SAndrew Thompson
226a593f6b8SAndrew Thompson (void)uether_do_request(&sc->sc_ue, &req, &val, 1000);
22702ac6454SAndrew Thompson return (le16toh(val));
22802ac6454SAndrew Thompson }
22902ac6454SAndrew Thompson
23002ac6454SAndrew Thompson static int
cue_csr_write_1(struct cue_softc * sc,uint16_t reg,uint16_t val)23102ac6454SAndrew Thompson cue_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val)
23202ac6454SAndrew Thompson {
233760bc48eSAndrew Thompson struct usb_device_request req;
23402ac6454SAndrew Thompson
23502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
23602ac6454SAndrew Thompson req.bRequest = CUE_CMD_WRITEREG;
23702ac6454SAndrew Thompson USETW(req.wValue, val);
23802ac6454SAndrew Thompson USETW(req.wIndex, reg);
23902ac6454SAndrew Thompson USETW(req.wLength, 0);
24002ac6454SAndrew Thompson
241a593f6b8SAndrew Thompson return (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
24202ac6454SAndrew Thompson }
24302ac6454SAndrew Thompson
24402ac6454SAndrew Thompson static int
cue_mem(struct cue_softc * sc,uint8_t cmd,uint16_t addr,void * buf,int len)24502ac6454SAndrew Thompson cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int len)
24602ac6454SAndrew Thompson {
247760bc48eSAndrew Thompson struct usb_device_request req;
24802ac6454SAndrew Thompson
24902ac6454SAndrew Thompson if (cmd == CUE_CMD_READSRAM)
25002ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
25102ac6454SAndrew Thompson else
25202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
25302ac6454SAndrew Thompson req.bRequest = cmd;
25402ac6454SAndrew Thompson USETW(req.wValue, 0);
25502ac6454SAndrew Thompson USETW(req.wIndex, addr);
25602ac6454SAndrew Thompson USETW(req.wLength, len);
25702ac6454SAndrew Thompson
258a593f6b8SAndrew Thompson return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
25902ac6454SAndrew Thompson }
26002ac6454SAndrew Thompson
26102ac6454SAndrew Thompson static int
cue_getmac(struct cue_softc * sc,void * buf)26202ac6454SAndrew Thompson cue_getmac(struct cue_softc *sc, void *buf)
26302ac6454SAndrew Thompson {
264760bc48eSAndrew Thompson struct usb_device_request req;
26502ac6454SAndrew Thompson
26602ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
26702ac6454SAndrew Thompson req.bRequest = CUE_CMD_GET_MACADDR;
26802ac6454SAndrew Thompson USETW(req.wValue, 0);
26902ac6454SAndrew Thompson USETW(req.wIndex, 0);
27002ac6454SAndrew Thompson USETW(req.wLength, ETHER_ADDR_LEN);
27102ac6454SAndrew Thompson
272a593f6b8SAndrew Thompson return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
27302ac6454SAndrew Thompson }
27402ac6454SAndrew Thompson
27502ac6454SAndrew Thompson #define CUE_BITS 9
27602ac6454SAndrew Thompson
27702ac6454SAndrew Thompson static uint32_t
cue_mchash(const uint8_t * addr)27802ac6454SAndrew Thompson cue_mchash(const uint8_t *addr)
27902ac6454SAndrew Thompson {
28002ac6454SAndrew Thompson uint32_t crc;
28102ac6454SAndrew Thompson
28202ac6454SAndrew Thompson /* Compute CRC for the address value. */
28302ac6454SAndrew Thompson crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
28402ac6454SAndrew Thompson
28502ac6454SAndrew Thompson return (crc & ((1 << CUE_BITS) - 1));
28602ac6454SAndrew Thompson }
28702ac6454SAndrew Thompson
28802ac6454SAndrew Thompson static void
cue_setpromisc(struct usb_ether * ue)289760bc48eSAndrew Thompson cue_setpromisc(struct usb_ether *ue)
29002ac6454SAndrew Thompson {
291a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
292*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
29302ac6454SAndrew Thompson
29402ac6454SAndrew Thompson CUE_LOCK_ASSERT(sc, MA_OWNED);
29502ac6454SAndrew Thompson
29602ac6454SAndrew Thompson /* if we want promiscuous mode, set the allframes bit */
297*935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_PROMISC)
29802ac6454SAndrew Thompson CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
29902ac6454SAndrew Thompson else
30002ac6454SAndrew Thompson CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
30102ac6454SAndrew Thompson
30202ac6454SAndrew Thompson /* write multicast hash-bits */
30302ac6454SAndrew Thompson cue_setmulti(ue);
30402ac6454SAndrew Thompson }
30502ac6454SAndrew Thompson
306c55e6e41SGleb Smirnoff static u_int
cue_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)307c55e6e41SGleb Smirnoff cue_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
308c55e6e41SGleb Smirnoff {
309c55e6e41SGleb Smirnoff uint8_t *hashtbl = arg;
310c55e6e41SGleb Smirnoff uint32_t h;
311c55e6e41SGleb Smirnoff
312c55e6e41SGleb Smirnoff h = cue_mchash(LLADDR(sdl));
313c55e6e41SGleb Smirnoff hashtbl[h >> 3] |= 1 << (h & 0x7);
314c55e6e41SGleb Smirnoff
315c55e6e41SGleb Smirnoff return (1);
316c55e6e41SGleb Smirnoff }
317c55e6e41SGleb Smirnoff
31802ac6454SAndrew Thompson static void
cue_setmulti(struct usb_ether * ue)319760bc48eSAndrew Thompson cue_setmulti(struct usb_ether *ue)
32002ac6454SAndrew Thompson {
321a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
322*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
323c55e6e41SGleb Smirnoff uint32_t h, i;
32402ac6454SAndrew Thompson uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
32502ac6454SAndrew Thompson
32602ac6454SAndrew Thompson CUE_LOCK_ASSERT(sc, MA_OWNED);
32702ac6454SAndrew Thompson
328*935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_ALLMULTI || if_getflags(ifp) & IFF_PROMISC) {
32902ac6454SAndrew Thompson for (i = 0; i < 8; i++)
33002ac6454SAndrew Thompson hashtbl[i] = 0xff;
33102ac6454SAndrew Thompson cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
33202ac6454SAndrew Thompson &hashtbl, 8);
33302ac6454SAndrew Thompson return;
33402ac6454SAndrew Thompson }
33502ac6454SAndrew Thompson
33602ac6454SAndrew Thompson /* now program new ones */
337c55e6e41SGleb Smirnoff if_foreach_llmaddr(ifp, cue_hash_maddr, hashtbl);
33802ac6454SAndrew Thompson
33902ac6454SAndrew Thompson /*
34002ac6454SAndrew Thompson * Also include the broadcast address in the filter
34102ac6454SAndrew Thompson * so we can receive broadcast frames.
34202ac6454SAndrew Thompson */
343*935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_BROADCAST) {
344*935b194dSJustin Hibbits h = cue_mchash(if_getbroadcastaddr(ifp));
34502ac6454SAndrew Thompson hashtbl[h >> 3] |= 1 << (h & 0x7);
34602ac6454SAndrew Thompson }
34702ac6454SAndrew Thompson
34802ac6454SAndrew Thompson cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8);
34902ac6454SAndrew Thompson }
35002ac6454SAndrew Thompson
35102ac6454SAndrew Thompson static void
cue_reset(struct cue_softc * sc)35202ac6454SAndrew Thompson cue_reset(struct cue_softc *sc)
35302ac6454SAndrew Thompson {
354760bc48eSAndrew Thompson struct usb_device_request req;
35502ac6454SAndrew Thompson
35602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
35702ac6454SAndrew Thompson req.bRequest = CUE_CMD_RESET;
35802ac6454SAndrew Thompson USETW(req.wValue, 0);
35902ac6454SAndrew Thompson USETW(req.wIndex, 0);
36002ac6454SAndrew Thompson USETW(req.wLength, 0);
36102ac6454SAndrew Thompson
362a593f6b8SAndrew Thompson if (uether_do_request(&sc->sc_ue, &req, NULL, 1000)) {
36302ac6454SAndrew Thompson /* ignore any errors */
36402ac6454SAndrew Thompson }
36502ac6454SAndrew Thompson
36602ac6454SAndrew Thompson /*
36702ac6454SAndrew Thompson * wait a little while for the chip to get its brains in order:
36802ac6454SAndrew Thompson */
369a593f6b8SAndrew Thompson uether_pause(&sc->sc_ue, hz / 100);
37002ac6454SAndrew Thompson }
37102ac6454SAndrew Thompson
37202ac6454SAndrew Thompson static void
cue_attach_post(struct usb_ether * ue)373760bc48eSAndrew Thompson cue_attach_post(struct usb_ether *ue)
37402ac6454SAndrew Thompson {
375a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
37602ac6454SAndrew Thompson
37702ac6454SAndrew Thompson cue_getmac(sc, ue->ue_eaddr);
37802ac6454SAndrew Thompson }
37902ac6454SAndrew Thompson
38002ac6454SAndrew Thompson static int
cue_probe(device_t dev)38102ac6454SAndrew Thompson cue_probe(device_t dev)
38202ac6454SAndrew Thompson {
383760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
38402ac6454SAndrew Thompson
385f29a0724SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST)
38602ac6454SAndrew Thompson return (ENXIO);
38702ac6454SAndrew Thompson if (uaa->info.bConfigIndex != CUE_CONFIG_IDX)
38802ac6454SAndrew Thompson return (ENXIO);
38902ac6454SAndrew Thompson if (uaa->info.bIfaceIndex != CUE_IFACE_IDX)
39002ac6454SAndrew Thompson return (ENXIO);
39102ac6454SAndrew Thompson
392a593f6b8SAndrew Thompson return (usbd_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa));
39302ac6454SAndrew Thompson }
39402ac6454SAndrew Thompson
39502ac6454SAndrew Thompson /*
39602ac6454SAndrew Thompson * Attach the interface. Allocate softc structures, do ifmedia
39702ac6454SAndrew Thompson * setup and ethernet/BPF attach.
39802ac6454SAndrew Thompson */
39902ac6454SAndrew Thompson static int
cue_attach(device_t dev)40002ac6454SAndrew Thompson cue_attach(device_t dev)
40102ac6454SAndrew Thompson {
402760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
40302ac6454SAndrew Thompson struct cue_softc *sc = device_get_softc(dev);
404760bc48eSAndrew Thompson struct usb_ether *ue = &sc->sc_ue;
40502ac6454SAndrew Thompson uint8_t iface_index;
40602ac6454SAndrew Thompson int error;
40702ac6454SAndrew Thompson
408a593f6b8SAndrew Thompson device_set_usb_desc(dev);
40902ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
41002ac6454SAndrew Thompson
41102ac6454SAndrew Thompson iface_index = CUE_IFACE_IDX;
412a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &iface_index,
41302ac6454SAndrew Thompson sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx);
41402ac6454SAndrew Thompson if (error) {
415767cb2e2SAndrew Thompson device_printf(dev, "allocating USB transfers failed\n");
41602ac6454SAndrew Thompson goto detach;
41702ac6454SAndrew Thompson }
41802ac6454SAndrew Thompson
41902ac6454SAndrew Thompson ue->ue_sc = sc;
42002ac6454SAndrew Thompson ue->ue_dev = dev;
42102ac6454SAndrew Thompson ue->ue_udev = uaa->device;
42202ac6454SAndrew Thompson ue->ue_mtx = &sc->sc_mtx;
42302ac6454SAndrew Thompson ue->ue_methods = &cue_ue_methods;
42402ac6454SAndrew Thompson
425a593f6b8SAndrew Thompson error = uether_ifattach(ue);
42602ac6454SAndrew Thompson if (error) {
42702ac6454SAndrew Thompson device_printf(dev, "could not attach interface\n");
42802ac6454SAndrew Thompson goto detach;
42902ac6454SAndrew Thompson }
43002ac6454SAndrew Thompson return (0); /* success */
43102ac6454SAndrew Thompson
43202ac6454SAndrew Thompson detach:
43302ac6454SAndrew Thompson cue_detach(dev);
43402ac6454SAndrew Thompson return (ENXIO); /* failure */
43502ac6454SAndrew Thompson }
43602ac6454SAndrew Thompson
43702ac6454SAndrew Thompson static int
cue_detach(device_t dev)43802ac6454SAndrew Thompson cue_detach(device_t dev)
43902ac6454SAndrew Thompson {
44002ac6454SAndrew Thompson struct cue_softc *sc = device_get_softc(dev);
441760bc48eSAndrew Thompson struct usb_ether *ue = &sc->sc_ue;
44202ac6454SAndrew Thompson
443a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER);
444a593f6b8SAndrew Thompson uether_ifdetach(ue);
44502ac6454SAndrew Thompson mtx_destroy(&sc->sc_mtx);
44602ac6454SAndrew Thompson
44702ac6454SAndrew Thompson return (0);
44802ac6454SAndrew Thompson }
44902ac6454SAndrew Thompson
45002ac6454SAndrew Thompson static void
cue_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)451ed6d949aSAndrew Thompson cue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
45202ac6454SAndrew Thompson {
453ed6d949aSAndrew Thompson struct cue_softc *sc = usbd_xfer_softc(xfer);
454760bc48eSAndrew Thompson struct usb_ether *ue = &sc->sc_ue;
455*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
456ed6d949aSAndrew Thompson struct usb_page_cache *pc;
45702ac6454SAndrew Thompson uint8_t buf[2];
45802ac6454SAndrew Thompson int len;
459ed6d949aSAndrew Thompson int actlen;
460ed6d949aSAndrew Thompson
461ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
46202ac6454SAndrew Thompson
46302ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
46402ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
46502ac6454SAndrew Thompson
4666d917491SHans Petter Selasky if (actlen <= (int)(2 + sizeof(struct ether_header))) {
467ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
46802ac6454SAndrew Thompson goto tr_setup;
46902ac6454SAndrew Thompson }
470ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
471ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, buf, 2);
472ed6d949aSAndrew Thompson actlen -= 2;
47302ac6454SAndrew Thompson len = buf[0] | (buf[1] << 8);
474ed6d949aSAndrew Thompson len = min(actlen, len);
47502ac6454SAndrew Thompson
476ed6d949aSAndrew Thompson uether_rxbuf(ue, pc, 2, len);
47702ac6454SAndrew Thompson /* FALLTHROUGH */
47802ac6454SAndrew Thompson case USB_ST_SETUP:
47902ac6454SAndrew Thompson tr_setup:
480ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
481a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
482a593f6b8SAndrew Thompson uether_rxflush(ue);
48302ac6454SAndrew Thompson return;
48402ac6454SAndrew Thompson
48502ac6454SAndrew Thompson default: /* Error */
48602ac6454SAndrew Thompson DPRINTF("bulk read error, %s\n",
487ed6d949aSAndrew Thompson usbd_errstr(error));
48802ac6454SAndrew Thompson
489ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
49002ac6454SAndrew Thompson /* try to clear stall first */
491ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
49202ac6454SAndrew Thompson goto tr_setup;
49302ac6454SAndrew Thompson }
49402ac6454SAndrew Thompson return;
49502ac6454SAndrew Thompson }
49602ac6454SAndrew Thompson }
49702ac6454SAndrew Thompson
49802ac6454SAndrew Thompson static void
cue_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)499ed6d949aSAndrew Thompson cue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
50002ac6454SAndrew Thompson {
501ed6d949aSAndrew Thompson struct cue_softc *sc = usbd_xfer_softc(xfer);
502*935b194dSJustin Hibbits if_t ifp = uether_getifp(&sc->sc_ue);
503ed6d949aSAndrew Thompson struct usb_page_cache *pc;
50402ac6454SAndrew Thompson struct mbuf *m;
50502ac6454SAndrew Thompson uint8_t buf[2];
50602ac6454SAndrew Thompson
50702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
50802ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
50902ac6454SAndrew Thompson DPRINTFN(11, "transfer complete\n");
510ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
51102ac6454SAndrew Thompson
51202ac6454SAndrew Thompson /* FALLTHROUGH */
51302ac6454SAndrew Thompson case USB_ST_SETUP:
51402ac6454SAndrew Thompson tr_setup:
515*935b194dSJustin Hibbits m = if_dequeue(ifp);
51602ac6454SAndrew Thompson
51702ac6454SAndrew Thompson if (m == NULL)
51802ac6454SAndrew Thompson return;
51902ac6454SAndrew Thompson if (m->m_pkthdr.len > MCLBYTES)
52002ac6454SAndrew Thompson m->m_pkthdr.len = MCLBYTES;
521ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, (m->m_pkthdr.len + 2));
52202ac6454SAndrew Thompson
52302ac6454SAndrew Thompson /* the first two bytes are the frame length */
52402ac6454SAndrew Thompson
52502ac6454SAndrew Thompson buf[0] = (uint8_t)(m->m_pkthdr.len);
52602ac6454SAndrew Thompson buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
52702ac6454SAndrew Thompson
528ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
529ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, buf, 2);
530ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len);
53102ac6454SAndrew Thompson
53202ac6454SAndrew Thompson /*
53302ac6454SAndrew Thompson * If there's a BPF listener, bounce a copy of this frame
53402ac6454SAndrew Thompson * to him.
53502ac6454SAndrew Thompson */
53602ac6454SAndrew Thompson BPF_MTAP(ifp, m);
53702ac6454SAndrew Thompson
53802ac6454SAndrew Thompson m_freem(m);
53902ac6454SAndrew Thompson
540a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
54102ac6454SAndrew Thompson
54202ac6454SAndrew Thompson return;
54302ac6454SAndrew Thompson
54402ac6454SAndrew Thompson default: /* Error */
54502ac6454SAndrew Thompson DPRINTFN(11, "transfer error, %s\n",
546ed6d949aSAndrew Thompson usbd_errstr(error));
54702ac6454SAndrew Thompson
548ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
54902ac6454SAndrew Thompson
550ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
55102ac6454SAndrew Thompson /* try to clear stall first */
552ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
55302ac6454SAndrew Thompson goto tr_setup;
55402ac6454SAndrew Thompson }
55502ac6454SAndrew Thompson return;
55602ac6454SAndrew Thompson }
55702ac6454SAndrew Thompson }
55802ac6454SAndrew Thompson
55902ac6454SAndrew Thompson static void
cue_tick(struct usb_ether * ue)560760bc48eSAndrew Thompson cue_tick(struct usb_ether *ue)
56102ac6454SAndrew Thompson {
562a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
563*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
56402ac6454SAndrew Thompson
56502ac6454SAndrew Thompson CUE_LOCK_ASSERT(sc, MA_OWNED);
56602ac6454SAndrew Thompson
567ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_SINGLECOLL));
568ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_MULTICOLL));
569ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_EXCESSCOLL));
57002ac6454SAndrew Thompson
57102ac6454SAndrew Thompson if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
572ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
57302ac6454SAndrew Thompson }
57402ac6454SAndrew Thompson
57502ac6454SAndrew Thompson static void
cue_start(struct usb_ether * ue)576760bc48eSAndrew Thompson cue_start(struct usb_ether *ue)
57702ac6454SAndrew Thompson {
578a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
57902ac6454SAndrew Thompson
58002ac6454SAndrew Thompson /*
58102ac6454SAndrew Thompson * start the USB transfers, if not already started:
58202ac6454SAndrew Thompson */
583a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]);
584a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]);
58502ac6454SAndrew Thompson }
58602ac6454SAndrew Thompson
58702ac6454SAndrew Thompson static void
cue_init(struct usb_ether * ue)588760bc48eSAndrew Thompson cue_init(struct usb_ether *ue)
58902ac6454SAndrew Thompson {
590a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
591*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
59202ac6454SAndrew Thompson int i;
59302ac6454SAndrew Thompson
59402ac6454SAndrew Thompson CUE_LOCK_ASSERT(sc, MA_OWNED);
59502ac6454SAndrew Thompson
59602ac6454SAndrew Thompson /*
59702ac6454SAndrew Thompson * Cancel pending I/O and free all RX/TX buffers.
59802ac6454SAndrew Thompson */
59902ac6454SAndrew Thompson cue_stop(ue);
60002ac6454SAndrew Thompson #if 0
60102ac6454SAndrew Thompson cue_reset(sc);
60202ac6454SAndrew Thompson #endif
60302ac6454SAndrew Thompson /* Set MAC address */
60402ac6454SAndrew Thompson for (i = 0; i < ETHER_ADDR_LEN; i++)
605*935b194dSJustin Hibbits cue_csr_write_1(sc, CUE_PAR0 - i, if_getlladdr(ifp)[i]);
60602ac6454SAndrew Thompson
60702ac6454SAndrew Thompson /* Enable RX logic. */
60802ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON);
60902ac6454SAndrew Thompson
61002ac6454SAndrew Thompson /* Load the multicast filter */
61102ac6454SAndrew Thompson cue_setpromisc(ue);
61202ac6454SAndrew Thompson
61302ac6454SAndrew Thompson /*
61402ac6454SAndrew Thompson * Set the number of RX and TX buffers that we want
61502ac6454SAndrew Thompson * to reserve inside the ASIC.
61602ac6454SAndrew Thompson */
61702ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES);
61802ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES);
61902ac6454SAndrew Thompson
62002ac6454SAndrew Thompson /* Set advanced operation modes. */
62102ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_ADVANCED_OPMODES,
62202ac6454SAndrew Thompson CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */
62302ac6454SAndrew Thompson
62402ac6454SAndrew Thompson /* Program the LED operation. */
62502ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
62602ac6454SAndrew Thompson
627ed6d949aSAndrew Thompson usbd_xfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]);
62802ac6454SAndrew Thompson
629*935b194dSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
63002ac6454SAndrew Thompson cue_start(ue);
63102ac6454SAndrew Thompson }
63202ac6454SAndrew Thompson
63302ac6454SAndrew Thompson /*
63402ac6454SAndrew Thompson * Stop the adapter and free any mbufs allocated to the
63502ac6454SAndrew Thompson * RX and TX lists.
63602ac6454SAndrew Thompson */
63702ac6454SAndrew Thompson static void
cue_stop(struct usb_ether * ue)638760bc48eSAndrew Thompson cue_stop(struct usb_ether *ue)
63902ac6454SAndrew Thompson {
640a593f6b8SAndrew Thompson struct cue_softc *sc = uether_getsc(ue);
641*935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
64202ac6454SAndrew Thompson
64302ac6454SAndrew Thompson CUE_LOCK_ASSERT(sc, MA_OWNED);
64402ac6454SAndrew Thompson
645*935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
64602ac6454SAndrew Thompson
64702ac6454SAndrew Thompson /*
64802ac6454SAndrew Thompson * stop all the transfers, if not already stopped:
64902ac6454SAndrew Thompson */
650a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]);
651a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]);
65202ac6454SAndrew Thompson
65302ac6454SAndrew Thompson cue_csr_write_1(sc, CUE_ETHCTL, 0);
65402ac6454SAndrew Thompson cue_reset(sc);
65502ac6454SAndrew Thompson }
656