1a17c678eSDavid Greenman /* 2a17c678eSDavid Greenman * Copyright (c) 1995, David Greenman 3a17c678eSDavid Greenman * All rights reserved. 4a17c678eSDavid Greenman * 5a17c678eSDavid Greenman * Redistribution and use in source and binary forms, with or without 6a17c678eSDavid Greenman * modification, are permitted provided that the following conditions 7a17c678eSDavid Greenman * are met: 8a17c678eSDavid Greenman * 1. Redistributions of source code must retain the above copyright 9a17c678eSDavid Greenman * notice unmodified, this list of conditions, and the following 10a17c678eSDavid Greenman * disclaimer. 11a17c678eSDavid Greenman * 2. Redistributions in binary form must reproduce the above copyright 12a17c678eSDavid Greenman * notice, this list of conditions and the following disclaimer in the 13a17c678eSDavid Greenman * documentation and/or other materials provided with the distribution. 14a17c678eSDavid Greenman * 15a17c678eSDavid Greenman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16a17c678eSDavid Greenman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17a17c678eSDavid Greenman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18a17c678eSDavid Greenman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19a17c678eSDavid Greenman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20a17c678eSDavid Greenman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21a17c678eSDavid Greenman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22a17c678eSDavid Greenman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23a17c678eSDavid Greenman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24a17c678eSDavid Greenman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25a17c678eSDavid Greenman * SUCH DAMAGE. 26a17c678eSDavid Greenman * 27dccee1a1SDavid Greenman * $Id: if_fxp.c,v 1.29 1997/02/22 09:44:05 peter Exp $ 28a17c678eSDavid Greenman */ 29a17c678eSDavid Greenman 30a17c678eSDavid Greenman /* 31ae12cddaSDavid Greenman * Intel EtherExpress Pro/100B PCI Fast Ethernet driver 32a17c678eSDavid Greenman */ 33a17c678eSDavid Greenman 34a17c678eSDavid Greenman #include "bpfilter.h" 35a17c678eSDavid Greenman 36a17c678eSDavid Greenman #include <sys/param.h> 37a17c678eSDavid Greenman #include <sys/systm.h> 38a17c678eSDavid Greenman #include <sys/ioctl.h> 39a17c678eSDavid Greenman #include <sys/mbuf.h> 40a17c678eSDavid Greenman #include <sys/malloc.h> 41a17c678eSDavid Greenman #include <sys/kernel.h> 424458ac71SBruce Evans #include <sys/socket.h> 43a17c678eSDavid Greenman #include <sys/syslog.h> 44a17c678eSDavid Greenman 45a17c678eSDavid Greenman #include <net/if.h> 46a17c678eSDavid Greenman #include <net/if_dl.h> 47a17c678eSDavid Greenman #include <net/if_types.h> 48a17c678eSDavid Greenman 49a17c678eSDavid Greenman #ifdef INET 50a17c678eSDavid Greenman #include <netinet/in.h> 51a17c678eSDavid Greenman #include <netinet/in_systm.h> 52a17c678eSDavid Greenman #include <netinet/in_var.h> 53a17c678eSDavid Greenman #include <netinet/ip.h> 54a17c678eSDavid Greenman #include <netinet/if_ether.h> 55a17c678eSDavid Greenman #endif 56a17c678eSDavid Greenman 57a17c678eSDavid Greenman #ifdef IPX 58a17c678eSDavid Greenman #include <netipx/ipx.h> 59a17c678eSDavid Greenman #include <netipx/ipx_if.h> 60a17c678eSDavid Greenman #endif 61a17c678eSDavid Greenman 62a17c678eSDavid Greenman #ifdef NS 63a17c678eSDavid Greenman #include <netns/ns.h> 64a17c678eSDavid Greenman #include <netns/ns_if.h> 65a17c678eSDavid Greenman #endif 66a17c678eSDavid Greenman 67a17c678eSDavid Greenman #if NBPFILTER > 0 68a17c678eSDavid Greenman #include <net/bpf.h> 69a17c678eSDavid Greenman #include <net/bpfdesc.h> 70a17c678eSDavid Greenman #endif 71a17c678eSDavid Greenman 72dfe61cf1SDavid Greenman #include <vm/vm.h> /* for vtophys */ 73dfe61cf1SDavid Greenman #include <vm/vm_param.h> /* for vtophys */ 74efeaf95aSDavid Greenman #include <vm/pmap.h> /* for vtophys */ 75dfe61cf1SDavid Greenman #include <machine/clock.h> /* for DELAY */ 76a17c678eSDavid Greenman 77a17c678eSDavid Greenman #include <pci/pcivar.h> 78a17c678eSDavid Greenman #include <pci/if_fxpreg.h> 79a17c678eSDavid Greenman 80a17c678eSDavid Greenman struct fxp_softc { 81dfe61cf1SDavid Greenman struct arpcom arpcom; /* per-interface network data */ 82dfe61cf1SDavid Greenman struct fxp_csr *csr; /* control/status registers */ 83a17c678eSDavid Greenman struct fxp_cb_tx *cbl_base; /* base of TxCB list */ 84a17c678eSDavid Greenman struct fxp_cb_tx *cbl_first; /* first active TxCB in list */ 85a17c678eSDavid Greenman struct fxp_cb_tx *cbl_last; /* last active TxCB in list */ 86a17c678eSDavid Greenman struct mbuf *rfa_headm; /* first mbuf in receive frame area */ 87a17c678eSDavid Greenman struct mbuf *rfa_tailm; /* last mbuf in receive frame area */ 88a17c678eSDavid Greenman struct fxp_stats *fxp_stats; /* Pointer to interface stats */ 89a17c678eSDavid Greenman int tx_queued; /* # of active TxCB's */ 90a17c678eSDavid Greenman int promisc_mode; /* promiscuous mode enabled */ 91dccee1a1SDavid Greenman int phy_primary_addr; /* address of primary PHY */ 92dccee1a1SDavid Greenman int phy_primary_device; /* device type of primary PHY */ 93dccee1a1SDavid Greenman int phy_10Mbps_only; /* PHY is 10Mbps-only device */ 94a17c678eSDavid Greenman }; 95a17c678eSDavid Greenman 96a17c678eSDavid Greenman static u_long fxp_count; 97a17c678eSDavid Greenman 98a17c678eSDavid Greenman /* 99a17c678eSDavid Greenman * Template for default configuration parameters. 100a17c678eSDavid Greenman * See struct fxp_cb_config for the bit definitions. 101a17c678eSDavid Greenman */ 102a17c678eSDavid Greenman static u_char fxp_cb_config_template[] = { 103a17c678eSDavid Greenman 0x0, 0x0, /* cb_status */ 104a17c678eSDavid Greenman 0x80, 0x2, /* cb_command */ 105a17c678eSDavid Greenman 0xff, 0xff, 0xff, 0xff, /* link_addr */ 106a17c678eSDavid Greenman 0x16, /* 0 */ 107a17c678eSDavid Greenman 0x8, /* 1 */ 108a17c678eSDavid Greenman 0x0, /* 2 */ 109a17c678eSDavid Greenman 0x0, /* 3 */ 110a17c678eSDavid Greenman 0x0, /* 4 */ 111a17c678eSDavid Greenman 0x80, /* 5 */ 112a17c678eSDavid Greenman 0xb2, /* 6 */ 113a17c678eSDavid Greenman 0x3, /* 7 */ 114a17c678eSDavid Greenman 0x1, /* 8 */ 115a17c678eSDavid Greenman 0x0, /* 9 */ 116a17c678eSDavid Greenman 0x26, /* 10 */ 117a17c678eSDavid Greenman 0x0, /* 11 */ 118a17c678eSDavid Greenman 0x60, /* 12 */ 119a17c678eSDavid Greenman 0x0, /* 13 */ 120a17c678eSDavid Greenman 0xf2, /* 14 */ 121a17c678eSDavid Greenman 0x48, /* 15 */ 122a17c678eSDavid Greenman 0x0, /* 16 */ 123a17c678eSDavid Greenman 0x40, /* 17 */ 124a17c678eSDavid Greenman 0xf3, /* 18 */ 125a17c678eSDavid Greenman 0x0, /* 19 */ 126a17c678eSDavid Greenman 0x3f, /* 20 */ 127a17c678eSDavid Greenman 0x5, /* 21 */ 128a17c678eSDavid Greenman 0x0, 0x0 129a17c678eSDavid Greenman }; 130a17c678eSDavid Greenman 1311cd443acSDavid Greenman static inline void fxp_scb_wait __P((struct fxp_csr *)); 132a17c678eSDavid Greenman static char *fxp_probe __P((pcici_t, pcidi_t)); 133a17c678eSDavid Greenman static void fxp_attach __P((pcici_t, int)); 134dd7610fcSStefan Eßer static void fxp_intr __P((void *)); 135a17c678eSDavid Greenman static void fxp_start __P((struct ifnet *)); 136a17c678eSDavid Greenman static int fxp_ioctl __P((struct ifnet *, int, caddr_t)); 137fb583156SDavid Greenman static void fxp_init __P((void *)); 1384a5f1499SDavid Greenman static void fxp_stop __P((struct fxp_softc *)); 1394a5f1499SDavid Greenman static void fxp_watchdog __P((struct ifnet *)); 140a17c678eSDavid Greenman static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); 1414a684684SDavid Greenman static void fxp_shutdown __P((int, void *)); 142dccee1a1SDavid Greenman static int fxp_mdi_read __P((struct fxp_csr *, int, int)); 143dccee1a1SDavid Greenman static void fxp_mdi_write __P((struct fxp_csr *, int, int, int)); 144dccee1a1SDavid Greenman static void fxp_read_eeprom __P((struct fxp_csr *, u_short *, int, int)); 145dccee1a1SDavid Greenman 146a17c678eSDavid Greenman 147a17c678eSDavid Greenman timeout_t fxp_stats_update; 148a17c678eSDavid Greenman 149a17c678eSDavid Greenman static struct pci_device fxp_device = { 150a17c678eSDavid Greenman "fxp", 151a17c678eSDavid Greenman fxp_probe, 152a17c678eSDavid Greenman fxp_attach, 153a17c678eSDavid Greenman &fxp_count, 1544a684684SDavid Greenman NULL 155a17c678eSDavid Greenman }; 156a17c678eSDavid Greenman DATA_SET(pcidevice_set, fxp_device); 157a17c678eSDavid Greenman 158a17c678eSDavid Greenman /* 159f9be9005SDavid Greenman * Set initial transmit threshold at 64 (512 bytes). This is 160f9be9005SDavid Greenman * increased by 64 (512 bytes) at a time, to maximum of 192 161f9be9005SDavid Greenman * (1536 bytes), if an underrun occurs. 162f9be9005SDavid Greenman */ 163f9be9005SDavid Greenman static int tx_threshold = 64; 164f9be9005SDavid Greenman 165f9be9005SDavid Greenman /* 166a17c678eSDavid Greenman * Number of transmit control blocks. This determines the number 167a17c678eSDavid Greenman * of transmit buffers that can be chained in the CB list. 168a17c678eSDavid Greenman * This must be a power of two. 169a17c678eSDavid Greenman */ 1701cd443acSDavid Greenman #define FXP_NTXCB 128 171a17c678eSDavid Greenman 172a17c678eSDavid Greenman /* 173a17c678eSDavid Greenman * TxCB list index mask. This is used to do list wrap-around. 174a17c678eSDavid Greenman */ 175a17c678eSDavid Greenman #define FXP_TXCB_MASK (FXP_NTXCB - 1) 176a17c678eSDavid Greenman 177a17c678eSDavid Greenman /* 178a17c678eSDavid Greenman * Number of DMA segments in a TxCB. Note that this is carefully 17923a0ed7cSDavid Greenman * chosen to make the total struct size an even power of two. It's 18023a0ed7cSDavid Greenman * critical that no TxCB be split across a page boundry since 18123a0ed7cSDavid Greenman * no attempt is made to allocate physically contiguous memory. 18223a0ed7cSDavid Greenman * 18323a0ed7cSDavid Greenman * XXX - don't forget to change the hard-coded constant in the 18423a0ed7cSDavid Greenman * fxp_cb_tx struct (defined in if_fxpreg.h), too! 185a17c678eSDavid Greenman */ 18623a0ed7cSDavid Greenman #define FXP_NTXSEG 29 187a17c678eSDavid Greenman 188a17c678eSDavid Greenman /* 189a17c678eSDavid Greenman * Number of receive frame area buffers. These are large so chose 190a17c678eSDavid Greenman * wisely. 191a17c678eSDavid Greenman */ 192a17c678eSDavid Greenman #define FXP_NRFABUFS 32 193a17c678eSDavid Greenman 194dfe61cf1SDavid Greenman /* 195dfe61cf1SDavid Greenman * Wait for the previous command to be accepted (but not necessarily 196dfe61cf1SDavid Greenman * completed). 197dfe61cf1SDavid Greenman */ 1981cd443acSDavid Greenman static inline void 199a17c678eSDavid Greenman fxp_scb_wait(csr) 200a17c678eSDavid Greenman struct fxp_csr *csr; 201a17c678eSDavid Greenman { 202a17c678eSDavid Greenman int i = 10000; 203a17c678eSDavid Greenman 204a17c678eSDavid Greenman while ((csr->scb_command & FXP_SCB_COMMAND_MASK) && --i); 205a17c678eSDavid Greenman } 206a17c678eSDavid Greenman 207dfe61cf1SDavid Greenman /* 208dfe61cf1SDavid Greenman * Return identification string if this is device is ours. 209dfe61cf1SDavid Greenman */ 210a17c678eSDavid Greenman static char * 211a17c678eSDavid Greenman fxp_probe(config_id, device_id) 212a17c678eSDavid Greenman pcici_t config_id; 213a17c678eSDavid Greenman pcidi_t device_id; 214a17c678eSDavid Greenman { 215a17c678eSDavid Greenman if (((device_id & 0xffff) == FXP_VENDORID_INTEL) && 216a17c678eSDavid Greenman ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557) 217dccee1a1SDavid Greenman return ("Intel EtherExpress Pro 10/100B Ethernet"); 218a17c678eSDavid Greenman 219a17c678eSDavid Greenman return NULL; 220a17c678eSDavid Greenman } 221a17c678eSDavid Greenman 222a17c678eSDavid Greenman /* 223a17c678eSDavid Greenman * Allocate data structures and attach the device. 224a17c678eSDavid Greenman */ 225a17c678eSDavid Greenman static void 226a17c678eSDavid Greenman fxp_attach(config_id, unit) 227a17c678eSDavid Greenman pcici_t config_id; 228a17c678eSDavid Greenman int unit; 229a17c678eSDavid Greenman { 230a17c678eSDavid Greenman struct fxp_softc *sc; 231a17c678eSDavid Greenman struct ifnet *ifp; 232a17c678eSDavid Greenman vm_offset_t pbase; 233a17c678eSDavid Greenman int s, i; 234dccee1a1SDavid Greenman u_short data; 235a17c678eSDavid Greenman 236a17c678eSDavid Greenman sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT); 237a17c678eSDavid Greenman if (sc == NULL) 238a17c678eSDavid Greenman return; 239a17c678eSDavid Greenman bzero(sc, sizeof(struct fxp_softc)); 240a17c678eSDavid Greenman 241a17c678eSDavid Greenman s = splimp(); 242a17c678eSDavid Greenman 243dfe61cf1SDavid Greenman /* 244dfe61cf1SDavid Greenman * Map control/status registers. 245dfe61cf1SDavid Greenman */ 246a17c678eSDavid Greenman if (!pci_map_mem(config_id, FXP_PCI_MMBA, 247a17c678eSDavid Greenman (vm_offset_t *)&sc->csr, &pbase)) { 248a17c678eSDavid Greenman printf("fxp%d: couldn't map memory\n", unit); 249a17c678eSDavid Greenman goto fail; 250a17c678eSDavid Greenman } 251a17c678eSDavid Greenman 252a17c678eSDavid Greenman /* 25333d14d86SDavid Greenman * Reset to a stable state. 254a17c678eSDavid Greenman */ 25533d14d86SDavid Greenman sc->csr->port = FXP_PORT_SELECTIVE_RESET; 256a17c678eSDavid Greenman DELAY(10); 257a17c678eSDavid Greenman 258dfe61cf1SDavid Greenman /* 259dfe61cf1SDavid Greenman * Allocate our interrupt. 260dfe61cf1SDavid Greenman */ 261a17c678eSDavid Greenman if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) { 262a17c678eSDavid Greenman printf("fxp%d: couldn't map interrupt\n", unit); 263a17c678eSDavid Greenman goto fail; 264a17c678eSDavid Greenman } 265a17c678eSDavid Greenman 266a17c678eSDavid Greenman sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, 267a17c678eSDavid Greenman M_DEVBUF, M_NOWAIT); 268a17c678eSDavid Greenman if (sc->cbl_base == NULL) 269a17c678eSDavid Greenman goto malloc_fail; 270a17c678eSDavid Greenman 271a17c678eSDavid Greenman sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); 272a17c678eSDavid Greenman if (sc->fxp_stats == NULL) 273a17c678eSDavid Greenman goto malloc_fail; 274a17c678eSDavid Greenman bzero(sc->fxp_stats, sizeof(struct fxp_stats)); 275a17c678eSDavid Greenman 276dfe61cf1SDavid Greenman /* 277dfe61cf1SDavid Greenman * Pre-allocate our receive buffers. 278dfe61cf1SDavid Greenman */ 279a17c678eSDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 280a17c678eSDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 281a17c678eSDavid Greenman goto malloc_fail; 282a17c678eSDavid Greenman } 283a17c678eSDavid Greenman } 284a17c678eSDavid Greenman 285dccee1a1SDavid Greenman /* 286dccee1a1SDavid Greenman * Get info about the primary PHY 287dccee1a1SDavid Greenman */ 288dccee1a1SDavid Greenman fxp_read_eeprom(sc->csr, (u_short *)&data, 6, 1); 289dccee1a1SDavid Greenman sc->phy_primary_addr = data & 0xff; 290dccee1a1SDavid Greenman sc->phy_primary_device = (data >> 8) & 0x3f; 291dccee1a1SDavid Greenman sc->phy_10Mbps_only = data >> 15; 292dccee1a1SDavid Greenman 293a17c678eSDavid Greenman ifp = &sc->arpcom.ac_if; 2949b44ff22SGarrett Wollman ifp->if_softc = sc; 295a17c678eSDavid Greenman ifp->if_unit = unit; 296a17c678eSDavid Greenman ifp->if_name = "fxp"; 297a17c678eSDavid Greenman ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 298a17c678eSDavid Greenman ifp->if_ioctl = fxp_ioctl; 299a17c678eSDavid Greenman ifp->if_output = ether_output; 300a17c678eSDavid Greenman ifp->if_start = fxp_start; 301a17c678eSDavid Greenman ifp->if_watchdog = fxp_watchdog; 302a330e1f1SGary Palmer ifp->if_baudrate = 100000000; 303fb583156SDavid Greenman ifp->if_init = fxp_init; 304a17c678eSDavid Greenman 305dccee1a1SDavid Greenman /* 306dccee1a1SDavid Greenman * Read MAC address 307dccee1a1SDavid Greenman */ 308dccee1a1SDavid Greenman fxp_read_eeprom(sc->csr, (u_short *)sc->arpcom.ac_enaddr, 0, 3); 309dccee1a1SDavid Greenman printf("fxp%d: Ethernet address %6D", unit, sc->arpcom.ac_enaddr, ":"); 310dccee1a1SDavid Greenman if (sc->phy_10Mbps_only) 311dccee1a1SDavid Greenman printf(", 10Mbps"); 312dccee1a1SDavid Greenman printf("\n"); 313a17c678eSDavid Greenman 314dfe61cf1SDavid Greenman /* 315dfe61cf1SDavid Greenman * Attach the interface. 316dfe61cf1SDavid Greenman */ 317a17c678eSDavid Greenman if_attach(ifp); 3189b44ff22SGarrett Wollman ether_ifattach(ifp); 3199b44ff22SGarrett Wollman 320a17c678eSDavid Greenman #if NBPFILTER > 0 3219b44ff22SGarrett Wollman bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 322a17c678eSDavid Greenman #endif 3234a684684SDavid Greenman 3244a684684SDavid Greenman /* 3254a684684SDavid Greenman * Add shutdown hook so that DMA is disabled prior to reboot. Not 3264a684684SDavid Greenman * doing do could allow DMA to corrupt kernel memory during the 3274a684684SDavid Greenman * reboot before the driver initializes. 3284a684684SDavid Greenman */ 3294a684684SDavid Greenman at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC); 3304a684684SDavid Greenman 331a17c678eSDavid Greenman splx(s); 332a17c678eSDavid Greenman return; 333a17c678eSDavid Greenman 334a17c678eSDavid Greenman malloc_fail: 335a17c678eSDavid Greenman printf("fxp%d: Failed to malloc memory\n", unit); 336a17c678eSDavid Greenman (void) pci_unmap_int(config_id); 337a17c678eSDavid Greenman if (sc && sc->cbl_base) 338a17c678eSDavid Greenman free(sc->cbl_base, M_DEVBUF); 339a17c678eSDavid Greenman if (sc && sc->fxp_stats) 340a17c678eSDavid Greenman free(sc->fxp_stats, M_DEVBUF); 341a17c678eSDavid Greenman /* frees entire chain */ 342a17c678eSDavid Greenman if (sc && sc->rfa_headm) 343a17c678eSDavid Greenman m_freem(sc->rfa_headm); 344a17c678eSDavid Greenman fail: 345a17c678eSDavid Greenman if (sc) 346a17c678eSDavid Greenman free(sc, M_DEVBUF); 347a17c678eSDavid Greenman splx(s); 348a17c678eSDavid Greenman } 349a17c678eSDavid Greenman 350a17c678eSDavid Greenman /* 351dccee1a1SDavid Greenman * Read from the serial EEPROM. Basically, you manually shift in 352dccee1a1SDavid Greenman * the read opcode (one bit at a time) and then shift in the address, 353dccee1a1SDavid Greenman * and then you shift out the data (all of this one bit at a time). 354dccee1a1SDavid Greenman * The word size is 16 bits, so you have to provide the address for 355dccee1a1SDavid Greenman * every 16 bits of data. 356a17c678eSDavid Greenman */ 357a17c678eSDavid Greenman static void 358dccee1a1SDavid Greenman fxp_read_eeprom(csr, data, offset, words) 359a17c678eSDavid Greenman struct fxp_csr *csr; 360dccee1a1SDavid Greenman u_short *data; 361dccee1a1SDavid Greenman int offset; 362dccee1a1SDavid Greenman int words; 363dccee1a1SDavid Greenman { 364dccee1a1SDavid Greenman u_short reg; 365a17c678eSDavid Greenman int i, x; 366a17c678eSDavid Greenman 367dccee1a1SDavid Greenman for (i = 0; i < words; i++) { 368a17c678eSDavid Greenman csr->eeprom_control = FXP_EEPROM_EECS; 369a17c678eSDavid Greenman /* 370a17c678eSDavid Greenman * Shift in read opcode. 371a17c678eSDavid Greenman */ 372a17c678eSDavid Greenman for (x = 3; x > 0; x--) { 373a17c678eSDavid Greenman if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { 374a17c678eSDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 375a17c678eSDavid Greenman } else { 376a17c678eSDavid Greenman reg = FXP_EEPROM_EECS; 377a17c678eSDavid Greenman } 378a17c678eSDavid Greenman csr->eeprom_control = reg; 379a17c678eSDavid Greenman csr->eeprom_control = reg | FXP_EEPROM_EESK; 380a17c678eSDavid Greenman DELAY(1); 381a17c678eSDavid Greenman csr->eeprom_control = reg; 382a17c678eSDavid Greenman DELAY(1); 383a17c678eSDavid Greenman } 384a17c678eSDavid Greenman /* 385a17c678eSDavid Greenman * Shift in address. 386a17c678eSDavid Greenman */ 387a17c678eSDavid Greenman for (x = 6; x > 0; x--) { 388dccee1a1SDavid Greenman if ((i + offset) & (1 << (x - 1))) { 389a17c678eSDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 390a17c678eSDavid Greenman } else { 391a17c678eSDavid Greenman reg = FXP_EEPROM_EECS; 392a17c678eSDavid Greenman } 393a17c678eSDavid Greenman csr->eeprom_control = reg; 394a17c678eSDavid Greenman csr->eeprom_control = reg | FXP_EEPROM_EESK; 395a17c678eSDavid Greenman DELAY(1); 396a17c678eSDavid Greenman csr->eeprom_control = reg; 397a17c678eSDavid Greenman DELAY(1); 398a17c678eSDavid Greenman } 399a17c678eSDavid Greenman reg = FXP_EEPROM_EECS; 400a17c678eSDavid Greenman data[i] = 0; 401a17c678eSDavid Greenman /* 402a17c678eSDavid Greenman * Shift out data. 403a17c678eSDavid Greenman */ 404a17c678eSDavid Greenman for (x = 16; x > 0; x--) { 405a17c678eSDavid Greenman csr->eeprom_control = reg | FXP_EEPROM_EESK; 406a17c678eSDavid Greenman DELAY(1); 407a17c678eSDavid Greenman if (csr->eeprom_control & FXP_EEPROM_EEDO) 408a17c678eSDavid Greenman data[i] |= (1 << (x - 1)); 409a17c678eSDavid Greenman csr->eeprom_control = reg; 410a17c678eSDavid Greenman DELAY(1); 411a17c678eSDavid Greenman } 412a17c678eSDavid Greenman csr->eeprom_control = 0; 413a17c678eSDavid Greenman DELAY(1); 414a17c678eSDavid Greenman } 415a17c678eSDavid Greenman } 416a17c678eSDavid Greenman 417a17c678eSDavid Greenman /* 4184a684684SDavid Greenman * Device shutdown routine. Called at system shutdown after sync. The 419a17c678eSDavid Greenman * main purpose of this routine is to shut off receiver DMA so that 420a17c678eSDavid Greenman * kernel memory doesn't get clobbered during warmboot. 421a17c678eSDavid Greenman */ 4224a684684SDavid Greenman static void 4234a684684SDavid Greenman fxp_shutdown(howto, sc) 4244a684684SDavid Greenman int howto; 4254a684684SDavid Greenman void *sc; 426a17c678eSDavid Greenman { 4274a684684SDavid Greenman fxp_stop((struct fxp_softc *) sc); 428a17c678eSDavid Greenman } 429a17c678eSDavid Greenman 430a17c678eSDavid Greenman /* 431a17c678eSDavid Greenman * Start packet transmission on the interface. 432a17c678eSDavid Greenman */ 433a17c678eSDavid Greenman static void 434a17c678eSDavid Greenman fxp_start(ifp) 435a17c678eSDavid Greenman struct ifnet *ifp; 436a17c678eSDavid Greenman { 4379b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 438a17c678eSDavid Greenman struct fxp_csr *csr = sc->csr; 439a17c678eSDavid Greenman struct fxp_cb_tx *txp; 440a17c678eSDavid Greenman struct mbuf *m, *mb_head; 44178fb85bbSDavid Greenman int segment, first = 1; 442a17c678eSDavid Greenman 443a17c678eSDavid Greenman txloop: 444a17c678eSDavid Greenman /* 4451cd443acSDavid Greenman * See if we're all filled up with buffers to transmit. 446a17c678eSDavid Greenman */ 4471cd443acSDavid Greenman if (sc->tx_queued >= FXP_NTXCB) 448a17c678eSDavid Greenman return; 4491cd443acSDavid Greenman 450dfe61cf1SDavid Greenman /* 451dfe61cf1SDavid Greenman * Grab a packet to transmit. 452dfe61cf1SDavid Greenman */ 453a17c678eSDavid Greenman IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, mb_head); 454a17c678eSDavid Greenman if (mb_head == NULL) { 455a17c678eSDavid Greenman /* 456a17c678eSDavid Greenman * No more packets to send. 457a17c678eSDavid Greenman */ 458a17c678eSDavid Greenman return; 459a17c678eSDavid Greenman } 460a17c678eSDavid Greenman 461dfe61cf1SDavid Greenman /* 462dfe61cf1SDavid Greenman * Get pointer to next available (unused) descriptor. 463dfe61cf1SDavid Greenman */ 464a17c678eSDavid Greenman txp = sc->cbl_last->next; 465a17c678eSDavid Greenman 466a17c678eSDavid Greenman /* 467a17c678eSDavid Greenman * Go through each of the mbufs in the chain and initialize 468a17c678eSDavid Greenman * the transmit buffers descriptors with the physical address 469a17c678eSDavid Greenman * and size of the mbuf. 470a17c678eSDavid Greenman */ 47123a0ed7cSDavid Greenman tbdinit: 472a17c678eSDavid Greenman for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { 473a17c678eSDavid Greenman if (m->m_len != 0) { 474a17c678eSDavid Greenman if (segment == FXP_NTXSEG) 475a17c678eSDavid Greenman break; 476a17c678eSDavid Greenman txp->tbd[segment].tb_addr = 477a17c678eSDavid Greenman vtophys(mtod(m, vm_offset_t)); 478a17c678eSDavid Greenman txp->tbd[segment].tb_size = m->m_len; 479a17c678eSDavid Greenman segment++; 480a17c678eSDavid Greenman } 481a17c678eSDavid Greenman } 482fb583156SDavid Greenman if (m != NULL) { 48323a0ed7cSDavid Greenman struct mbuf *mn; 48423a0ed7cSDavid Greenman 485a17c678eSDavid Greenman /* 486a17c678eSDavid Greenman * We ran out of segments. We have to recopy this mbuf 487a17c678eSDavid Greenman * chain first. 488a17c678eSDavid Greenman */ 48923a0ed7cSDavid Greenman MGETHDR(mn, M_DONTWAIT, MT_DATA); 49023a0ed7cSDavid Greenman if (mn == NULL) { 49123a0ed7cSDavid Greenman m_freem(mb_head); 49223a0ed7cSDavid Greenman return; 493a17c678eSDavid Greenman } 49423a0ed7cSDavid Greenman if (mb_head->m_pkthdr.len > MHLEN) { 49523a0ed7cSDavid Greenman MCLGET(mn, M_DONTWAIT); 49623a0ed7cSDavid Greenman if ((mn->m_flags & M_EXT) == 0) { 49723a0ed7cSDavid Greenman m_freem(mn); 49823a0ed7cSDavid Greenman m_freem(mb_head); 49923a0ed7cSDavid Greenman return; 50023a0ed7cSDavid Greenman } 50123a0ed7cSDavid Greenman } 50223a0ed7cSDavid Greenman m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t)); 50323a0ed7cSDavid Greenman mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; 50423a0ed7cSDavid Greenman m_freem(mb_head); 50523a0ed7cSDavid Greenman mb_head = mn; 50623a0ed7cSDavid Greenman goto tbdinit; 50723a0ed7cSDavid Greenman } 50823a0ed7cSDavid Greenman 50923a0ed7cSDavid Greenman txp->tbd_number = segment; 5101cd443acSDavid Greenman txp->mb_head = mb_head; 51123a0ed7cSDavid Greenman 512a17c678eSDavid Greenman /* 513a17c678eSDavid Greenman * Finish the initialization of this TxCB. 514a17c678eSDavid Greenman */ 515a17c678eSDavid Greenman txp->cb_status = 0; 516a17c678eSDavid Greenman txp->cb_command = 517a17c678eSDavid Greenman FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; 518f9be9005SDavid Greenman txp->tx_threshold = tx_threshold; 519a17c678eSDavid Greenman 520a17c678eSDavid Greenman /* 521a17c678eSDavid Greenman * Advance the end-of-list forward. 522a17c678eSDavid Greenman */ 523a17c678eSDavid Greenman sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; 524a17c678eSDavid Greenman sc->cbl_last = txp; 525a17c678eSDavid Greenman 526a17c678eSDavid Greenman /* 5271cd443acSDavid Greenman * Advance the beginning of the list forward if there are 5281cd443acSDavid Greenman * no other packets queued (when nothing is queued, cbl_first 5291cd443acSDavid Greenman * sits on the last TxCB that was sent out).. 530a17c678eSDavid Greenman */ 5311cd443acSDavid Greenman if (sc->tx_queued == 0) 532a17c678eSDavid Greenman sc->cbl_first = txp; 533a17c678eSDavid Greenman 5341cd443acSDavid Greenman sc->tx_queued++; 5351cd443acSDavid Greenman 53678fb85bbSDavid Greenman /* 53778fb85bbSDavid Greenman * Only need to wait prior to the first resume command. 53878fb85bbSDavid Greenman */ 53978fb85bbSDavid Greenman if (first) { 54078fb85bbSDavid Greenman first--; 5411cd443acSDavid Greenman fxp_scb_wait(csr); 54278fb85bbSDavid Greenman } 5433ba65732SDavid Greenman 544a17c678eSDavid Greenman /* 54578fb85bbSDavid Greenman * Resume transmission if suspended. 546a17c678eSDavid Greenman */ 547a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_RESUME; 548a17c678eSDavid Greenman 549a17c678eSDavid Greenman #if NBPFILTER > 0 550a17c678eSDavid Greenman /* 551a17c678eSDavid Greenman * Pass packet to bpf if there is a listener. 552a17c678eSDavid Greenman */ 553fb583156SDavid Greenman if (ifp->if_bpf) 5549b44ff22SGarrett Wollman bpf_mtap(ifp, mb_head); 555a17c678eSDavid Greenman #endif 556a17c678eSDavid Greenman /* 557a17c678eSDavid Greenman * Set a 5 second timer just in case we don't hear from the 558a17c678eSDavid Greenman * card again. 559a17c678eSDavid Greenman */ 560a17c678eSDavid Greenman ifp->if_timer = 5; 561a17c678eSDavid Greenman 562a17c678eSDavid Greenman goto txloop; 563a17c678eSDavid Greenman } 564a17c678eSDavid Greenman 565a17c678eSDavid Greenman /* 5669c7d2607SDavid Greenman * Process interface interrupts. 567a17c678eSDavid Greenman */ 568dd7610fcSStefan Eßer static void 569a17c678eSDavid Greenman fxp_intr(arg) 570a17c678eSDavid Greenman void *arg; 571a17c678eSDavid Greenman { 572a17c678eSDavid Greenman struct fxp_softc *sc = arg; 573a17c678eSDavid Greenman struct fxp_csr *csr = sc->csr; 574a17c678eSDavid Greenman struct ifnet *ifp = &sc->arpcom.ac_if; 5751cd443acSDavid Greenman u_int8_t statack; 576a17c678eSDavid Greenman 577a17c678eSDavid Greenman while ((statack = csr->scb_statack) != 0) { 578a17c678eSDavid Greenman /* 579a17c678eSDavid Greenman * First ACK all the interrupts in this pass. 580a17c678eSDavid Greenman */ 581a17c678eSDavid Greenman csr->scb_statack = statack; 582a17c678eSDavid Greenman 583a17c678eSDavid Greenman /* 584a17c678eSDavid Greenman * Free any finished transmit mbuf chains. 585a17c678eSDavid Greenman */ 586a17c678eSDavid Greenman if (statack & FXP_SCB_STATACK_CNA) { 587a17c678eSDavid Greenman struct fxp_cb_tx *txp; 588a17c678eSDavid Greenman 589a17c678eSDavid Greenman for (txp = sc->cbl_first; 5901cd443acSDavid Greenman (txp->cb_status & FXP_CB_STATUS_C) != 0; 591a17c678eSDavid Greenman txp = txp->next) { 5921cd443acSDavid Greenman if (txp->mb_head != NULL) { 593a17c678eSDavid Greenman m_freem(txp->mb_head); 594a17c678eSDavid Greenman txp->mb_head = NULL; 595a17c678eSDavid Greenman sc->tx_queued--; 596a17c678eSDavid Greenman } 5971cd443acSDavid Greenman if (txp->cb_command & FXP_CB_COMMAND_S) 5981cd443acSDavid Greenman break; 5991cd443acSDavid Greenman } 600a17c678eSDavid Greenman sc->cbl_first = txp; 601a17c678eSDavid Greenman /* 602a17c678eSDavid Greenman * Clear watchdog timer. It may or may not be set 603a17c678eSDavid Greenman * again in fxp_start(). 604a17c678eSDavid Greenman */ 605a17c678eSDavid Greenman ifp->if_timer = 0; 606a17c678eSDavid Greenman fxp_start(ifp); 607a17c678eSDavid Greenman } 608a17c678eSDavid Greenman /* 609a17c678eSDavid Greenman * Process receiver interrupts. If a no-resource (RNR) 610a17c678eSDavid Greenman * condition exists, get whatever packets we can and 611a17c678eSDavid Greenman * re-start the receiver. 612a17c678eSDavid Greenman */ 613a17c678eSDavid Greenman if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) { 614a17c678eSDavid Greenman struct mbuf *m; 615a17c678eSDavid Greenman struct fxp_rfa *rfa; 616a17c678eSDavid Greenman rcvloop: 617a17c678eSDavid Greenman m = sc->rfa_headm; 618dfe61cf1SDavid Greenman rfa = (struct fxp_rfa *)m->m_ext.ext_buf; 619a17c678eSDavid Greenman 620a17c678eSDavid Greenman if (rfa->rfa_status & FXP_RFA_STATUS_C) { 621dfe61cf1SDavid Greenman /* 622dfe61cf1SDavid Greenman * Remove first packet from the chain. 623dfe61cf1SDavid Greenman */ 624a17c678eSDavid Greenman sc->rfa_headm = m->m_next; 625a17c678eSDavid Greenman m->m_next = NULL; 626a17c678eSDavid Greenman 627dfe61cf1SDavid Greenman /* 628dfe61cf1SDavid Greenman * Add a new buffer to the receive chain. If this 629dfe61cf1SDavid Greenman * fails, the old buffer is recycled instead. 630dfe61cf1SDavid Greenman */ 631a17c678eSDavid Greenman if (fxp_add_rfabuf(sc, m) == 0) { 632a17c678eSDavid Greenman struct ether_header *eh; 633a17c678eSDavid Greenman u_short total_len; 634a17c678eSDavid Greenman 635a17c678eSDavid Greenman total_len = rfa->actual_size & (MCLBYTES - 1); 636a17c678eSDavid Greenman m->m_pkthdr.rcvif = ifp; 637a17c678eSDavid Greenman m->m_pkthdr.len = m->m_len = total_len - 638a17c678eSDavid Greenman sizeof(struct ether_header); 639a17c678eSDavid Greenman eh = mtod(m, struct ether_header *); 640a17c678eSDavid Greenman #if NBPFILTER > 0 641fb583156SDavid Greenman if (ifp->if_bpf) { 6429b44ff22SGarrett Wollman bpf_tap(ifp, mtod(m, caddr_t), total_len); 643a17c678eSDavid Greenman /* 644a17c678eSDavid Greenman * Only pass this packet up if it is for us. 645a17c678eSDavid Greenman */ 646a17c678eSDavid Greenman if ((ifp->if_flags & IFF_PROMISC) && 647a17c678eSDavid Greenman (rfa->rfa_status & FXP_RFA_STATUS_IAMATCH) && 648a17c678eSDavid Greenman (eh->ether_dhost[0] & 1) == 0) { 649a17c678eSDavid Greenman m_freem(m); 650a17c678eSDavid Greenman goto rcvloop; 651a17c678eSDavid Greenman } 652a17c678eSDavid Greenman } 653a17c678eSDavid Greenman #endif 654a17c678eSDavid Greenman m->m_data += sizeof(struct ether_header); 655a17c678eSDavid Greenman ether_input(ifp, eh, m); 656a17c678eSDavid Greenman } 657a17c678eSDavid Greenman goto rcvloop; 658a17c678eSDavid Greenman } 659a17c678eSDavid Greenman if (statack & FXP_SCB_STATACK_RNR) { 660a17c678eSDavid Greenman struct fxp_csr *csr = sc->csr; 661a17c678eSDavid Greenman 6621cd443acSDavid Greenman fxp_scb_wait(csr); 663dfe61cf1SDavid Greenman csr->scb_general = vtophys(sc->rfa_headm->m_ext.ext_buf); 664a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_RU_START; 665a17c678eSDavid Greenman } 666a17c678eSDavid Greenman } 667a17c678eSDavid Greenman } 668a17c678eSDavid Greenman } 669a17c678eSDavid Greenman 670dfe61cf1SDavid Greenman /* 671dfe61cf1SDavid Greenman * Update packet in/out/collision statistics. The i82557 doesn't 672dfe61cf1SDavid Greenman * allow you to access these counters without doing a fairly 673dfe61cf1SDavid Greenman * expensive DMA to get _all_ of the statistics it maintains, so 674dfe61cf1SDavid Greenman * we do this operation here only once per second. The statistics 675dfe61cf1SDavid Greenman * counters in the kernel are updated from the previous dump-stats 676dfe61cf1SDavid Greenman * DMA and then a new dump-stats DMA is started. The on-chip 677dfe61cf1SDavid Greenman * counters are zeroed when the DMA completes. If we can't start 678dfe61cf1SDavid Greenman * the DMA immediately, we don't wait - we just prepare to read 679dfe61cf1SDavid Greenman * them again next time. 680dfe61cf1SDavid Greenman */ 681a17c678eSDavid Greenman void 682a17c678eSDavid Greenman fxp_stats_update(arg) 683a17c678eSDavid Greenman void *arg; 684a17c678eSDavid Greenman { 685a17c678eSDavid Greenman struct fxp_softc *sc = arg; 686a17c678eSDavid Greenman struct ifnet *ifp = &sc->arpcom.ac_if; 687a17c678eSDavid Greenman struct fxp_stats *sp = sc->fxp_stats; 688a17c678eSDavid Greenman 689a17c678eSDavid Greenman ifp->if_opackets += sp->tx_good; 690a17c678eSDavid Greenman ifp->if_collisions += sp->tx_total_collisions; 691a17c678eSDavid Greenman ifp->if_ipackets += sp->rx_good; 6923ba65732SDavid Greenman ifp->if_ierrors += 6933ba65732SDavid Greenman sp->rx_crc_errors + 6943ba65732SDavid Greenman sp->rx_alignment_errors + 6953ba65732SDavid Greenman sp->rx_rnr_errors + 6966e39e599SDavid Greenman sp->rx_overrun_errors; 697a17c678eSDavid Greenman /* 698f9be9005SDavid Greenman * If any transmit underruns occured, bump up the transmit 699f9be9005SDavid Greenman * threshold by another 512 bytes (64 * 8). 700f9be9005SDavid Greenman */ 701f9be9005SDavid Greenman if (sp->tx_underruns) { 702f9be9005SDavid Greenman ifp->if_oerrors += sp->tx_underruns; 703f9be9005SDavid Greenman if (tx_threshold < 192) 704f9be9005SDavid Greenman tx_threshold += 64; 705f9be9005SDavid Greenman } 706f9be9005SDavid Greenman /* 7073ba65732SDavid Greenman * If there is no pending command, start another stats 7083ba65732SDavid Greenman * dump. Otherwise punt for now. 709a17c678eSDavid Greenman */ 710dfe61cf1SDavid Greenman if ((sc->csr->scb_command & FXP_SCB_COMMAND_MASK) == 0) { 711a17c678eSDavid Greenman /* 712dfe61cf1SDavid Greenman * Start another stats dump. By waiting for it to be 713dfe61cf1SDavid Greenman * accepted, we avoid having to do splhigh locking when 714dfe61cf1SDavid Greenman * writing scb_command in other parts of the driver. 715a17c678eSDavid Greenman */ 716a17c678eSDavid Greenman sc->csr->scb_command = FXP_SCB_COMMAND_CU_DUMPRESET; 7171cd443acSDavid Greenman fxp_scb_wait(sc->csr); 718dfe61cf1SDavid Greenman } else { 719dfe61cf1SDavid Greenman /* 720dfe61cf1SDavid Greenman * A previous command is still waiting to be accepted. 721dfe61cf1SDavid Greenman * Just zero our copy of the stats and wait for the 7223ba65732SDavid Greenman * next timer event to update them. 723dfe61cf1SDavid Greenman */ 724dfe61cf1SDavid Greenman sp->tx_good = 0; 725f9be9005SDavid Greenman sp->tx_underruns = 0; 726dfe61cf1SDavid Greenman sp->tx_total_collisions = 0; 7273ba65732SDavid Greenman 728dfe61cf1SDavid Greenman sp->rx_good = 0; 7293ba65732SDavid Greenman sp->rx_crc_errors = 0; 7303ba65732SDavid Greenman sp->rx_alignment_errors = 0; 7313ba65732SDavid Greenman sp->rx_rnr_errors = 0; 7323ba65732SDavid Greenman sp->rx_overrun_errors = 0; 733dfe61cf1SDavid Greenman } 734a17c678eSDavid Greenman /* 735a17c678eSDavid Greenman * Schedule another timeout one second from now. 736a17c678eSDavid Greenman */ 737a17c678eSDavid Greenman timeout(fxp_stats_update, sc, hz); 738a17c678eSDavid Greenman } 739a17c678eSDavid Greenman 740a17c678eSDavid Greenman /* 741a17c678eSDavid Greenman * Stop the interface. Cancels the statistics updater and resets 742a17c678eSDavid Greenman * the interface. 743a17c678eSDavid Greenman */ 744a17c678eSDavid Greenman static void 7454a5f1499SDavid Greenman fxp_stop(sc) 7464a5f1499SDavid Greenman struct fxp_softc *sc; 747a17c678eSDavid Greenman { 748a17c678eSDavid Greenman struct ifnet *ifp = &sc->arpcom.ac_if; 7493ba65732SDavid Greenman struct fxp_cb_tx *txp; 7503ba65732SDavid Greenman int i; 751a17c678eSDavid Greenman 752a17c678eSDavid Greenman /* 753a17c678eSDavid Greenman * Cancel stats updater. 754a17c678eSDavid Greenman */ 755a17c678eSDavid Greenman untimeout(fxp_stats_update, sc); 7563ba65732SDavid Greenman 7573ba65732SDavid Greenman /* 7583ba65732SDavid Greenman * Issue software reset 7593ba65732SDavid Greenman */ 76033d14d86SDavid Greenman sc->csr->port = FXP_PORT_SELECTIVE_RESET; 761a17c678eSDavid Greenman DELAY(10); 762a17c678eSDavid Greenman 7633ba65732SDavid Greenman /* 7643ba65732SDavid Greenman * Release any xmit buffers. 7653ba65732SDavid Greenman */ 7663ba65732SDavid Greenman for (txp = sc->cbl_first; txp != NULL && txp->mb_head != NULL; 7673ba65732SDavid Greenman txp = txp->next) { 7683ba65732SDavid Greenman m_freem(txp->mb_head); 7693ba65732SDavid Greenman txp->mb_head = NULL; 7703ba65732SDavid Greenman } 7713ba65732SDavid Greenman sc->tx_queued = 0; 7723ba65732SDavid Greenman 7733ba65732SDavid Greenman /* 7743ba65732SDavid Greenman * Free all the receive buffers then reallocate/reinitialize 7753ba65732SDavid Greenman */ 7763ba65732SDavid Greenman if (sc->rfa_headm != NULL) 7773ba65732SDavid Greenman m_freem(sc->rfa_headm); 7783ba65732SDavid Greenman sc->rfa_headm = NULL; 7793ba65732SDavid Greenman sc->rfa_tailm = NULL; 7803ba65732SDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 7813ba65732SDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 7823ba65732SDavid Greenman /* 7833ba65732SDavid Greenman * This "can't happen" - we're at splimp() 7843ba65732SDavid Greenman * and we just freed all the buffers we need 7853ba65732SDavid Greenman * above. 7863ba65732SDavid Greenman */ 7873ba65732SDavid Greenman panic("fxp_stop: no buffers!"); 7883ba65732SDavid Greenman } 7893ba65732SDavid Greenman } 7903ba65732SDavid Greenman 7913ba65732SDavid Greenman ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 7923ba65732SDavid Greenman ifp->if_timer = 0; 793a17c678eSDavid Greenman } 794a17c678eSDavid Greenman 795a17c678eSDavid Greenman /* 796a17c678eSDavid Greenman * Watchdog/transmission transmit timeout handler. Called when a 797a17c678eSDavid Greenman * transmission is started on the interface, but no interrupt is 798a17c678eSDavid Greenman * received before the timeout. This usually indicates that the 799a17c678eSDavid Greenman * card has wedged for some reason. 800a17c678eSDavid Greenman */ 801a17c678eSDavid Greenman static void 8024a5f1499SDavid Greenman fxp_watchdog(ifp) 8034a5f1499SDavid Greenman struct ifnet *ifp; 804a17c678eSDavid Greenman { 8054a5f1499SDavid Greenman log(LOG_ERR, "fxp%d: device timeout\n", ifp->if_unit); 8064a5f1499SDavid Greenman ifp->if_oerrors++; 807a17c678eSDavid Greenman 808fb583156SDavid Greenman fxp_init(ifp->if_softc); 809a17c678eSDavid Greenman } 810a17c678eSDavid Greenman 811a17c678eSDavid Greenman static void 812fb583156SDavid Greenman fxp_init(xsc) 813fb583156SDavid Greenman void *xsc; 814a17c678eSDavid Greenman { 815fb583156SDavid Greenman struct fxp_softc *sc = xsc; 816fb583156SDavid Greenman struct ifnet *ifp = &sc->arpcom.ac_if; 817a17c678eSDavid Greenman struct fxp_cb_config *cbp; 818a17c678eSDavid Greenman struct fxp_cb_ias *cb_ias; 819a17c678eSDavid Greenman struct fxp_cb_tx *txp; 820a17c678eSDavid Greenman struct fxp_csr *csr = sc->csr; 821a17c678eSDavid Greenman int i, s, mcast, prm; 822a17c678eSDavid Greenman 823a17c678eSDavid Greenman s = splimp(); 824a17c678eSDavid Greenman /* 8253ba65732SDavid Greenman * Cancel any pending I/O 826a17c678eSDavid Greenman */ 8273ba65732SDavid Greenman fxp_stop(sc); 828a17c678eSDavid Greenman 829a17c678eSDavid Greenman prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; 830a17c678eSDavid Greenman sc->promisc_mode = prm; 831a17c678eSDavid Greenman /* 832a17c678eSDavid Greenman * Sleeze out here and enable reception of all multicasts if 833a17c678eSDavid Greenman * multicasts are enabled. Ideally, we'd program the multicast 834a17c678eSDavid Greenman * address filter to only accept specific multicasts. 835a17c678eSDavid Greenman */ 836a17c678eSDavid Greenman mcast = (ifp->if_flags & (IFF_MULTICAST|IFF_ALLMULTI)) ? 1 : 0; 837a17c678eSDavid Greenman 838a17c678eSDavid Greenman /* 839a17c678eSDavid Greenman * Initialize base of CBL and RFA memory. Loading with zero 840a17c678eSDavid Greenman * sets it up for regular linear addressing. 841a17c678eSDavid Greenman */ 842a17c678eSDavid Greenman csr->scb_general = 0; 843a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_BASE; 844a17c678eSDavid Greenman 8451cd443acSDavid Greenman fxp_scb_wait(csr); 846a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_RU_BASE; 847a17c678eSDavid Greenman 848a17c678eSDavid Greenman /* 849a17c678eSDavid Greenman * Initialize base of dump-stats buffer. 850a17c678eSDavid Greenman */ 8511cd443acSDavid Greenman fxp_scb_wait(csr); 852a17c678eSDavid Greenman csr->scb_general = vtophys(sc->fxp_stats); 853a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_DUMP_ADR; 854a17c678eSDavid Greenman 855a17c678eSDavid Greenman /* 856a17c678eSDavid Greenman * We temporarily use memory that contains the TxCB list to 857a17c678eSDavid Greenman * construct the config CB. The TxCB list memory is rebuilt 858a17c678eSDavid Greenman * later. 859a17c678eSDavid Greenman */ 860a17c678eSDavid Greenman cbp = (struct fxp_cb_config *) sc->cbl_base; 861a17c678eSDavid Greenman 862a17c678eSDavid Greenman /* 863a17c678eSDavid Greenman * This bcopy is kind of disgusting, but there are a bunch of must be 864a17c678eSDavid Greenman * zero and must be one bits in this structure and this is the easiest 865a17c678eSDavid Greenman * way to initialize them all to proper values. 866a17c678eSDavid Greenman */ 867a17c678eSDavid Greenman bcopy(fxp_cb_config_template, cbp, sizeof(struct fxp_cb_config)); 868a17c678eSDavid Greenman 869a17c678eSDavid Greenman cbp->cb_status = 0; 870a17c678eSDavid Greenman cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; 871a17c678eSDavid Greenman cbp->link_addr = -1; /* (no) next command */ 872a17c678eSDavid Greenman cbp->byte_count = 22; /* (22) bytes to config */ 873001696daSDavid Greenman cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ 874001696daSDavid Greenman cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ 875a17c678eSDavid Greenman cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ 876001696daSDavid Greenman cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ 877001696daSDavid Greenman cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ 878001696daSDavid Greenman cbp->dma_bce = 0; /* (disable) dma max counters */ 879a17c678eSDavid Greenman cbp->late_scb = 0; /* (don't) defer SCB update */ 880a17c678eSDavid Greenman cbp->tno_int = 0; /* (disable) tx not okay interrupt */ 881001696daSDavid Greenman cbp->ci_int = 0; /* interrupt on CU not active */ 882a17c678eSDavid Greenman cbp->save_bf = prm; /* save bad frames */ 883a17c678eSDavid Greenman cbp->disc_short_rx = !prm; /* discard short packets */ 884a17c678eSDavid Greenman cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ 885dccee1a1SDavid Greenman cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */ 886a17c678eSDavid Greenman cbp->nsai = 1; /* (don't) disable source addr insert */ 887a17c678eSDavid Greenman cbp->preamble_length = 2; /* (7 byte) preamble */ 888a17c678eSDavid Greenman cbp->loopback = 0; /* (don't) loopback */ 889a17c678eSDavid Greenman cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ 890a17c678eSDavid Greenman cbp->linear_pri_mode = 0; /* (wait after xmit only) */ 891a17c678eSDavid Greenman cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ 892a17c678eSDavid Greenman cbp->promiscuous = prm; /* promiscuous mode */ 893a17c678eSDavid Greenman cbp->bcast_disable = 0; /* (don't) disable broadcasts */ 894001696daSDavid Greenman cbp->crscdt = 0; /* (CRS only) */ 895a17c678eSDavid Greenman cbp->stripping = !prm; /* truncate rx packet to byte count */ 896a17c678eSDavid Greenman cbp->padding = 1; /* (do) pad short tx packets */ 897a17c678eSDavid Greenman cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ 898a17c678eSDavid Greenman cbp->force_fdx = 0; /* (don't) force full duplex */ 8993ba65732SDavid Greenman cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ 900a17c678eSDavid Greenman cbp->multi_ia = 0; /* (don't) accept multiple IAs */ 901a17c678eSDavid Greenman cbp->mc_all = mcast; /* accept all multicasts */ 902a17c678eSDavid Greenman 903a17c678eSDavid Greenman /* 904a17c678eSDavid Greenman * Start the config command/DMA. 905a17c678eSDavid Greenman */ 9061cd443acSDavid Greenman fxp_scb_wait(csr); 907a17c678eSDavid Greenman csr->scb_general = vtophys(cbp); 908a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_START; 909a17c678eSDavid Greenman /* ...and wait for it to complete. */ 910a17c678eSDavid Greenman while (!(cbp->cb_status & FXP_CB_STATUS_C)); 911a17c678eSDavid Greenman 912a17c678eSDavid Greenman /* 913a17c678eSDavid Greenman * Now initialize the station address. Temporarily use the TxCB 914a17c678eSDavid Greenman * memory area like we did above for the config CB. 915a17c678eSDavid Greenman */ 916a17c678eSDavid Greenman cb_ias = (struct fxp_cb_ias *) sc->cbl_base; 917a17c678eSDavid Greenman cb_ias->cb_status = 0; 918a17c678eSDavid Greenman cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; 919a17c678eSDavid Greenman cb_ias->link_addr = -1; 920a17c678eSDavid Greenman bcopy(sc->arpcom.ac_enaddr, (void *)cb_ias->macaddr, 921a17c678eSDavid Greenman sizeof(sc->arpcom.ac_enaddr)); 922a17c678eSDavid Greenman 923a17c678eSDavid Greenman /* 924a17c678eSDavid Greenman * Start the IAS (Individual Address Setup) command/DMA. 925a17c678eSDavid Greenman */ 9261cd443acSDavid Greenman fxp_scb_wait(csr); 927a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_START; 928a17c678eSDavid Greenman /* ...and wait for it to complete. */ 929a17c678eSDavid Greenman while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); 930a17c678eSDavid Greenman 931a17c678eSDavid Greenman /* 932a17c678eSDavid Greenman * Initialize transmit control block (TxCB) list. 933a17c678eSDavid Greenman */ 934a17c678eSDavid Greenman 935a17c678eSDavid Greenman txp = sc->cbl_base; 936a17c678eSDavid Greenman bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB); 937a17c678eSDavid Greenman for (i = 0; i < FXP_NTXCB; i++) { 938a17c678eSDavid Greenman txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; 939a17c678eSDavid Greenman txp[i].cb_command = FXP_CB_COMMAND_NOP; 940a17c678eSDavid Greenman txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK]); 941a17c678eSDavid Greenman txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]); 942a17c678eSDavid Greenman txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK]; 943a17c678eSDavid Greenman } 944a17c678eSDavid Greenman /* 945a17c678eSDavid Greenman * Set the stop flag on the first TxCB and start the control 946a17c678eSDavid Greenman * unit. It will execute the NOP and then suspend. 947a17c678eSDavid Greenman */ 948a17c678eSDavid Greenman txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S; 949a17c678eSDavid Greenman sc->cbl_first = sc->cbl_last = txp; 950a17c678eSDavid Greenman sc->tx_queued = 0; 951a17c678eSDavid Greenman 9521cd443acSDavid Greenman fxp_scb_wait(csr); 953a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_CU_START; 954a17c678eSDavid Greenman 955a17c678eSDavid Greenman /* 956a17c678eSDavid Greenman * Initialize receiver buffer area - RFA. 957a17c678eSDavid Greenman */ 9581cd443acSDavid Greenman fxp_scb_wait(csr); 959dfe61cf1SDavid Greenman csr->scb_general = vtophys(sc->rfa_headm->m_ext.ext_buf); 960a17c678eSDavid Greenman csr->scb_command = FXP_SCB_COMMAND_RU_START; 961a17c678eSDavid Greenman 962dccee1a1SDavid Greenman /* 963dccee1a1SDavid Greenman * Toggle a few bits in the DP83840 PHY. 964dccee1a1SDavid Greenman */ 965dccee1a1SDavid Greenman if (sc->phy_primary_device == FXP_PHY_DP83840) { 966dccee1a1SDavid Greenman fxp_mdi_write(sc->csr, sc->phy_primary_addr, FXP_DP83840_PCR, 967dccee1a1SDavid Greenman fxp_mdi_read(sc->csr, sc->phy_primary_addr, FXP_DP83840_PCR) | 968dccee1a1SDavid Greenman FXP_DP83840_PCR_LED4_MODE | /* LED4 always indicates duplex */ 969dccee1a1SDavid Greenman FXP_DP83840_PCR_F_CONNECT | /* force link disconnect bypass */ 970dccee1a1SDavid Greenman FXP_DP83840_PCR_BIT10); /* XXX I have no idea */ 971dccee1a1SDavid Greenman } 972dccee1a1SDavid Greenman 973a17c678eSDavid Greenman ifp->if_flags |= IFF_RUNNING; 974a17c678eSDavid Greenman ifp->if_flags &= ~IFF_OACTIVE; 975a17c678eSDavid Greenman splx(s); 976a17c678eSDavid Greenman 977a17c678eSDavid Greenman /* 978a17c678eSDavid Greenman * Start stats updater. 979a17c678eSDavid Greenman */ 980a17c678eSDavid Greenman timeout(fxp_stats_update, sc, hz); 981a17c678eSDavid Greenman } 982a17c678eSDavid Greenman 983a17c678eSDavid Greenman /* 984a17c678eSDavid Greenman * Add a buffer to the end of the RFA buffer list. 985a17c678eSDavid Greenman * Return 0 if successful, 1 for failure. A failure results in 986a17c678eSDavid Greenman * adding the 'oldm' (if non-NULL) on to the end of the list - 987a17c678eSDavid Greenman * tossing out it's old contents and recycling it. 988a17c678eSDavid Greenman * The RFA struct is stuck at the beginning of mbuf cluster and the 989a17c678eSDavid Greenman * data pointer is fixed up to point just past it. 990a17c678eSDavid Greenman */ 991a17c678eSDavid Greenman static int 992a17c678eSDavid Greenman fxp_add_rfabuf(sc, oldm) 993a17c678eSDavid Greenman struct fxp_softc *sc; 994a17c678eSDavid Greenman struct mbuf *oldm; 995a17c678eSDavid Greenman { 996a17c678eSDavid Greenman struct mbuf *m; 997a17c678eSDavid Greenman struct fxp_rfa *rfa, *p_rfa; 998a17c678eSDavid Greenman 999a17c678eSDavid Greenman MGETHDR(m, M_DONTWAIT, MT_DATA); 1000a17c678eSDavid Greenman if (m != NULL) { 1001a17c678eSDavid Greenman MCLGET(m, M_DONTWAIT); 1002a17c678eSDavid Greenman if ((m->m_flags & M_EXT) == 0) { 1003a17c678eSDavid Greenman m_freem(m); 1004eadd5e3aSDavid Greenman if (oldm == NULL) 1005eadd5e3aSDavid Greenman return 1; 1006a17c678eSDavid Greenman m = oldm; 1007eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1008a17c678eSDavid Greenman } 1009a17c678eSDavid Greenman } else { 1010eadd5e3aSDavid Greenman if (oldm == NULL) 1011a17c678eSDavid Greenman return 1; 1012eadd5e3aSDavid Greenman m = oldm; 1013eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1014eadd5e3aSDavid Greenman } 1015eadd5e3aSDavid Greenman /* 1016eadd5e3aSDavid Greenman * Get a pointer to the base of the mbuf cluster and move 1017eadd5e3aSDavid Greenman * data start past it. 1018eadd5e3aSDavid Greenman */ 1019a17c678eSDavid Greenman rfa = mtod(m, struct fxp_rfa *); 1020eadd5e3aSDavid Greenman m->m_data += sizeof(struct fxp_rfa); 1021eadd5e3aSDavid Greenman rfa->size = MCLBYTES - sizeof(struct fxp_rfa); 1022eadd5e3aSDavid Greenman 1023a17c678eSDavid Greenman rfa->rfa_status = 0; 1024a17c678eSDavid Greenman rfa->rfa_control = FXP_RFA_CONTROL_EL; 1025a17c678eSDavid Greenman rfa->link_addr = -1; 1026a17c678eSDavid Greenman rfa->rbd_addr = -1; 1027a17c678eSDavid Greenman rfa->actual_size = 0; 1028dfe61cf1SDavid Greenman /* 1029dfe61cf1SDavid Greenman * If there are other buffers already on the list, attach this 1030dfe61cf1SDavid Greenman * one to the end by fixing up the tail to point to this one. 1031dfe61cf1SDavid Greenman */ 1032a17c678eSDavid Greenman if (sc->rfa_headm != NULL) { 1033dfe61cf1SDavid Greenman p_rfa = (struct fxp_rfa *) sc->rfa_tailm->m_ext.ext_buf; 1034a17c678eSDavid Greenman sc->rfa_tailm->m_next = m; 1035a17c678eSDavid Greenman p_rfa->link_addr = vtophys(rfa); 1036a17c678eSDavid Greenman p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL; 1037a17c678eSDavid Greenman } else { 1038a17c678eSDavid Greenman sc->rfa_headm = m; 1039a17c678eSDavid Greenman } 1040a17c678eSDavid Greenman sc->rfa_tailm = m; 1041a17c678eSDavid Greenman 1042dfe61cf1SDavid Greenman return (m == oldm); 1043a17c678eSDavid Greenman } 1044a17c678eSDavid Greenman 1045a17c678eSDavid Greenman static int 1046dccee1a1SDavid Greenman fxp_mdi_read(csr, phy, reg) 1047dccee1a1SDavid Greenman struct fxp_csr *csr; 1048dccee1a1SDavid Greenman int phy; 1049dccee1a1SDavid Greenman int reg; 1050dccee1a1SDavid Greenman { 1051dccee1a1SDavid Greenman int count = 10000; 1052dccee1a1SDavid Greenman 1053dccee1a1SDavid Greenman csr->mdi_control = (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21); 1054dccee1a1SDavid Greenman 1055dccee1a1SDavid Greenman while ((csr->mdi_control & 0x10000000) == 0 && count--) 1056dccee1a1SDavid Greenman DELAY(1); 1057dccee1a1SDavid Greenman 1058dccee1a1SDavid Greenman if (count <= 0) 1059dccee1a1SDavid Greenman printf("fxp_mdi_read: timed out\n"); 1060dccee1a1SDavid Greenman 1061dccee1a1SDavid Greenman return (csr->mdi_control & 0xffff); 1062dccee1a1SDavid Greenman } 1063dccee1a1SDavid Greenman 1064dccee1a1SDavid Greenman static void 1065dccee1a1SDavid Greenman fxp_mdi_write(csr, phy, reg, value) 1066dccee1a1SDavid Greenman struct fxp_csr *csr; 1067dccee1a1SDavid Greenman int phy; 1068dccee1a1SDavid Greenman int reg; 1069dccee1a1SDavid Greenman int value; 1070dccee1a1SDavid Greenman { 1071dccee1a1SDavid Greenman int count = 10000; 1072dccee1a1SDavid Greenman 1073dccee1a1SDavid Greenman csr->mdi_control = (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) 1074dccee1a1SDavid Greenman | (value & 0xffff); 1075dccee1a1SDavid Greenman 1076dccee1a1SDavid Greenman while ((csr->mdi_control & 10000000) == 0 && count--) 1077dccee1a1SDavid Greenman DELAY(1); 1078dccee1a1SDavid Greenman 1079dccee1a1SDavid Greenman if (count <= 0) 1080dccee1a1SDavid Greenman printf("fxp_mdi_write: timed out\n"); 1081dccee1a1SDavid Greenman } 1082dccee1a1SDavid Greenman 1083dccee1a1SDavid Greenman static int 1084a17c678eSDavid Greenman fxp_ioctl(ifp, command, data) 1085a17c678eSDavid Greenman struct ifnet *ifp; 1086a17c678eSDavid Greenman int command; 1087a17c678eSDavid Greenman caddr_t data; 1088a17c678eSDavid Greenman { 1089a17c678eSDavid Greenman struct ifaddr *ifa = (struct ifaddr *) data; 10909b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 1091a17c678eSDavid Greenman struct ifreq *ifr = (struct ifreq *) data; 1092a17c678eSDavid Greenman int s, error = 0; 1093a17c678eSDavid Greenman 1094a17c678eSDavid Greenman s = splimp(); 1095a17c678eSDavid Greenman 1096a17c678eSDavid Greenman switch (command) { 1097a17c678eSDavid Greenman 1098a17c678eSDavid Greenman case SIOCSIFADDR: 1099a17c678eSDavid Greenman case SIOCGIFADDR: 1100fb583156SDavid Greenman case SIOCSIFMTU: 1101fb583156SDavid Greenman error = ether_ioctl(ifp, command, data); 1102a17c678eSDavid Greenman break; 1103a17c678eSDavid Greenman 1104a17c678eSDavid Greenman case SIOCSIFFLAGS: 1105a17c678eSDavid Greenman 1106a17c678eSDavid Greenman /* 1107a17c678eSDavid Greenman * If interface is marked up and not running, then start it. 1108a17c678eSDavid Greenman * If it is marked down and running, stop it. 1109a17c678eSDavid Greenman * XXX If it's up then re-initialize it. This is so flags 1110a17c678eSDavid Greenman * such as IFF_PROMISC are handled. 1111a17c678eSDavid Greenman */ 1112a17c678eSDavid Greenman if (ifp->if_flags & IFF_UP) { 1113fb583156SDavid Greenman fxp_init(sc); 1114a17c678eSDavid Greenman } else { 1115a17c678eSDavid Greenman if (ifp->if_flags & IFF_RUNNING) 11164a5f1499SDavid Greenman fxp_stop(sc); 1117a17c678eSDavid Greenman } 1118a17c678eSDavid Greenman break; 1119a17c678eSDavid Greenman 1120a17c678eSDavid Greenman case SIOCADDMULTI: 1121a17c678eSDavid Greenman case SIOCDELMULTI: 1122a17c678eSDavid Greenman /* 1123a17c678eSDavid Greenman * Multicast list has changed; set the hardware filter 1124a17c678eSDavid Greenman * accordingly. 1125a17c678eSDavid Greenman */ 1126fb583156SDavid Greenman fxp_init(sc); 1127a17c678eSDavid Greenman error = 0; 1128a17c678eSDavid Greenman break; 1129a17c678eSDavid Greenman 1130a17c678eSDavid Greenman default: 1131a17c678eSDavid Greenman error = EINVAL; 1132a17c678eSDavid Greenman } 1133a17c678eSDavid Greenman (void) splx(s); 1134a17c678eSDavid Greenman return (error); 1135a17c678eSDavid Greenman } 1136