1*41003Swilliam /*- 2*41003Swilliam * Copyright (c) 1990 The Regents of the University of California. 3*41003Swilliam * All rights reserved. 4*41003Swilliam * 5*41003Swilliam * This code is derived from software contributed to Berkeley by 6*41003Swilliam * Tim L. Tucker 7*41003Swilliam * 8*41003Swilliam * %sccs.include.noredist.c% 9*41003Swilliam * 10*41003Swilliam * @(#)if_we.c 5.1 (Berkeley) 04/22/90 11*41003Swilliam */ 12*41003Swilliam 13*41003Swilliam /* 14*41003Swilliam * Modification history 15*41003Swilliam * 16*41003Swilliam * 8/28/89 - Initial version, Tim L Tucker 17*41003Swilliam */ 18*41003Swilliam 19*41003Swilliam #include "wd.h" 20*41003Swilliam #if NWD > 0 21*41003Swilliam /* 22*41003Swilliam * Western Digital 8003 ethernet/starlan adapter 23*41003Swilliam * 24*41003Swilliam * Supports the following interface cards: 25*41003Swilliam * WD8003E, WD8003EBT, WD8003S, WD8003SBT 26*41003Swilliam * 27*41003Swilliam * The Western Digital card is one of many AT/MCA ethernet interfaces 28*41003Swilliam * based on the National N8390/NS32490 Network Interface chip set. 29*41003Swilliam */ 30*41003Swilliam #include "param.h" 31*41003Swilliam #include "mbuf.h" 32*41003Swilliam #include "socket.h" 33*41003Swilliam #include "ioctl.h" 34*41003Swilliam #include "errno.h" 35*41003Swilliam #include "syslog.h" 36*41003Swilliam 37*41003Swilliam #include "../net/if.h" 38*41003Swilliam #include "../net/netisr.h" 39*41003Swilliam 40*41003Swilliam #ifdef INET 41*41003Swilliam #include "../netinet/in.h" 42*41003Swilliam #include "../netinet/in_systm.h" 43*41003Swilliam #include "../netinet/in_var.h" 44*41003Swilliam #include "../netinet/ip.h" 45*41003Swilliam #include "../netinet/if_ether.h" 46*41003Swilliam #endif 47*41003Swilliam 48*41003Swilliam #ifdef NS 49*41003Swilliam #include "../netns/ns.h" 50*41003Swilliam #include "../netns/ns_if.h" 51*41003Swilliam #endif 52*41003Swilliam 53*41003Swilliam #include "if_wdreg.h" 54*41003Swilliam #include "../isa/isavar.h" 55*41003Swilliam 56*41003Swilliam /* 57*41003Swilliam * This constant should really be 60 because the wd adds 4 bytes of crc. 58*41003Swilliam * However when set to 60 our packets are ignored by deuna's , 3coms are 59*41003Swilliam * okay ?????????????????????????????????????????? 60*41003Swilliam */ 61*41003Swilliam #define ETHER_MIN_LEN 64 62*41003Swilliam #define ETHER_ADDR_LEN 6 63*41003Swilliam #define ETHER_HDR_SIZE 14 64*41003Swilliam 65*41003Swilliam /* 66*41003Swilliam * Ethernet software status per interface. 67*41003Swilliam * 68*41003Swilliam * Each interface is referenced by a network interface structure, 69*41003Swilliam * qe_if, which the routing code uses to locate the interface. 70*41003Swilliam * This structure contains the output queue for the interface, its address, ... 71*41003Swilliam */ 72*41003Swilliam struct wd_softc { 73*41003Swilliam struct arpcom wd_ac; /* Ethernet common part */ 74*41003Swilliam #define wd_if wd_ac.ac_if /* network-visible interface */ 75*41003Swilliam #define wd_addr wd_ac.ac_enaddr /* hardware Ethernet address */ 76*41003Swilliam 77*41003Swilliam u_char wd_flags; /* software state */ 78*41003Swilliam #define WDF_RUNNING 0x01 79*41003Swilliam #define WDF_TXBUSY 0x02 80*41003Swilliam 81*41003Swilliam u_char wd_type; /* interface type code */ 82*41003Swilliam u_short wd_vector; /* interrupt vector */ 83*41003Swilliam caddr_t wd_io_ctl_addr; /* i/o bus address, control */ 84*41003Swilliam caddr_t wd_io_nic_addr; /* i/o bus address, NS32490 */ 85*41003Swilliam 86*41003Swilliam caddr_t wd_vmem_addr; /* card RAM virtual memory base */ 87*41003Swilliam u_long wd_vmem_size; /* card RAM bytes */ 88*41003Swilliam caddr_t wd_vmem_ring; /* receive ring RAM vaddress */ 89*41003Swilliam } wd_softc[NWD]; 90*41003Swilliam 91*41003Swilliam int wdprobe(), wdattach(), wdintr(); 92*41003Swilliam int wdinit(), wdoutput(), wdioctl(), wdreset(); 93*41003Swilliam 94*41003Swilliam /* 95*41003Swilliam * Probe the WD8003 to see if it's there 96*41003Swilliam */ 97*41003Swilliam wdprobe(reg, is) 98*41003Swilliam caddr_t reg; 99*41003Swilliam struct isa_device *is; 100*41003Swilliam { 101*41003Swilliam register int i; 102*41003Swilliam register struct wd_softc *sc = &wd_softc[is->is_unit]; 103*41003Swilliam union wd_mem_sel wdm; 104*41003Swilliam u_char sum; 105*41003Swilliam 106*41003Swilliam /* 107*41003Swilliam * Here we check the card ROM, if the checksum passes, and the 108*41003Swilliam * type code and ethernet address check out, then we know we have 109*41003Swilliam * a wd8003 card. 110*41003Swilliam * 111*41003Swilliam * Autoconfiguration: No warning message is printed on error. 112*41003Swilliam */ 113*41003Swilliam for (sum = 0, i = 0; i < 8; ++i) 114*41003Swilliam sum += INB(reg + WD_ROM_OFFSET + i); 115*41003Swilliam if (sum != WD_CHECKSUM) 116*41003Swilliam return (0); 117*41003Swilliam sc->wd_type = INB(reg + WD_ROM_OFFSET + 6); 118*41003Swilliam if ((sc->wd_type != WD_ETHER) && (sc->wd_type != WD_STARLAN)) 119*41003Swilliam return (0); 120*41003Swilliam 121*41003Swilliam /* 122*41003Swilliam * Setup card RAM area and i/o addresses 123*41003Swilliam * Kernel Virtual to segment C0000-DFFFF????? 124*41003Swilliam */ 125*41003Swilliam sc->wd_io_ctl_addr = reg; 126*41003Swilliam sc->wd_io_nic_addr = sc->wd_io_ctl_addr + WD_NIC_OFFSET; 127*41003Swilliam sc->wd_vector = is->is_vector; 128*41003Swilliam sc->wd_vmem_addr = (caddr_t)is->is_mem; 129*41003Swilliam sc->wd_vmem_size = is->is_memsize; 130*41003Swilliam sc->wd_vmem_ring = sc->wd_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE); 131*41003Swilliam 132*41003Swilliam /* 133*41003Swilliam * Save board ROM station address 134*41003Swilliam */ 135*41003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i) 136*41003Swilliam sc->wd_addr[i] = INB(sc->wd_io_ctl_addr + WD_ROM_OFFSET + i); 137*41003Swilliam 138*41003Swilliam /* 139*41003Swilliam * Mapin interface memory, setup memory select register 140*41003Swilliam */ 141*41003Swilliam wdm.ms_addr = (u_long)sc->wd_vmem_addr >> 13; 142*41003Swilliam wdm.ms_enable = 1; 143*41003Swilliam wdm.ms_reset = 0; 144*41003Swilliam OUTB(sc->wd_io_ctl_addr, wdm.ms_byte); 145*41003Swilliam 146*41003Swilliam /* 147*41003Swilliam * clear interface memory, then sum to make sure its valid 148*41003Swilliam */ 149*41003Swilliam for (i = 0; i < sc->wd_vmem_size; ++i) 150*41003Swilliam sc->wd_vmem_addr[i] = 0x0; 151*41003Swilliam for (sum = 0, i = 0; i < sc->wd_vmem_size; ++i) 152*41003Swilliam sum += sc->wd_vmem_addr[i]; 153*41003Swilliam if (sum != 0x0) { 154*41003Swilliam printf("wd%d: wd8003 dual port RAM address error\n", is->is_unit); 155*41003Swilliam return (0); 156*41003Swilliam } 157*41003Swilliam 158*41003Swilliam return (WD_IO_PORTS); 159*41003Swilliam } 160*41003Swilliam 161*41003Swilliam /* 162*41003Swilliam * Interface exists: make available by filling in network interface 163*41003Swilliam * record. System will initialize the interface when it is ready 164*41003Swilliam * to accept packets. 165*41003Swilliam */ 166*41003Swilliam wdattach(is) 167*41003Swilliam struct isa_device *is; 168*41003Swilliam { 169*41003Swilliam register struct wd_softc *sc = &wd_softc[is->is_unit]; 170*41003Swilliam register struct ifnet *ifp = &sc->wd_if; 171*41003Swilliam 172*41003Swilliam /* 173*41003Swilliam * Initialize ifnet structure 174*41003Swilliam */ 175*41003Swilliam ifp->if_unit = is->is_unit; 176*41003Swilliam ifp->if_name = "wd"; 177*41003Swilliam ifp->if_mtu = ETHERMTU; 178*41003Swilliam ifp->if_flags = IFF_BROADCAST|IFF_NOTRAILERS; 179*41003Swilliam ifp->if_init = wdinit; 180*41003Swilliam ifp->if_output = wdoutput; 181*41003Swilliam ifp->if_ioctl = wdioctl; 182*41003Swilliam ifp->if_reset = wdreset; 183*41003Swilliam ifp->if_watchdog = 0; 184*41003Swilliam if_attach(ifp); 185*41003Swilliam 186*41003Swilliam /* 187*41003Swilliam * Banner... 188*41003Swilliam */ 189*41003Swilliam printf("wd%d: %s, hardware address %s\n", is->is_unit, 190*41003Swilliam ((sc->wd_type == WD_ETHER) ? "ethernet" : "starlan"), 191*41003Swilliam ether_sprintf(sc->wd_addr)); 192*41003Swilliam } 193*41003Swilliam 194*41003Swilliam /* 195*41003Swilliam * Reset of interface. 196*41003Swilliam */ 197*41003Swilliam wdreset(unit, uban) 198*41003Swilliam int unit, uban; 199*41003Swilliam { 200*41003Swilliam if (unit >= NWD) 201*41003Swilliam return; 202*41003Swilliam printf("wd%d: reset\n", unit); 203*41003Swilliam wd_softc[unit].wd_flags &= ~WDF_RUNNING; 204*41003Swilliam wdinit(unit); 205*41003Swilliam } 206*41003Swilliam 207*41003Swilliam /* 208*41003Swilliam * Take interface offline. 209*41003Swilliam */ 210*41003Swilliam wdstop(unit) 211*41003Swilliam int unit; 212*41003Swilliam { 213*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 214*41003Swilliam union wd_command wdcmd; 215*41003Swilliam int s; 216*41003Swilliam 217*41003Swilliam /* 218*41003Swilliam * Shutdown NS32490 219*41003Swilliam */ 220*41003Swilliam s = splimp(); 221*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 222*41003Swilliam wdcmd.cs_stp = 1; 223*41003Swilliam wdcmd.cs_sta = 0; 224*41003Swilliam wdcmd.cs_ps = 0; 225*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 226*41003Swilliam (void) splx(s); 227*41003Swilliam } 228*41003Swilliam 229*41003Swilliam /* 230*41003Swilliam * Initialization of interface (really just NS32490). 231*41003Swilliam */ 232*41003Swilliam wdinit(unit) 233*41003Swilliam int unit; 234*41003Swilliam { 235*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 236*41003Swilliam register struct ifnet *ifp = &sc->wd_if; 237*41003Swilliam union wd_command wdcmd; 238*41003Swilliam int i, s; 239*41003Swilliam 240*41003Swilliam /* address not known */ 241*41003Swilliam if (ifp->if_addrlist == (struct ifaddr *)0) 242*41003Swilliam return; 243*41003Swilliam 244*41003Swilliam /* already running */ 245*41003Swilliam if (sc->wd_flags & WDF_RUNNING) 246*41003Swilliam return; 247*41003Swilliam 248*41003Swilliam /* 249*41003Swilliam * Initialize NS32490 in order given in NSC NIC manual. 250*41003Swilliam * this is stock code...please see the National manual for details. 251*41003Swilliam */ 252*41003Swilliam s = splhi(); 253*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 254*41003Swilliam wdcmd.cs_stp = 1; 255*41003Swilliam wdcmd.cs_sta = 0; 256*41003Swilliam wdcmd.cs_ps = 0; 257*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 258*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_DCR, WD_D_CONFIG); 259*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_RBCR0, 0); 260*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_RBCR1, 0); 261*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_RCR, WD_R_MON); 262*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_TCR, WD_T_CONFIG); 263*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_TPSR, 0); 264*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE); 265*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_PSTOP, 266*41003Swilliam sc->wd_vmem_size / WD_PAGE_SIZE); 267*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE); 268*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_ISR, 0xff); 269*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_IMR, WD_I_CONFIG); 270*41003Swilliam wdcmd.cs_ps = 1; 271*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 272*41003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i) 273*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P1_PAR0 + i, sc->wd_addr[i]); 274*41003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */ 275*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P1_MAR0 + i, 0xff); 276*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE); 277*41003Swilliam wdcmd.cs_ps = 0; 278*41003Swilliam wdcmd.cs_stp = 0; 279*41003Swilliam wdcmd.cs_sta = 1; 280*41003Swilliam wdcmd.cs_rd = 0x4; 281*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P1_COMMAND, wdcmd.cs_byte); 282*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_RCR, WD_R_CONFIG); 283*41003Swilliam 284*41003Swilliam /* 285*41003Swilliam * Take the interface out of reset, program the vector, 286*41003Swilliam * enable interrupts, and tell the world we are up. 287*41003Swilliam */ 288*41003Swilliam ifp->if_flags |= IFF_UP | IFF_RUNNING; 289*41003Swilliam sc->wd_flags |= WDF_RUNNING; 290*41003Swilliam sc->wd_flags &= ~WDF_TXBUSY; 291*41003Swilliam (void) splx(s); 292*41003Swilliam wdstart(unit); 293*41003Swilliam } 294*41003Swilliam 295*41003Swilliam /* 296*41003Swilliam * Start output on interface. 297*41003Swilliam */ 298*41003Swilliam wdstart(unit) 299*41003Swilliam int unit; 300*41003Swilliam { 301*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 302*41003Swilliam struct mbuf *m0, *m; 303*41003Swilliam register caddr_t buffer; 304*41003Swilliam int len = 0, s; 305*41003Swilliam union wd_command wdcmd; 306*41003Swilliam 307*41003Swilliam /* 308*41003Swilliam * The NS32490 has only one transmit buffer, if it is busy we 309*41003Swilliam * must wait until the transmit interrupt completes. 310*41003Swilliam */ 311*41003Swilliam s = splhi(); 312*41003Swilliam if (sc->wd_flags & WDF_TXBUSY) { 313*41003Swilliam (void) splx(s); 314*41003Swilliam return; 315*41003Swilliam } 316*41003Swilliam IF_DEQUEUE(&sc->wd_if.if_snd, m); 317*41003Swilliam if (m == 0) { 318*41003Swilliam (void) splx(s); 319*41003Swilliam return; 320*41003Swilliam } 321*41003Swilliam sc->wd_flags |= WDF_TXBUSY; 322*41003Swilliam (void) splx(s); 323*41003Swilliam 324*41003Swilliam /* 325*41003Swilliam * Copy the mbuf chain into the transmit buffer 326*41003Swilliam */ 327*41003Swilliam buffer = sc->wd_vmem_addr; 328*41003Swilliam for (m0 = m; m != 0; m = m->m_next) { 329*41003Swilliam bcopy(mtod(m, caddr_t), buffer, m->m_len); 330*41003Swilliam buffer += m->m_len; 331*41003Swilliam len += m->m_len; 332*41003Swilliam } 333*41003Swilliam 334*41003Swilliam /* 335*41003Swilliam * If this was a broadcast packet loop it 336*41003Swilliam * back because the hardware can't hear its own 337*41003Swilliam * transmits. 338*41003Swilliam */ 339*41003Swilliam if (bcmp((caddr_t)(mtod(m0, struct ether_header *)->ether_dhost), 340*41003Swilliam (caddr_t)etherbroadcastaddr, 341*41003Swilliam sizeof(etherbroadcastaddr)) == 0) { 342*41003Swilliam wdread(sc, m0); 343*41003Swilliam } else { 344*41003Swilliam m_freem(m0); 345*41003Swilliam } 346*41003Swilliam 347*41003Swilliam /* 348*41003Swilliam * Init transmit length registers, and set transmit start flag. 349*41003Swilliam */ 350*41003Swilliam s = splhi(); 351*41003Swilliam len = MAX(len, ETHER_MIN_LEN); 352*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 353*41003Swilliam wdcmd.cs_ps = 0; 354*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 355*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_TBCR0, len & 0xff); 356*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_TBCR1, len >> 8); 357*41003Swilliam wdcmd.cs_txp = 1; 358*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 359*41003Swilliam (void) splx(s); 360*41003Swilliam } 361*41003Swilliam 362*41003Swilliam /* 363*41003Swilliam * Ethernet interface interrupt processor 364*41003Swilliam */ 365*41003Swilliam wdintr(unit) 366*41003Swilliam int unit; 367*41003Swilliam { 368*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 369*41003Swilliam union wd_command wdcmd; 370*41003Swilliam union wd_interrupt wdisr; 371*41003Swilliam int s; 372*41003Swilliam 373*41003Swilliam /* disable onboard interrupts, then get interrupt status */ 374*41003Swilliam s = splhi(); 375*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 376*41003Swilliam wdcmd.cs_ps = 0; 377*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 378*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_IMR, 0); 379*41003Swilliam wdisr.is_byte = INB(sc->wd_io_nic_addr + WD_P0_ISR); 380*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_ISR, 0xff); 381*41003Swilliam (void) splx(s); 382*41003Swilliam 383*41003Swilliam /* transmit error */ 384*41003Swilliam if (wdisr.is_txe) { 385*41003Swilliam /* need to read these registers to clear status */ 386*41003Swilliam sc->wd_if.if_collisions += 387*41003Swilliam INB(sc->wd_io_nic_addr + WD_P0_TBCR0); 388*41003Swilliam ++sc->wd_if.if_oerrors; 389*41003Swilliam } 390*41003Swilliam 391*41003Swilliam /* receiver error */ 392*41003Swilliam if (wdisr.is_rxe) { 393*41003Swilliam /* need to read these registers to clear status */ 394*41003Swilliam (void) INB(sc->wd_io_nic_addr + 0xD); 395*41003Swilliam (void) INB(sc->wd_io_nic_addr + 0xE); 396*41003Swilliam (void) INB(sc->wd_io_nic_addr + 0xF); 397*41003Swilliam ++sc->wd_if.if_ierrors; 398*41003Swilliam } 399*41003Swilliam 400*41003Swilliam /* normal transmit complete */ 401*41003Swilliam if (wdisr.is_ptx) 402*41003Swilliam wdtint (unit); 403*41003Swilliam 404*41003Swilliam /* normal receive notification */ 405*41003Swilliam if (wdisr.is_prx) 406*41003Swilliam wdrint (unit); 407*41003Swilliam 408*41003Swilliam /* try to start transmit */ 409*41003Swilliam wdstart(unit); 410*41003Swilliam 411*41003Swilliam /* re-enable onboard interrupts */ 412*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 413*41003Swilliam wdcmd.cs_ps = 0; 414*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 415*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_IMR, WD_I_CONFIG); 416*41003Swilliam } 417*41003Swilliam 418*41003Swilliam /* 419*41003Swilliam * Ethernet interface transmit interrupt. 420*41003Swilliam */ 421*41003Swilliam wdtint(unit) 422*41003Swilliam int unit; 423*41003Swilliam { 424*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 425*41003Swilliam 426*41003Swilliam /* 427*41003Swilliam * Do some statistics (assume page zero of NIC mapped in) 428*41003Swilliam */ 429*41003Swilliam sc->wd_flags &= ~WDF_TXBUSY; 430*41003Swilliam sc->wd_if.if_timer = 0; 431*41003Swilliam ++sc->wd_if.if_opackets; 432*41003Swilliam sc->wd_if.if_collisions += INB(sc->wd_io_nic_addr + WD_P0_TBCR0); 433*41003Swilliam } 434*41003Swilliam 435*41003Swilliam /* 436*41003Swilliam * Ethernet interface receiver interrupt. 437*41003Swilliam */ 438*41003Swilliam wdrint(unit) 439*41003Swilliam int unit; 440*41003Swilliam { 441*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 442*41003Swilliam register struct mbuf **m; 443*41003Swilliam int mlen, len, count; 444*41003Swilliam u_char bnry, curr; 445*41003Swilliam union wd_command wdcmd; 446*41003Swilliam struct wd_ring *wdr; 447*41003Swilliam struct mbuf *m0; 448*41003Swilliam caddr_t pkt, endp; 449*41003Swilliam 450*41003Swilliam /* 451*41003Swilliam * Traverse the receive ring looking for packets to pass back. 452*41003Swilliam * The search is complete when we find a descriptor not in use. 453*41003Swilliam */ 454*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 455*41003Swilliam wdcmd.cs_ps = 0; 456*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 457*41003Swilliam bnry = INB(sc->wd_io_nic_addr + WD_P0_BNRY); 458*41003Swilliam wdcmd.cs_ps = 1; 459*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 460*41003Swilliam curr = INB(sc->wd_io_nic_addr + WD_P1_CURR); 461*41003Swilliam while (bnry != curr) 462*41003Swilliam { 463*41003Swilliam /* get pointer to this buffer header structure */ 464*41003Swilliam wdr = (struct wd_ring *)(sc->wd_vmem_addr + (bnry << 8)); 465*41003Swilliam len = wdr->wd_count - 4; /* count includes CRC */ 466*41003Swilliam pkt = (caddr_t)(wdr + 1) - 2; /* 2 - word align pkt data */ 467*41003Swilliam count = len + 2; /* copy two extra bytes */ 468*41003Swilliam endp = (caddr_t)(sc->wd_vmem_addr + sc->wd_vmem_size); 469*41003Swilliam ++sc->wd_if.if_ipackets; 470*41003Swilliam 471*41003Swilliam /* pull packet out of dual ported RAM */ 472*41003Swilliam m = &m0; m0 = 0; 473*41003Swilliam while (count > 0) 474*41003Swilliam { 475*41003Swilliam /* drop chain if can't get another buffer */ 476*41003Swilliam MGET(*m, M_DONTWAIT, MT_DATA); 477*41003Swilliam if (*m == 0) 478*41003Swilliam { 479*41003Swilliam m_freem(m0); 480*41003Swilliam goto outofbufs; 481*41003Swilliam } 482*41003Swilliam 483*41003Swilliam /* fill mbuf and attach to packet list */ 484*41003Swilliam mlen = MIN(MLEN, count); 485*41003Swilliam mlen = MIN(mlen, endp - pkt); 486*41003Swilliam bcopy(pkt, mtod(*m, caddr_t), mlen); 487*41003Swilliam (*m)->m_len = mlen; 488*41003Swilliam m = &((*m)->m_next); 489*41003Swilliam pkt += mlen; 490*41003Swilliam count -= mlen; 491*41003Swilliam 492*41003Swilliam /* wrap memory pointer around circ buffer */ 493*41003Swilliam if (pkt == endp) 494*41003Swilliam pkt = (caddr_t)sc->wd_vmem_ring; 495*41003Swilliam } 496*41003Swilliam 497*41003Swilliam /* skip aligment bytes, send packet up to higher levels */ 498*41003Swilliam if (m0 != 0) 499*41003Swilliam { 500*41003Swilliam m0->m_off += 2; 501*41003Swilliam wdread(sc, m0); 502*41003Swilliam } 503*41003Swilliam 504*41003Swilliam outofbufs: 505*41003Swilliam /* advance on chip Boundry register */ 506*41003Swilliam bnry = wdr->wd_next_packet; 507*41003Swilliam wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND); 508*41003Swilliam wdcmd.cs_ps = 0; 509*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 510*41003Swilliam 511*41003Swilliam /* watch out for NIC overflow, reset Boundry if invalid */ 512*41003Swilliam if ((bnry - 1) < WD_TXBUF_SIZE) { 513*41003Swilliam #ifdef notdef 514*41003Swilliam wdreset(unit, 0); 515*41003Swilliam break; 516*41003Swilliam #else 517*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_BNRY, 518*41003Swilliam (sc->wd_vmem_size / WD_PAGE_SIZE) - 1); 519*41003Swilliam #endif 520*41003Swilliam } 521*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_BNRY, bnry - 1); 522*41003Swilliam 523*41003Swilliam /* refresh our copy of CURR */ 524*41003Swilliam wdcmd.cs_ps = 1; 525*41003Swilliam OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte); 526*41003Swilliam curr = INB(sc->wd_io_nic_addr + WD_P1_CURR); 527*41003Swilliam } 528*41003Swilliam } 529*41003Swilliam 530*41003Swilliam /* 531*41003Swilliam * Ethernet output routine. 532*41003Swilliam * Encapsulate a packet of type family for the local net. 533*41003Swilliam */ 534*41003Swilliam wdoutput(ifp, m0, dst) 535*41003Swilliam struct ifnet *ifp; 536*41003Swilliam struct mbuf *m0; 537*41003Swilliam struct sockaddr *dst; 538*41003Swilliam { 539*41003Swilliam int type, s, error; 540*41003Swilliam u_char edst[6]; 541*41003Swilliam struct in_addr idst; 542*41003Swilliam register struct wd_softc *sc = &wd_softc[ifp->if_unit]; 543*41003Swilliam register struct mbuf *m = m0; 544*41003Swilliam register struct ether_header *eh; 545*41003Swilliam int usetrailers; 546*41003Swilliam 547*41003Swilliam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 548*41003Swilliam error = ENETDOWN; 549*41003Swilliam goto bad; 550*41003Swilliam } 551*41003Swilliam 552*41003Swilliam switch (dst->sa_family) { 553*41003Swilliam 554*41003Swilliam #ifdef INET 555*41003Swilliam case AF_INET: 556*41003Swilliam /* Note: we ignore usetrailers */ 557*41003Swilliam idst = ((struct sockaddr_in *)dst)->sin_addr; 558*41003Swilliam if (!arpresolve(&sc->wd_ac, m, &idst, edst, &usetrailers)) 559*41003Swilliam return (0); /* if not yet resolved */ 560*41003Swilliam type = ETHERTYPE_IP; 561*41003Swilliam break; 562*41003Swilliam #endif 563*41003Swilliam #ifdef NS 564*41003Swilliam case AF_NS: 565*41003Swilliam type = ETHERTYPE_NS; 566*41003Swilliam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 567*41003Swilliam (caddr_t)edst, sizeof (edst)); 568*41003Swilliam break; 569*41003Swilliam #endif 570*41003Swilliam 571*41003Swilliam 572*41003Swilliam case AF_UNSPEC: 573*41003Swilliam eh = (struct ether_header *)dst->sa_data; 574*41003Swilliam bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); 575*41003Swilliam type = eh->ether_type; 576*41003Swilliam break; 577*41003Swilliam 578*41003Swilliam default: 579*41003Swilliam printf("wd%d: can't handle af%d\n", ifp->if_unit, 580*41003Swilliam dst->sa_family); 581*41003Swilliam error = EAFNOSUPPORT; 582*41003Swilliam goto bad; 583*41003Swilliam } 584*41003Swilliam 585*41003Swilliam /* 586*41003Swilliam * Add local net header. If no space in first mbuf, 587*41003Swilliam * allocate another. 588*41003Swilliam */ 589*41003Swilliam if (m->m_off > MMAXOFF || MMINOFF + ETHER_HDR_SIZE > m->m_off) { 590*41003Swilliam m = m_get(M_DONTWAIT, MT_HEADER); 591*41003Swilliam if (m == 0) { 592*41003Swilliam error = ENOBUFS; 593*41003Swilliam goto bad; 594*41003Swilliam } 595*41003Swilliam m->m_next = m0; 596*41003Swilliam m->m_off = MMINOFF; 597*41003Swilliam m->m_len = ETHER_HDR_SIZE; 598*41003Swilliam } else { 599*41003Swilliam m->m_off -= ETHER_HDR_SIZE; 600*41003Swilliam m->m_len += ETHER_HDR_SIZE; 601*41003Swilliam } 602*41003Swilliam eh = mtod(m, struct ether_header *); 603*41003Swilliam eh->ether_type = htons((u_short)type); 604*41003Swilliam bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); 605*41003Swilliam bcopy((caddr_t)sc->wd_addr, (caddr_t)eh->ether_shost, 606*41003Swilliam sizeof (sc->wd_addr)); 607*41003Swilliam 608*41003Swilliam /* 609*41003Swilliam * Queue message on interface, and start output if interface 610*41003Swilliam * not yet active. 611*41003Swilliam */ 612*41003Swilliam s = splimp(); 613*41003Swilliam if (IF_QFULL(&ifp->if_snd)) { 614*41003Swilliam IF_DROP(&ifp->if_snd); 615*41003Swilliam (void) splx(s); 616*41003Swilliam m_freem(m); 617*41003Swilliam return (ENOBUFS); 618*41003Swilliam } 619*41003Swilliam IF_ENQUEUE(&ifp->if_snd, m); 620*41003Swilliam (void) splx(s); 621*41003Swilliam wdstart(ifp->if_unit); 622*41003Swilliam return (0); 623*41003Swilliam 624*41003Swilliam bad: 625*41003Swilliam m_freem(m0); 626*41003Swilliam return (error); 627*41003Swilliam } 628*41003Swilliam 629*41003Swilliam /* 630*41003Swilliam * Process an ioctl request. 631*41003Swilliam */ 632*41003Swilliam wdioctl(ifp, cmd, data) 633*41003Swilliam register struct ifnet *ifp; 634*41003Swilliam int cmd; 635*41003Swilliam caddr_t data; 636*41003Swilliam { 637*41003Swilliam struct wd_softc *sc = &wd_softc[ifp->if_unit]; 638*41003Swilliam struct ifaddr *ifa = (struct ifaddr *)data; 639*41003Swilliam int s = splimp(), error = 0; 640*41003Swilliam 641*41003Swilliam switch (cmd) { 642*41003Swilliam 643*41003Swilliam case SIOCSIFADDR: 644*41003Swilliam ifp->if_flags |= IFF_UP; 645*41003Swilliam wdinit(ifp->if_unit); 646*41003Swilliam switch(ifa->ifa_addr.sa_family) { 647*41003Swilliam #ifdef INET 648*41003Swilliam case AF_INET: 649*41003Swilliam ((struct arpcom *)ifp)->ac_ipaddr = 650*41003Swilliam IA_SIN(ifa)->sin_addr; 651*41003Swilliam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 652*41003Swilliam break; 653*41003Swilliam #endif 654*41003Swilliam #ifdef NS 655*41003Swilliam case AF_NS: 656*41003Swilliam { 657*41003Swilliam register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 658*41003Swilliam 659*41003Swilliam if (ns_nullhost(*ina)) 660*41003Swilliam ina->x_host = *(union ns_host *)(sc->wd_addr); 661*41003Swilliam else 662*41003Swilliam wdsetaddr(ina->x_host.c_host, ifp->if_unit); 663*41003Swilliam break; 664*41003Swilliam } 665*41003Swilliam #endif 666*41003Swilliam } 667*41003Swilliam break; 668*41003Swilliam 669*41003Swilliam case SIOCSIFFLAGS: 670*41003Swilliam if (((ifp->if_flags & IFF_UP) == 0) && 671*41003Swilliam (sc->wd_flags & WDF_RUNNING)) { 672*41003Swilliam wdstop(ifp->if_unit); 673*41003Swilliam } else if (((ifp->if_flags & IFF_UP) == IFF_UP) && 674*41003Swilliam ((sc->wd_flags & WDF_RUNNING) == 0)) 675*41003Swilliam wdinit(ifp->if_unit); 676*41003Swilliam break; 677*41003Swilliam 678*41003Swilliam default: 679*41003Swilliam error = EINVAL; 680*41003Swilliam 681*41003Swilliam } 682*41003Swilliam (void) splx(s); 683*41003Swilliam return (error); 684*41003Swilliam } 685*41003Swilliam 686*41003Swilliam /* 687*41003Swilliam * set ethernet address for unit 688*41003Swilliam */ 689*41003Swilliam wdsetaddr(physaddr, unit) 690*41003Swilliam u_char *physaddr; 691*41003Swilliam int unit; 692*41003Swilliam { 693*41003Swilliam register struct wd_softc *sc = &wd_softc[unit]; 694*41003Swilliam register int i; 695*41003Swilliam 696*41003Swilliam /* 697*41003Swilliam * Rewrite ethernet address, and then force restart of NIC 698*41003Swilliam */ 699*41003Swilliam for (i = 0; i < ETHER_ADDR_LEN; i++) 700*41003Swilliam sc->wd_addr[i] = physaddr[i]; 701*41003Swilliam sc->wd_flags &= ~WDF_RUNNING; 702*41003Swilliam wdinit(unit); 703*41003Swilliam } 704*41003Swilliam 705*41003Swilliam /* 706*41003Swilliam * Pass a packet to the higher levels. 707*41003Swilliam * NO TRAILER PROTOCOL! 708*41003Swilliam */ 709*41003Swilliam wdread(sc, m) 710*41003Swilliam register struct wd_softc *sc; 711*41003Swilliam struct mbuf *m; 712*41003Swilliam { 713*41003Swilliam struct ether_header *eh; 714*41003Swilliam int scn, type, s; 715*41003Swilliam struct ifqueue *inq; 716*41003Swilliam 717*41003Swilliam /* 718*41003Swilliam * Get ethernet protocol type out of ether header 719*41003Swilliam */ 720*41003Swilliam eh = mtod(m, struct ether_header *); 721*41003Swilliam type = ntohs((u_short)eh->ether_type); 722*41003Swilliam 723*41003Swilliam /* 724*41003Swilliam * Drop ethernet header 725*41003Swilliam */ 726*41003Swilliam m->m_off += ETHER_HDR_SIZE; 727*41003Swilliam m->m_len -= ETHER_HDR_SIZE; 728*41003Swilliam 729*41003Swilliam /* 730*41003Swilliam * Insert ifp pointer at start of packet 731*41003Swilliam */ 732*41003Swilliam m->m_off -= sizeof (struct ifnet *); 733*41003Swilliam m->m_len += sizeof (struct ifnet *); 734*41003Swilliam *(mtod(m, struct ifnet **)) = &sc->wd_if; 735*41003Swilliam 736*41003Swilliam switch (type) { 737*41003Swilliam 738*41003Swilliam #ifdef INET 739*41003Swilliam case ETHERTYPE_IP: 740*41003Swilliam scn = NETISR_IP; 741*41003Swilliam inq = &ipintrq; 742*41003Swilliam break; 743*41003Swilliam 744*41003Swilliam case ETHERTYPE_ARP: 745*41003Swilliam arpinput(&sc->wd_ac, m); 746*41003Swilliam return; 747*41003Swilliam #endif 748*41003Swilliam #ifdef NS 749*41003Swilliam case ETHERTYPE_NS: 750*41003Swilliam scn = NETISR_NS; 751*41003Swilliam inq = &nsintrq; 752*41003Swilliam break; 753*41003Swilliam 754*41003Swilliam #endif 755*41003Swilliam 756*41003Swilliam default: 757*41003Swilliam m_freem(m); 758*41003Swilliam return; 759*41003Swilliam } 760*41003Swilliam 761*41003Swilliam s = splimp(); 762*41003Swilliam if (IF_QFULL(inq)) { 763*41003Swilliam IF_DROP(inq); 764*41003Swilliam m_freem(m); 765*41003Swilliam } else 766*41003Swilliam IF_ENQUEUE(inq, m); 767*41003Swilliam schednetisr(scn); 768*41003Swilliam (void) splx(s); 769*41003Swilliam } 770*41003Swilliam 771*41003Swilliam #endif 772