Annotation of sys/dev/ic/dp8390.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: dp8390.c,v 1.39 2007/05/07 18:53:04 deraadt Exp $ */
! 2: /* $NetBSD: dp8390.c,v 1.13 1998/07/05 06:49:11 jonathan Exp $ */
! 3:
! 4: /*
! 5: * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
! 6: * adapters.
! 7: *
! 8: * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
! 9: *
! 10: * Copyright (C) 1993, David Greenman. This software may be used, modified,
! 11: * copied, distributed, and sold, in both source and binary form provided that
! 12: * the above copyright and these terms are retained. Under no circumstances is
! 13: * the author responsible for the proper functioning of this software, nor does
! 14: * the author assume any responsibility for damages incurred with its use.
! 15: */
! 16:
! 17: #include "bpfilter.h"
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/device.h>
! 22: #include <sys/errno.h>
! 23: #include <sys/ioctl.h>
! 24: #include <sys/mbuf.h>
! 25: #include <sys/socket.h>
! 26: #include <sys/syslog.h>
! 27:
! 28: #include <net/if.h>
! 29: #include <net/if_dl.h>
! 30: #include <net/if_types.h>
! 31: #include <net/if_media.h>
! 32:
! 33: #ifdef INET
! 34: #include <netinet/in.h>
! 35: #include <netinet/in_systm.h>
! 36: #include <netinet/in_var.h>
! 37: #include <netinet/ip.h>
! 38: #include <netinet/if_ether.h>
! 39: #endif
! 40:
! 41: #if NBPFILTER > 0
! 42: #include <net/bpf.h>
! 43: #endif
! 44:
! 45: #include <machine/bus.h>
! 46:
! 47: #include <dev/ic/dp8390reg.h>
! 48: #include <dev/ic/dp8390var.h>
! 49:
! 50: #ifdef DEBUG
! 51: #define __inline__ /* XXX for debugging porpoises */
! 52: #endif
! 53:
! 54: static __inline__ void dp8390_xmit(struct dp8390_softc *);
! 55:
! 56: static __inline__ void dp8390_read_hdr(struct dp8390_softc *,
! 57: int, struct dp8390_ring *);
! 58: static __inline__ int dp8390_ring_copy(struct dp8390_softc *,
! 59: int, caddr_t, u_short);
! 60: static __inline__ int dp8390_write_mbuf(struct dp8390_softc *,
! 61: struct mbuf *, int);
! 62:
! 63: static int dp8390_test_mem(struct dp8390_softc *);
! 64:
! 65: int dp8390_enable(struct dp8390_softc *);
! 66: void dp8390_disable(struct dp8390_softc *);
! 67:
! 68: #ifdef DEBUG
! 69: int dp8390_debug = 0;
! 70: #endif
! 71:
! 72: /*
! 73: * Standard media init routine for the dp8390.
! 74: */
! 75: void
! 76: dp8390_media_init(struct dp8390_softc *sc)
! 77: {
! 78: ifmedia_init(&sc->sc_media, 0, dp8390_mediachange, dp8390_mediastatus);
! 79: ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
! 80: ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
! 81: }
! 82:
! 83: /*
! 84: * Do bus-independent setup.
! 85: */
! 86: int
! 87: dp8390_config(struct dp8390_softc *sc)
! 88: {
! 89: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 90: int rv;
! 91:
! 92: rv = 1;
! 93:
! 94: if (!sc->test_mem)
! 95: sc->test_mem = dp8390_test_mem;
! 96:
! 97: /* Allocate one xmit buffer if < 16k, two buffers otherwise. */
! 98: if ((sc->mem_size < 16384) ||
! 99: (sc->sc_flags & DP8390_NO_MULTI_BUFFERING))
! 100: sc->txb_cnt = 1;
! 101: else if (sc->mem_size < 8192 * 3)
! 102: sc->txb_cnt = 2;
! 103: else
! 104: sc->txb_cnt = 3;
! 105:
! 106: sc->tx_page_start = sc->mem_start >> ED_PAGE_SHIFT;
! 107: sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
! 108: sc->rec_page_stop = sc->tx_page_start + (sc->mem_size >> ED_PAGE_SHIFT);
! 109: sc->mem_ring = sc->mem_start + (sc->rec_page_start << ED_PAGE_SHIFT);
! 110: sc->mem_end = sc->mem_start + sc->mem_size;
! 111:
! 112: /* Now zero memory and verify that it is clear. */
! 113: if ((*sc->test_mem)(sc))
! 114: goto out;
! 115:
! 116: /* Set interface to stopped condition (reset). */
! 117: dp8390_stop(sc);
! 118:
! 119: /* Initialize ifnet structure. */
! 120: bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
! 121: ifp->if_softc = sc;
! 122: ifp->if_start = dp8390_start;
! 123: ifp->if_ioctl = dp8390_ioctl;
! 124: if (!ifp->if_watchdog)
! 125: ifp->if_watchdog = dp8390_watchdog;
! 126: ifp->if_flags =
! 127: IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
! 128: IFQ_SET_READY(&ifp->if_snd);
! 129:
! 130: ifp->if_capabilities = IFCAP_VLAN_MTU;
! 131:
! 132: /* Print additional info when attached. */
! 133: printf(", address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
! 134:
! 135: /* Initialize media goo. */
! 136: (*sc->sc_media_init)(sc);
! 137:
! 138: /* Attach the interface. */
! 139: if_attach(ifp);
! 140: ether_ifattach(ifp);
! 141:
! 142: rv = 0;
! 143: out:
! 144: return (rv);
! 145: }
! 146:
! 147: /*
! 148: * Media change callback.
! 149: */
! 150: int
! 151: dp8390_mediachange(struct ifnet *ifp)
! 152: {
! 153: struct dp8390_softc *sc = ifp->if_softc;
! 154:
! 155: if (sc->sc_mediachange)
! 156: return ((*sc->sc_mediachange)(sc));
! 157:
! 158: return (0);
! 159: }
! 160:
! 161: /*
! 162: * Media status callback.
! 163: */
! 164: void
! 165: dp8390_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
! 166: {
! 167: struct dp8390_softc *sc = ifp->if_softc;
! 168:
! 169: if (sc->sc_enabled == 0) {
! 170: ifmr->ifm_active = IFM_ETHER | IFM_NONE;
! 171: ifmr->ifm_status = 0;
! 172: return;
! 173: }
! 174:
! 175: if (sc->sc_mediastatus)
! 176: (*sc->sc_mediastatus)(sc, ifmr);
! 177: }
! 178:
! 179: /*
! 180: * Reset interface.
! 181: */
! 182: void
! 183: dp8390_reset(struct dp8390_softc *sc)
! 184: {
! 185: int s;
! 186:
! 187: s = splnet();
! 188: dp8390_stop(sc);
! 189: dp8390_init(sc);
! 190: splx(s);
! 191: }
! 192:
! 193: /*
! 194: * Take interface offline.
! 195: */
! 196: void
! 197: dp8390_stop(struct dp8390_softc *sc)
! 198: {
! 199: bus_space_tag_t regt = sc->sc_regt;
! 200: bus_space_handle_t regh = sc->sc_regh;
! 201: int n = 5000;
! 202:
! 203: /* Stop everything on the interface, and select page 0 registers. */
! 204: NIC_BARRIER(regt, regh);
! 205: NIC_PUT(regt, regh, ED_P0_CR,
! 206: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
! 207: NIC_BARRIER(regt, regh);
! 208:
! 209: /*
! 210: * Wait for interface to enter stopped state, but limit # of checks to
! 211: * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but
! 212: * just in case it's an old one.
! 213: */
! 214: while (((NIC_GET(regt, regh,
! 215: ED_P0_ISR) & ED_ISR_RST) == 0) && --n)
! 216: DELAY(1);
! 217:
! 218: if (sc->stop_card != NULL)
! 219: (*sc->stop_card)(sc);
! 220: }
! 221:
! 222: /*
! 223: * Device timeout/watchdog routine. Entered if the device neglects to generate
! 224: * an interrupt after a transmit has been started on it.
! 225: */
! 226:
! 227: void
! 228: dp8390_watchdog(struct ifnet *ifp)
! 229: {
! 230: struct dp8390_softc *sc = ifp->if_softc;
! 231:
! 232: log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
! 233: ++sc->sc_arpcom.ac_if.if_oerrors;
! 234:
! 235: dp8390_reset(sc);
! 236: }
! 237:
! 238: /*
! 239: * Initialize device.
! 240: */
! 241: void
! 242: dp8390_init(struct dp8390_softc *sc)
! 243: {
! 244: bus_space_tag_t regt = sc->sc_regt;
! 245: bus_space_handle_t regh = sc->sc_regh;
! 246: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 247: u_int8_t mcaf[8];
! 248: int i;
! 249:
! 250: /*
! 251: * Initialize the NIC in the exact order outlined in the NS manual.
! 252: * This init procedure is "mandatory"...don't change what or when
! 253: * things happen.
! 254: */
! 255:
! 256: /* Reset transmitter flags. */
! 257: ifp->if_timer = 0;
! 258:
! 259: sc->txb_inuse = 0;
! 260: sc->txb_new = 0;
! 261: sc->txb_next_tx = 0;
! 262:
! 263: /* Set interface for page 0, remote DMA complete, stopped. */
! 264: NIC_BARRIER(regt, regh);
! 265: NIC_PUT(regt, regh, ED_P0_CR,
! 266: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
! 267: NIC_BARRIER(regt, regh);
! 268:
! 269: if (sc->dcr_reg & ED_DCR_LS) {
! 270: NIC_PUT(regt, regh, ED_P0_DCR, sc->dcr_reg);
! 271: } else {
! 272: /*
! 273: * Set FIFO threshold to 8, No auto-init Remote DMA, byte
! 274: * order=80x86, byte-wide DMA xfers,
! 275: */
! 276: NIC_PUT(regt, regh, ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
! 277: }
! 278:
! 279: /* Clear remote byte count registers. */
! 280: NIC_PUT(regt, regh, ED_P0_RBCR0, 0);
! 281: NIC_PUT(regt, regh, ED_P0_RBCR1, 0);
! 282:
! 283: /* Tell RCR to do nothing for now. */
! 284: NIC_PUT(regt, regh, ED_P0_RCR, ED_RCR_MON | sc->rcr_proto);
! 285:
! 286: /* Place NIC in internal loopback mode. */
! 287: NIC_PUT(regt, regh, ED_P0_TCR, ED_TCR_LB0);
! 288:
! 289: /* Set lower bits of byte addressable framing to 0. */
! 290: if (sc->is790)
! 291: NIC_PUT(regt, regh, 0x09, 0);
! 292:
! 293: /* Initialize receive buffer ring. */
! 294: NIC_PUT(regt, regh, ED_P0_BNRY, sc->rec_page_start);
! 295: NIC_PUT(regt, regh, ED_P0_PSTART, sc->rec_page_start);
! 296: NIC_PUT(regt, regh, ED_P0_PSTOP, sc->rec_page_stop);
! 297:
! 298: /*
! 299: * Enable the following interrupts: receive/transmit complete,
! 300: * receive/transmit error, and Receiver OverWrite.
! 301: *
! 302: * Counter overflow and Remote DMA complete are *not* enabled.
! 303: */
! 304: NIC_PUT(regt, regh, ED_P0_IMR,
! 305: ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE |
! 306: ED_IMR_OVWE);
! 307:
! 308: /*
! 309: * Clear all interrupts. A '1' in each bit position clears the
! 310: * corresponding flag.
! 311: */
! 312: NIC_PUT(regt, regh, ED_P0_ISR, 0xff);
! 313:
! 314: /* Program command register for page 1. */
! 315: NIC_BARRIER(regt, regh);
! 316: NIC_PUT(regt, regh, ED_P0_CR,
! 317: sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
! 318: NIC_BARRIER(regt, regh);
! 319:
! 320: /* Copy out our station address. */
! 321: for (i = 0; i < ETHER_ADDR_LEN; ++i)
! 322: NIC_PUT(regt, regh, ED_P1_PAR0 + i,
! 323: sc->sc_arpcom.ac_enaddr[i]);
! 324:
! 325: /* Set multicast filter on chip. */
! 326: dp8390_getmcaf(&sc->sc_arpcom, mcaf);
! 327: for (i = 0; i < 8; i++)
! 328: NIC_PUT(regt, regh, ED_P1_MAR0 + i, mcaf[i]);
! 329:
! 330: /*
! 331: * Set current page pointer to one page after the boundary pointer, as
! 332: * recommended in the National manual.
! 333: */
! 334: sc->next_packet = sc->rec_page_start + 1;
! 335: NIC_PUT(regt, regh, ED_P1_CURR, sc->next_packet);
! 336:
! 337: /* Program command register for page 0. */
! 338: NIC_BARRIER(regt, regh);
! 339: NIC_PUT(regt, regh, ED_P1_CR,
! 340: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
! 341: NIC_BARRIER(regt, regh);
! 342:
! 343: /* Accept broadcast and multicast packets by default. */
! 344: i = ED_RCR_AB | ED_RCR_AM | sc->rcr_proto;
! 345: if (ifp->if_flags & IFF_PROMISC) {
! 346: /*
! 347: * Set promiscuous mode. Multicast filter was set earlier so
! 348: * that we should receive all multicast packets.
! 349: */
! 350: i |= ED_RCR_PRO | ED_RCR_AR | ED_RCR_SEP;
! 351: }
! 352: NIC_PUT(regt, regh, ED_P0_RCR, i);
! 353:
! 354: /* Take interface out of loopback. */
! 355: NIC_PUT(regt, regh, ED_P0_TCR, 0);
! 356:
! 357: /* Do any card-specific initialization, if applicable. */
! 358: if (sc->init_card)
! 359: (*sc->init_card)(sc);
! 360:
! 361: /* Fire up the interface. */
! 362: NIC_BARRIER(regt, regh);
! 363: NIC_PUT(regt, regh, ED_P0_CR,
! 364: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
! 365:
! 366: /* Set 'running' flag, and clear output active flag. */
! 367: ifp->if_flags |= IFF_RUNNING;
! 368: ifp->if_flags &= ~IFF_OACTIVE;
! 369:
! 370: /* ...and attempt to start output. */
! 371: dp8390_start(ifp);
! 372: }
! 373:
! 374: /*
! 375: * This routine actually starts the transmission on the interface.
! 376: */
! 377: static __inline__ void
! 378: dp8390_xmit(struct dp8390_softc *sc)
! 379: {
! 380: bus_space_tag_t regt = sc->sc_regt;
! 381: bus_space_handle_t regh = sc->sc_regh;
! 382: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 383: u_short len;
! 384:
! 385: #ifdef DIAGNOSTIC
! 386: if ((sc->txb_next_tx + sc->txb_inuse) % sc->txb_cnt != sc->txb_new)
! 387: panic("dp8390_xmit: desync, next_tx=%d inuse=%d cnt=%d new=%d",
! 388: sc->txb_next_tx, sc->txb_inuse, sc->txb_cnt, sc->txb_new);
! 389:
! 390: if (sc->txb_inuse == 0)
! 391: panic("dp8390_xmit: no packets to xmit");
! 392: #endif
! 393:
! 394: len = sc->txb_len[sc->txb_next_tx];
! 395:
! 396: /* Set NIC for page 0 register access. */
! 397: NIC_BARRIER(regt, regh);
! 398: NIC_PUT(regt, regh, ED_P0_CR,
! 399: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
! 400: NIC_BARRIER(regt, regh);
! 401:
! 402: /* Set TX buffer start page. */
! 403: NIC_PUT(regt, regh, ED_P0_TPSR, sc->tx_page_start +
! 404: sc->txb_next_tx * ED_TXBUF_SIZE);
! 405:
! 406: /* Set TX length. */
! 407: NIC_PUT(regt, regh, ED_P0_TBCR0, len);
! 408: NIC_PUT(regt, regh, ED_P0_TBCR1, len >> 8);
! 409:
! 410: /* Set page 0, remote DMA complete, transmit packet, and *start*. */
! 411: NIC_BARRIER(regt, regh);
! 412: NIC_PUT(regt, regh, ED_P0_CR,
! 413: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_TXP | ED_CR_STA);
! 414:
! 415: /* Point to next transmit buffer slot and wrap if necessary. */
! 416: if (++sc->txb_next_tx == sc->txb_cnt)
! 417: sc->txb_next_tx = 0;
! 418:
! 419: /* Set a timer just in case we never hear from the board again. */
! 420: ifp->if_timer = 2;
! 421: }
! 422:
! 423: /*
! 424: * Start output on interface.
! 425: * We make two assumptions here:
! 426: * 1) that the current priority is set to splnet _before_ this code
! 427: * is called *and* is returned to the appropriate priority after
! 428: * return
! 429: * 2) that the IFF_OACTIVE flag is checked before this code is called
! 430: * (i.e. that the output part of the interface is idle)
! 431: */
! 432: void
! 433: dp8390_start(struct ifnet *ifp)
! 434: {
! 435: struct dp8390_softc *sc = ifp->if_softc;
! 436: struct mbuf *m0;
! 437: int buffer;
! 438: int len;
! 439:
! 440: if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
! 441: return;
! 442:
! 443: outloop:
! 444: /* See if there is room to put another packet in the buffer. */
! 445: if (sc->txb_inuse == sc->txb_cnt) {
! 446: /* No room. Indicate this to the outside world and exit. */
! 447: ifp->if_flags |= IFF_OACTIVE;
! 448: return;
! 449: }
! 450: IFQ_DEQUEUE(&ifp->if_snd, m0);
! 451: if (m0 == 0)
! 452: return;
! 453:
! 454: /* We need to use m->m_pkthdr.len, so require the header */
! 455: if ((m0->m_flags & M_PKTHDR) == 0)
! 456: panic("dp8390_start: no header mbuf");
! 457:
! 458: #if NBPFILTER > 0
! 459: /* Tap off here if there is a BPF listener. */
! 460: if (ifp->if_bpf)
! 461: bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
! 462: #endif
! 463:
! 464: /* txb_new points to next open buffer slot. */
! 465: buffer = sc->mem_start +
! 466: ((sc->txb_new * ED_TXBUF_SIZE) << ED_PAGE_SHIFT);
! 467:
! 468: if (sc->write_mbuf)
! 469: len = (*sc->write_mbuf)(sc, m0, buffer);
! 470: else
! 471: len = dp8390_write_mbuf(sc, m0, buffer);
! 472:
! 473: m_freem(m0);
! 474: sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN - ETHER_CRC_LEN);
! 475:
! 476: /* Point to next buffer slot and wrap if necessary. */
! 477: if (++sc->txb_new == sc->txb_cnt)
! 478: sc->txb_new = 0;
! 479:
! 480: /* Start the first packet transmitting. */
! 481: if (sc->txb_inuse++ == 0)
! 482: dp8390_xmit(sc);
! 483:
! 484: /* Loop back to the top to possibly buffer more packets. */
! 485: goto outloop;
! 486: }
! 487:
! 488: /*
! 489: * Ethernet interface receiver interrupt.
! 490: */
! 491: void
! 492: dp8390_rint(struct dp8390_softc *sc)
! 493: {
! 494: bus_space_tag_t regt = sc->sc_regt;
! 495: bus_space_handle_t regh = sc->sc_regh;
! 496: struct dp8390_ring packet_hdr;
! 497: int packet_ptr;
! 498: u_short len;
! 499: u_char boundary, current;
! 500: u_char nlen;
! 501:
! 502: loop:
! 503: /* Set NIC to page 1 registers to get 'current' pointer. */
! 504: NIC_BARRIER(regt, regh);
! 505: NIC_PUT(regt, regh, ED_P0_CR,
! 506: sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
! 507: NIC_BARRIER(regt, regh);
! 508:
! 509: /*
! 510: * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
! 511: * it points to where new data has been buffered. The 'CURR' (current)
! 512: * register points to the logical end of the ring-buffer - i.e. it
! 513: * points to where additional new data will be added. We loop here
! 514: * until the logical beginning equals the logical end (or in other
! 515: * words, until the ring-buffer is empty).
! 516: */
! 517: current = NIC_GET(regt, regh, ED_P1_CURR);
! 518: if (sc->next_packet == current)
! 519: return;
! 520:
! 521: /* Set NIC to page 0 registers to update boundary register. */
! 522: NIC_BARRIER(regt, regh);
! 523: NIC_PUT(regt, regh, ED_P1_CR,
! 524: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
! 525: NIC_BARRIER(regt, regh);
! 526:
! 527: do {
! 528: /* Get pointer to this buffer's header structure. */
! 529: packet_ptr = sc->mem_ring +
! 530: ((sc->next_packet - sc->rec_page_start) << ED_PAGE_SHIFT);
! 531:
! 532: if (sc->read_hdr)
! 533: (*sc->read_hdr)(sc, packet_ptr, &packet_hdr);
! 534: else
! 535: dp8390_read_hdr(sc, packet_ptr, &packet_hdr);
! 536: len = packet_hdr.count;
! 537:
! 538: /*
! 539: * Try do deal with old, buggy chips that sometimes duplicate
! 540: * the low byte of the length into the high byte. We do this
! 541: * by simply ignoring the high byte of the length and always
! 542: * recalculating it.
! 543: *
! 544: * NOTE: sc->next_packet is pointing at the current packet.
! 545: */
! 546: if (packet_hdr.next_packet >= sc->next_packet)
! 547: nlen = (packet_hdr.next_packet - sc->next_packet);
! 548: else
! 549: nlen = ((packet_hdr.next_packet - sc->rec_page_start) +
! 550: (sc->rec_page_stop - sc->next_packet));
! 551: --nlen;
! 552: if ((len & ED_PAGE_MASK) + sizeof(packet_hdr) > ED_PAGE_SIZE)
! 553: --nlen;
! 554: len = (len & ED_PAGE_MASK) | (nlen << ED_PAGE_SHIFT);
! 555: #ifdef DIAGNOSTIC
! 556: if (len != packet_hdr.count) {
! 557: printf("%s: length does not match "
! 558: "next packet pointer\n", sc->sc_dev.dv_xname);
! 559: printf("%s: len %04x nlen %04x start %02x "
! 560: "first %02x curr %02x next %02x stop %02x\n",
! 561: sc->sc_dev.dv_xname, packet_hdr.count, len,
! 562: sc->rec_page_start, sc->next_packet, current,
! 563: packet_hdr.next_packet, sc->rec_page_stop);
! 564: }
! 565: #endif
! 566:
! 567: /*
! 568: * Be fairly liberal about what we allow as a "reasonable"
! 569: * length so that a [crufty] packet will make it to BPF (and
! 570: * can thus be analyzed). Note that all that is really
! 571: * important is that we have a length that will fit into one
! 572: * mbuf cluster or less; the upper layer protocols can then
! 573: * figure out the length from their own length field(s).
! 574: */
! 575: if (len <= MCLBYTES &&
! 576: packet_hdr.next_packet >= sc->rec_page_start &&
! 577: packet_hdr.next_packet < sc->rec_page_stop) {
! 578: /* Go get packet. */
! 579: dp8390_read(sc,
! 580: packet_ptr + sizeof(struct dp8390_ring),
! 581: len - sizeof(struct dp8390_ring));
! 582: } else {
! 583: /* Really BAD. The ring pointers are corrupted. */
! 584: log(LOG_ERR, "%s: NIC memory corrupt - "
! 585: "invalid packet length %d\n",
! 586: sc->sc_dev.dv_xname, len);
! 587: ++sc->sc_arpcom.ac_if.if_ierrors;
! 588: dp8390_reset(sc);
! 589: return;
! 590: }
! 591:
! 592: /* Update next packet pointer. */
! 593: sc->next_packet = packet_hdr.next_packet;
! 594:
! 595: /*
! 596: * Update NIC boundary pointer - being careful to keep it one
! 597: * buffer behind (as recommended by NS databook).
! 598: */
! 599: boundary = sc->next_packet - 1;
! 600: if (boundary < sc->rec_page_start)
! 601: boundary = sc->rec_page_stop - 1;
! 602: NIC_PUT(regt, regh, ED_P0_BNRY, boundary);
! 603: } while (sc->next_packet != current);
! 604:
! 605: goto loop;
! 606: }
! 607:
! 608: /* Ethernet interface interrupt processor. */
! 609: int
! 610: dp8390_intr(void *arg)
! 611: {
! 612: struct dp8390_softc *sc = (struct dp8390_softc *)arg;
! 613: bus_space_tag_t regt = sc->sc_regt;
! 614: bus_space_handle_t regh = sc->sc_regh;
! 615: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 616: u_char isr;
! 617:
! 618: if (sc->sc_enabled == 0)
! 619: return (0);
! 620:
! 621: /* Set NIC to page 0 registers. */
! 622: NIC_BARRIER(regt, regh);
! 623: NIC_PUT(regt, regh, ED_P0_CR,
! 624: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
! 625: NIC_BARRIER(regt, regh);
! 626:
! 627: isr = NIC_GET(regt, regh, ED_P0_ISR);
! 628: if (!isr)
! 629: return (0);
! 630:
! 631: /* Loop until there are no more new interrupts. */
! 632: for (;;) {
! 633: /*
! 634: * Reset all the bits that we are 'acknowledging' by writing a
! 635: * '1' to each bit position that was set.
! 636: * (Writing a '1' *clears* the bit.)
! 637: */
! 638: NIC_PUT(regt, regh, ED_P0_ISR, isr);
! 639:
! 640: /* Work around for AX88190 bug */
! 641: if ((sc->sc_flags & DP8390_DO_AX88190_WORKAROUND) != 0)
! 642: while ((NIC_GET(regt, regh, ED_P0_ISR) & isr) != 0) {
! 643: NIC_PUT(regt, regh, ED_P0_ISR, 0);
! 644: NIC_PUT(regt, regh, ED_P0_ISR, isr);
! 645: }
! 646:
! 647: /*
! 648: * Handle transmitter interrupts. Handle these first because
! 649: * the receiver will reset the board under some conditions.
! 650: *
! 651: * If the chip was reset while a packet was transmitting, it
! 652: * may still deliver a TX interrupt. In this case, just ignore
! 653: * the interrupt.
! 654: */
! 655: if (isr & (ED_ISR_PTX | ED_ISR_TXE) &&
! 656: sc->txb_inuse != 0) {
! 657: u_char collisions =
! 658: NIC_GET(regt, regh, ED_P0_NCR) & 0x0f;
! 659:
! 660: /*
! 661: * Check for transmit error. If a TX completed with an
! 662: * error, we end up throwing the packet away. Really
! 663: * the only error that is possible is excessive
! 664: * collisions, and in this case it is best to allow the
! 665: * automatic mechanisms of TCP to backoff the flow. Of
! 666: * course, with UDP we're screwed, but this is expected
! 667: * when a network is heavily loaded.
! 668: */
! 669: if (isr & ED_ISR_TXE) {
! 670: /*
! 671: * Excessive collisions (16).
! 672: */
! 673: if ((NIC_GET(regt, regh, ED_P0_TSR)
! 674: & ED_TSR_ABT) && (collisions == 0)) {
! 675: /*
! 676: * When collisions total 16, the P0_NCR
! 677: * will indicate 0, and the TSR_ABT is
! 678: * set.
! 679: */
! 680: collisions = 16;
! 681: }
! 682:
! 683: /* Update output errors counter. */
! 684: ++ifp->if_oerrors;
! 685: } else {
! 686: /*
! 687: * Throw away the non-error status bits.
! 688: *
! 689: * XXX
! 690: * It may be useful to detect loss of carrier
! 691: * and late collisions here.
! 692: */
! 693: (void)NIC_GET(regt, regh, ED_P0_TSR);
! 694:
! 695: /*
! 696: * Update total number of successfully
! 697: * transmitted packets.
! 698: */
! 699: ++ifp->if_opackets;
! 700: }
! 701:
! 702: /* Clear watchdog timer. */
! 703: ifp->if_timer = 0;
! 704: ifp->if_flags &= ~IFF_OACTIVE;
! 705:
! 706: /*
! 707: * Add in total number of collisions on last
! 708: * transmission.
! 709: */
! 710: ifp->if_collisions += collisions;
! 711:
! 712: /*
! 713: * Decrement buffer in-use count if not zero (can only
! 714: * be zero if a transmitter interrupt occurred while not
! 715: * actually transmitting).
! 716: * If data is ready to transmit, start it transmitting,
! 717: * otherwise defer until after handling receiver.
! 718: */
! 719: if (--sc->txb_inuse != 0)
! 720: dp8390_xmit(sc);
! 721: }
! 722:
! 723: /* Handle receiver interrupts. */
! 724: if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) {
! 725: /*
! 726: * Overwrite warning. In order to make sure that a
! 727: * lockup of the local DMA hasn't occurred, we reset
! 728: * and re-init the NIC. The NSC manual suggests only a
! 729: * partial reset/re-init is necessary - but some chips
! 730: * seem to want more. The DMA lockup has been seen
! 731: * only with early rev chips - Methinks this bug was
! 732: * fixed in later revs. -DG
! 733: */
! 734: if (isr & ED_ISR_OVW) {
! 735: ++ifp->if_ierrors;
! 736: #ifdef DEBUG
! 737: log(LOG_WARNING, "%s: warning - receiver "
! 738: "ring buffer overrun\n",
! 739: sc->sc_dev.dv_xname);
! 740: #endif
! 741: /* Stop/reset/re-init NIC. */
! 742: dp8390_reset(sc);
! 743: } else {
! 744: /*
! 745: * Receiver Error. One or more of: CRC error,
! 746: * frame alignment error FIFO overrun, or
! 747: * missed packet.
! 748: */
! 749: if (isr & ED_ISR_RXE) {
! 750: ++ifp->if_ierrors;
! 751: #ifdef DEBUG
! 752: if (dp8390_debug) {
! 753: printf("%s: receive error %x\n",
! 754: sc->sc_dev.dv_xname,
! 755: NIC_GET(regt, regh,
! 756: ED_P0_RSR));
! 757: }
! 758: #endif
! 759: }
! 760:
! 761: /*
! 762: * Go get the packet(s)
! 763: * XXX - Doing this on an error is dubious
! 764: * because there shouldn't be any data to get
! 765: * (we've configured the interface to not
! 766: * accept packets with errors).
! 767: */
! 768: if (sc->recv_int)
! 769: (*sc->recv_int)(sc);
! 770: else
! 771: dp8390_rint(sc);
! 772: }
! 773: }
! 774:
! 775: /*
! 776: * If it looks like the transmitter can take more data, attempt
! 777: * to start output on the interface. This is done after
! 778: * handling the receiver to give the receiver priority.
! 779: */
! 780: dp8390_start(ifp);
! 781:
! 782: /*
! 783: * Return NIC CR to standard state: page 0, remote DMA
! 784: * complete, start (toggling the TXP bit off, even if was just
! 785: * set in the transmit routine, is *okay* - it is 'edge'
! 786: * triggered from low to high).
! 787: */
! 788: NIC_BARRIER(regt, regh);
! 789: NIC_PUT(regt, regh, ED_P0_CR,
! 790: sc->cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
! 791: NIC_BARRIER(regt, regh);
! 792:
! 793: /*
! 794: * If the Network Talley Counters overflow, read them to reset
! 795: * them. It appears that old 8390's won't clear the ISR flag
! 796: * otherwise - resulting in an infinite loop.
! 797: */
! 798: if (isr & ED_ISR_CNT) {
! 799: (void)NIC_GET(regt, regh, ED_P0_CNTR0);
! 800: (void)NIC_GET(regt, regh, ED_P0_CNTR1);
! 801: (void)NIC_GET(regt, regh, ED_P0_CNTR2);
! 802: }
! 803:
! 804: isr = NIC_GET(regt, regh, ED_P0_ISR);
! 805: if (!isr)
! 806: return (1);
! 807: }
! 808: }
! 809:
! 810: /*
! 811: * Process an ioctl request. This code needs some work - it looks pretty ugly.
! 812: */
! 813: int
! 814: dp8390_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
! 815: {
! 816: struct dp8390_softc *sc = ifp->if_softc;
! 817: struct ifaddr *ifa = (struct ifaddr *) data;
! 818: struct ifreq *ifr = (struct ifreq *) data;
! 819: int s, error = 0;
! 820:
! 821: s = splnet();
! 822:
! 823: switch (cmd) {
! 824:
! 825: case SIOCSIFADDR:
! 826: if ((error = dp8390_enable(sc)) != 0)
! 827: break;
! 828: ifp->if_flags |= IFF_UP;
! 829:
! 830: switch (ifa->ifa_addr->sa_family) {
! 831: #ifdef INET
! 832: case AF_INET:
! 833: dp8390_init(sc);
! 834: arp_ifinit(&sc->sc_arpcom, ifa);
! 835: break;
! 836: #endif
! 837: default:
! 838: dp8390_init(sc);
! 839: break;
! 840: }
! 841: break;
! 842:
! 843: case SIOCSIFMTU:
! 844: if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) {
! 845: error = EINVAL;
! 846: } else if (ifp->if_mtu != ifr->ifr_mtu) {
! 847: ifp->if_mtu = ifr->ifr_mtu;
! 848: }
! 849: break;
! 850:
! 851: case SIOCSIFFLAGS:
! 852: if ((ifp->if_flags & IFF_UP) == 0 &&
! 853: (ifp->if_flags & IFF_RUNNING) != 0) {
! 854: /*
! 855: * If interface is marked down and it is running, then
! 856: * stop it.
! 857: */
! 858: dp8390_stop(sc);
! 859: ifp->if_flags &= ~IFF_RUNNING;
! 860: dp8390_disable(sc);
! 861: } else if ((ifp->if_flags & IFF_UP) != 0 &&
! 862: (ifp->if_flags & IFF_RUNNING) == 0) {
! 863: /*
! 864: * If interface is marked up and it is stopped, then
! 865: * start it.
! 866: */
! 867: if ((error = dp8390_enable(sc)) != 0)
! 868: break;
! 869: dp8390_init(sc);
! 870: } else if ((ifp->if_flags & IFF_UP) != 0) {
! 871: /*
! 872: * Reset the interface to pick up changes in any other
! 873: * flags that affect hardware registers.
! 874: */
! 875: dp8390_stop(sc);
! 876: dp8390_init(sc);
! 877: }
! 878: break;
! 879:
! 880: case SIOCADDMULTI:
! 881: case SIOCDELMULTI:
! 882: if (sc->sc_enabled == 0) {
! 883: error = EIO;
! 884: break;
! 885: }
! 886:
! 887: /* Update our multicast list. */
! 888: error = (cmd == SIOCADDMULTI) ?
! 889: ether_addmulti(ifr, &sc->sc_arpcom) :
! 890: ether_delmulti(ifr, &sc->sc_arpcom);
! 891:
! 892: if (error == ENETRESET) {
! 893: /*
! 894: * Multicast list has changed; set the hardware filter
! 895: * accordingly.
! 896: */
! 897: if (ifp->if_flags & IFF_RUNNING) {
! 898: dp8390_stop(sc); /* XXX for ds_setmcaf? */
! 899: dp8390_init(sc);
! 900: }
! 901: error = 0;
! 902: }
! 903: break;
! 904:
! 905: case SIOCGIFMEDIA:
! 906: case SIOCSIFMEDIA:
! 907: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
! 908: break;
! 909:
! 910: default:
! 911: error = EINVAL;
! 912: break;
! 913: }
! 914:
! 915: splx(s);
! 916: return (error);
! 917: }
! 918:
! 919: /*
! 920: * Retrieve packet from buffer memory and send to the next level up via
! 921: * ether_input(). If there is a BPF listener, give a copy to BPF, too.
! 922: */
! 923: void
! 924: dp8390_read(struct dp8390_softc *sc, int buf, u_short len)
! 925: {
! 926: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 927: struct mbuf *m;
! 928:
! 929: /* Pull packet off interface. */
! 930: m = dp8390_get(sc, buf, len);
! 931: if (m == 0) {
! 932: ifp->if_ierrors++;
! 933: return;
! 934: }
! 935:
! 936: ifp->if_ipackets++;
! 937:
! 938: #if NBPFILTER > 0
! 939: /*
! 940: * Check if there's a BPF listener on this interface.
! 941: * If so, hand off the raw packet to bpf.
! 942: */
! 943: if (ifp->if_bpf)
! 944: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
! 945: #endif
! 946:
! 947: ether_input_mbuf(ifp, m);
! 948: }
! 949:
! 950:
! 951: /*
! 952: * Supporting routines.
! 953: */
! 954:
! 955: /*
! 956: * Compute the multicast address filter from the list of multicast addresses we
! 957: * need to listen to.
! 958: */
! 959: void
! 960: dp8390_getmcaf(struct arpcom *ac, u_int8_t *af)
! 961: {
! 962: struct ifnet *ifp = &ac->ac_if;
! 963: struct ether_multi *enm;
! 964: u_int32_t crc;
! 965: int i;
! 966: struct ether_multistep step;
! 967:
! 968: /*
! 969: * Set up multicast address filter by passing all multicast addresses
! 970: * through a crc generator, and then using the high order 6 bits as an
! 971: * index into the 64 bit logical address filter. The high order bit
! 972: * selects the word, while the rest of the bits select the bit within
! 973: * the word.
! 974: */
! 975:
! 976: if (ifp->if_flags & IFF_PROMISC) {
! 977: ifp->if_flags |= IFF_ALLMULTI;
! 978: for (i = 0; i < 8; i++)
! 979: af[i] = 0xff;
! 980: return;
! 981: }
! 982: for (i = 0; i < 8; i++)
! 983: af[i] = 0;
! 984: ETHER_FIRST_MULTI(step, ac, enm);
! 985: while (enm != NULL) {
! 986: if (bcmp(enm->enm_addrlo, enm->enm_addrhi,
! 987: sizeof(enm->enm_addrlo)) != 0) {
! 988: /*
! 989: * We must listen to a range of multicast addresses.
! 990: * For now, just accept all multicasts, rather than
! 991: * trying to set only those filter bits needed to match
! 992: * the range. (At this time, the only use of address
! 993: * ranges is for IP multicast routing, for which the
! 994: * range is big enough to require all bits set.)
! 995: */
! 996: ifp->if_flags |= IFF_ALLMULTI;
! 997: for (i = 0; i < 8; i++)
! 998: af[i] = 0xff;
! 999: return;
! 1000: }
! 1001:
! 1002: /* Just want the 6 most significant bits. */
! 1003: crc = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26;
! 1004:
! 1005: /* Turn on the corresponding bit in the filter. */
! 1006: af[crc >> 3] |= 1 << (crc & 0x7);
! 1007:
! 1008: ETHER_NEXT_MULTI(step, enm);
! 1009: }
! 1010: ifp->if_flags &= ~IFF_ALLMULTI;
! 1011: }
! 1012:
! 1013: /*
! 1014: * Copy data from receive buffer to a new mbuf chain allocating mbufs
! 1015: * as needed. Return pointer to first mbuf in chain.
! 1016: * sc = dp8390 info (softc)
! 1017: * src = pointer in dp8390 ring buffer
! 1018: * total_len = amount of data to copy
! 1019: */
! 1020: struct mbuf *
! 1021: dp8390_get(struct dp8390_softc *sc, int src, u_short total_len)
! 1022: {
! 1023: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 1024: struct mbuf *m, *m0, *newm;
! 1025: u_short len;
! 1026:
! 1027: MGETHDR(m0, M_DONTWAIT, MT_DATA);
! 1028: if (m0 == NULL)
! 1029: return (0);
! 1030: m0->m_pkthdr.rcvif = ifp;
! 1031: m0->m_pkthdr.len = total_len;
! 1032: len = MHLEN;
! 1033: m = m0;
! 1034:
! 1035: while (total_len > 0) {
! 1036: if (total_len >= MINCLSIZE) {
! 1037: MCLGET(m, M_DONTWAIT);
! 1038: if (!(m->m_flags & M_EXT))
! 1039: goto bad;
! 1040: len = MCLBYTES;
! 1041: }
! 1042:
! 1043: /*
! 1044: * Make sure the data after the Ethernet header is aligned.
! 1045: */
! 1046: if (m == m0) {
! 1047: caddr_t newdata = (caddr_t)
! 1048: ALIGN(m->m_data + sizeof(struct ether_header)) -
! 1049: sizeof(struct ether_header);
! 1050: len -= newdata - m->m_data;
! 1051: m->m_data = newdata;
! 1052: }
! 1053:
! 1054: m->m_len = len = min(total_len, len);
! 1055: if (sc->ring_copy)
! 1056: src = (*sc->ring_copy)(sc, src, mtod(m, caddr_t), len);
! 1057: else
! 1058: src = dp8390_ring_copy(sc, src, mtod(m, caddr_t), len);
! 1059:
! 1060: total_len -= len;
! 1061: if (total_len > 0) {
! 1062: MGET(newm, M_DONTWAIT, MT_DATA);
! 1063: if (newm == NULL)
! 1064: goto bad;
! 1065: len = MLEN;
! 1066: m = m->m_next = newm;
! 1067: }
! 1068: }
! 1069:
! 1070: return (m0);
! 1071:
! 1072: bad:
! 1073: m_freem(m0);
! 1074: return (0);
! 1075: }
! 1076:
! 1077:
! 1078: /*
! 1079: * Default driver support functions.
! 1080: *
! 1081: * NOTE: all support functions assume 8-bit shared memory.
! 1082: */
! 1083: /*
! 1084: * Zero NIC buffer memory and verify that it is clear.
! 1085: */
! 1086: static int
! 1087: dp8390_test_mem(struct dp8390_softc *sc)
! 1088: {
! 1089: bus_space_tag_t buft = sc->sc_buft;
! 1090: bus_space_handle_t bufh = sc->sc_bufh;
! 1091: int i;
! 1092:
! 1093: bus_space_set_region_1(buft, bufh, sc->mem_start, 0, sc->mem_size);
! 1094:
! 1095: for (i = 0; i < sc->mem_size; ++i) {
! 1096: if (bus_space_read_1(buft, bufh, sc->mem_start + i)) {
! 1097: printf(": failed to clear NIC buffer at offset %x - "
! 1098: "check configuration\n", (sc->mem_start + i));
! 1099: return 1;
! 1100: }
! 1101: }
! 1102:
! 1103: return 0;
! 1104: }
! 1105:
! 1106: /*
! 1107: * Read a packet header from the ring, given the source offset.
! 1108: */
! 1109: static __inline__ void
! 1110: dp8390_read_hdr(struct dp8390_softc *sc, int src, struct dp8390_ring *hdrp)
! 1111: {
! 1112: bus_space_tag_t buft = sc->sc_buft;
! 1113: bus_space_handle_t bufh = sc->sc_bufh;
! 1114:
! 1115: /*
! 1116: * The byte count includes a 4 byte header that was added by
! 1117: * the NIC.
! 1118: */
! 1119: hdrp->rsr = bus_space_read_1(buft, bufh, src);
! 1120: hdrp->next_packet = bus_space_read_1(buft, bufh, src + 1);
! 1121: hdrp->count = bus_space_read_1(buft, bufh, src + 2) |
! 1122: (bus_space_read_1(buft, bufh, src + 3) << 8);
! 1123: }
! 1124:
! 1125: /*
! 1126: * Copy `amount' bytes from a packet in the ring buffer to a linear
! 1127: * destination buffer, given a source offset and destination address.
! 1128: * Takes into account ring-wrap.
! 1129: */
! 1130: static __inline__ int
! 1131: dp8390_ring_copy(struct dp8390_softc *sc, int src, caddr_t dst, u_short amount)
! 1132: {
! 1133: bus_space_tag_t buft = sc->sc_buft;
! 1134: bus_space_handle_t bufh = sc->sc_bufh;
! 1135: u_short tmp_amount;
! 1136:
! 1137: /* Does copy wrap to lower addr in ring buffer? */
! 1138: if (src + amount > sc->mem_end) {
! 1139: tmp_amount = sc->mem_end - src;
! 1140:
! 1141: /* Copy amount up to end of NIC memory. */
! 1142: bus_space_read_region_1(buft, bufh, src, dst, tmp_amount);
! 1143:
! 1144: amount -= tmp_amount;
! 1145: src = sc->mem_ring;
! 1146: dst += tmp_amount;
! 1147: }
! 1148: bus_space_read_region_1(buft, bufh, src, dst, amount);
! 1149:
! 1150: return (src + amount);
! 1151: }
! 1152:
! 1153: /*
! 1154: * Copy a packet from an mbuf to the transmit buffer on the card.
! 1155: *
! 1156: * Currently uses an extra buffer/extra memory copy, unless the whole
! 1157: * packet fits in one mbuf.
! 1158: */
! 1159: static __inline__ int
! 1160: dp8390_write_mbuf(struct dp8390_softc *sc, struct mbuf *m, int buf)
! 1161: {
! 1162: bus_space_tag_t buft = sc->sc_buft;
! 1163: bus_space_handle_t bufh = sc->sc_bufh;
! 1164: u_char *data;
! 1165: int len, totlen = 0;
! 1166:
! 1167: for (; m ; m = m->m_next) {
! 1168: data = mtod(m, u_char *);
! 1169: len = m->m_len;
! 1170: if (len > 0) {
! 1171: bus_space_write_region_1(buft, bufh, buf, data, len);
! 1172: totlen += len;
! 1173: buf += len;
! 1174: }
! 1175: }
! 1176:
! 1177: return (totlen);
! 1178: }
! 1179:
! 1180: /*
! 1181: * Enable power on the interface.
! 1182: */
! 1183: int
! 1184: dp8390_enable(struct dp8390_softc *sc)
! 1185: {
! 1186:
! 1187: if (sc->sc_enabled == 0 && sc->sc_enable != NULL) {
! 1188: if ((*sc->sc_enable)(sc) != 0) {
! 1189: printf("%s: device enable failed\n",
! 1190: sc->sc_dev.dv_xname);
! 1191: return (EIO);
! 1192: }
! 1193: }
! 1194:
! 1195: sc->sc_enabled = 1;
! 1196: return (0);
! 1197: }
! 1198:
! 1199: /*
! 1200: * Disable power on the interface.
! 1201: */
! 1202: void
! 1203: dp8390_disable(struct dp8390_softc *sc)
! 1204: {
! 1205: if (sc->sc_enabled != 0 && sc->sc_disable != NULL) {
! 1206: (*sc->sc_disable)(sc);
! 1207: sc->sc_enabled = 0;
! 1208: }
! 1209: }
! 1210:
! 1211: int
! 1212: dp8390_detach(struct dp8390_softc *sc, int flags)
! 1213: {
! 1214: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 1215:
! 1216: /* dp8390_disable() checks sc->sc_enabled */
! 1217: dp8390_disable(sc);
! 1218:
! 1219: if (sc->sc_media_fini != NULL)
! 1220: (*sc->sc_media_fini)(sc);
! 1221:
! 1222: /* Delete all reamining media. */
! 1223: ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
! 1224:
! 1225: ether_ifdetach(ifp);
! 1226: if_detach(ifp);
! 1227:
! 1228: return (0);
! 1229: }
CVSweb