| 1 | /*- |
| 2 | * Copyright (c) 1990, 1991 William F. Jolitz. |
| 3 | * Copyright (c) 1990 The Regents of the University of California. |
| 4 | * All rights reserved. |
| 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without |
| 7 | * modification, are permitted provided that the following conditions |
| 8 | * are met: |
| 9 | * 1. Redistributions of source code must retain the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer. |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in the |
| 13 | * documentation and/or other materials provided with the distribution. |
| 14 | * 3. All advertising materials mentioning features or use of this software |
| 15 | * must display the following acknowledgement: |
| 16 | * This product includes software developed by the University of |
| 17 | * California, Berkeley and its contributors. |
| 18 | * 4. Neither the name of the University nor the names of its contributors |
| 19 | * may be used to endorse or promote products derived from this software |
| 20 | * without specific prior written permission. |
| 21 | * |
| 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 32 | * SUCH DAMAGE. |
| 33 | * |
| 34 | * @(#)if_ne.c 7.4 (Berkeley) 5/21/91 |
| 35 | */ |
| 36 | |
| 37 | /* |
| 38 | * NE2000 Ethernet driver |
| 39 | * |
| 40 | * Parts inspired from Tim Tucker's if_wd driver for the wd8003, |
| 41 | * insight on the ne2000 gained from Robert Clements PC/FTP driver. |
| 42 | */ |
| 43 | |
| 44 | #include "ne.h" |
| 45 | #if NNE > 0 |
| 46 | |
| 47 | #include "param.h" |
| 48 | #include "systm.h" |
| 49 | #include "mbuf.h" |
| 50 | #include "buf.h" |
| 51 | #include "protosw.h" |
| 52 | #include "socket.h" |
| 53 | #include "ioctl.h" |
| 54 | #include "errno.h" |
| 55 | #include "syslog.h" |
| 56 | |
| 57 | #include "net/if.h" |
| 58 | #include "net/netisr.h" |
| 59 | #include "net/route.h" |
| 60 | |
| 61 | #ifdef INET |
| 62 | #include "netinet/in.h" |
| 63 | #include "netinet/in_systm.h" |
| 64 | #include "netinet/in_var.h" |
| 65 | #include "netinet/ip.h" |
| 66 | #include "netinet/if_ether.h" |
| 67 | #endif |
| 68 | |
| 69 | #ifdef NS |
| 70 | #include "netns/ns.h" |
| 71 | #include "netns/ns_if.h" |
| 72 | #endif |
| 73 | |
| 74 | #include "i386/isa/isa_device.h" |
| 75 | #include "i386/isa/if_nereg.h" |
| 76 | #include "i386/isa/icu.h" |
| 77 | |
| 78 | int neprobe(), neattach(), neintr(); |
| 79 | int nestart(),neinit(), ether_output(), neioctl(); |
| 80 | |
| 81 | struct isa_driver nedriver = { |
| 82 | neprobe, neattach, "ne", |
| 83 | }; |
| 84 | |
| 85 | struct mbuf *neget(); |
| 86 | |
| 87 | #define ETHER_MIN_LEN 64 |
| 88 | #define ETHER_MAX_LEN 1536 |
| 89 | |
| 90 | /* |
| 91 | * Ethernet software status per interface. |
| 92 | * |
| 93 | * Each interface is referenced by a network interface structure, |
| 94 | * ns_if, which the routing code uses to locate the interface. |
| 95 | * This structure contains the output queue for the interface, its address, ... |
| 96 | */ |
| 97 | struct ne_softc { |
| 98 | struct arpcom ns_ac; /* Ethernet common part */ |
| 99 | #define ns_if ns_ac.ac_if /* network-visible interface */ |
| 100 | #define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */ |
| 101 | int ns_flags; |
| 102 | #define DSF_LOCK 1 /* block re-entering enstart */ |
| 103 | int ns_oactive ; |
| 104 | int ns_mask ; |
| 105 | int ns_ba; /* byte addr in buffer ram of inc pkt */ |
| 106 | int ns_cur; /* current page being filled */ |
| 107 | struct prhdr ns_ph; /* hardware header of incoming packet*/ |
| 108 | struct ether_header ns_eh; /* header of incoming packet */ |
| 109 | u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/]; |
| 110 | } ne_softc[NNE] ; |
| 111 | #define ENBUFSIZE (sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN) |
| 112 | |
| 113 | int nec; |
| 114 | |
| 115 | u_short boarddata[16]; |
| 116 | |
| 117 | neprobe(dvp) |
| 118 | struct isa_device *dvp; |
| 119 | { |
| 120 | int val,i,s; |
| 121 | register struct ne_softc *ns = &ne_softc[0]; |
| 122 | |
| 123 | #ifdef lint |
| 124 | neintr(0); |
| 125 | #endif |
| 126 | |
| 127 | nec = dvp->id_iobase; |
| 128 | s = splimp(); |
| 129 | |
| 130 | /* Reset the bastard */ |
| 131 | val = inb(nec+ne_reset); |
| 132 | DELAY(2000000); |
| 133 | outb(nec+ne_reset,val); |
| 134 | |
| 135 | outb(nec+ds_cmd, DSCM_STOP|DSCM_NODMA); |
| 136 | |
| 137 | i = 1000000; |
| 138 | while ((inb(nec+ds0_isr)&DSIS_RESET) == 0 && i-- > 0); |
| 139 | if (i < 0) return (0); |
| 140 | |
| 141 | outb(nec+ds0_isr, 0xff); |
| 142 | |
| 143 | /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */ |
| 144 | outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1); |
| 145 | |
| 146 | outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); |
| 147 | DELAY(10000); |
| 148 | |
| 149 | /* Check cmd reg and fail if not right */ |
| 150 | if ((i=inb(nec+ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP)) |
| 151 | return(0); |
| 152 | |
| 153 | outb(nec+ds0_tcr, 0); |
| 154 | outb(nec+ds0_rcr, DSRC_MON); |
| 155 | outb(nec+ds0_pstart, RBUF/DS_PGSIZE); |
| 156 | outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE); |
| 157 | outb(nec+ds0_bnry, RBUFEND/DS_PGSIZE); |
| 158 | outb(nec+ds0_imr, 0); |
| 159 | outb(nec+ds0_isr, 0); |
| 160 | outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); |
| 161 | outb(nec+ds1_curr, RBUF/DS_PGSIZE); |
| 162 | outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); |
| 163 | |
| 164 | #ifdef NEDEBUG |
| 165 | #define PAT(n) (0xa55a + 37*(n)) |
| 166 | #define RCON 37 |
| 167 | { int i, rom, pat; |
| 168 | |
| 169 | rom=1; |
| 170 | printf("ne ram "); |
| 171 | |
| 172 | for (i = 0; i < 0xfff0; i+=4) { |
| 173 | pat = PAT(i); |
| 174 | neput(&pat,i,4); |
| 175 | nefetch(&pat,i,4); |
| 176 | if (pat == PAT(i)) { |
| 177 | if (rom) { |
| 178 | rom=0; |
| 179 | printf(" %x", i); |
| 180 | } |
| 181 | } else { |
| 182 | if (!rom) { |
| 183 | rom=1; |
| 184 | printf("..%x ", i); |
| 185 | } |
| 186 | } |
| 187 | pat=0; |
| 188 | neput(&pat,i,4); |
| 189 | } |
| 190 | printf("\n"); |
| 191 | } |
| 192 | #endif |
| 193 | |
| 194 | /* Extract board address */ |
| 195 | nefetch ((caddr_t)boarddata, 0, sizeof(boarddata)); |
| 196 | for(i=0; i < 6; i++) ns->ns_addr[i] = boarddata[i]; |
| 197 | splx(s); |
| 198 | return (1); |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | * Fetch from onboard ROM/RAM |
| 203 | */ |
| 204 | nefetch (up, ad, len) caddr_t up; { |
| 205 | u_char cmd; |
| 206 | |
| 207 | cmd = inb(nec+ds_cmd); |
| 208 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); |
| 209 | |
| 210 | /* Setup remote dma */ |
| 211 | outb (nec+ds0_isr, DSIS_RDC); |
| 212 | outb (nec+ds0_rbcr0, len); |
| 213 | outb (nec+ds0_rbcr1, len>>8); |
| 214 | outb (nec+ds0_rsar0, ad); |
| 215 | outb (nec+ds0_rsar1, ad>>8); |
| 216 | |
| 217 | /* Execute & extract from card */ |
| 218 | outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START); |
| 219 | insw (nec+ne_data, up, len/2); |
| 220 | |
| 221 | /* Wait till done, then shutdown feature */ |
| 222 | while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ; |
| 223 | outb (nec+ds0_isr, DSIS_RDC); |
| 224 | outb (nec+ds_cmd, cmd); |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | * Put to onboard RAM |
| 229 | */ |
| 230 | neput (up, ad, len) caddr_t up; { |
| 231 | u_char cmd; |
| 232 | |
| 233 | cmd = inb(nec+ds_cmd); |
| 234 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); |
| 235 | |
| 236 | /* Setup for remote dma */ |
| 237 | outb (nec+ds0_isr, DSIS_RDC); |
| 238 | if(len&1) len++; /* roundup to words */ |
| 239 | outb (nec+ds0_rbcr0, len); |
| 240 | outb (nec+ds0_rbcr1, len>>8); |
| 241 | outb (nec+ds0_rsar0, ad); |
| 242 | outb (nec+ds0_rsar1, ad>>8); |
| 243 | |
| 244 | /* Execute & stuff to card */ |
| 245 | outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START); |
| 246 | outsw (nec+ne_data, up, len/2); |
| 247 | |
| 248 | /* Wait till done, then shutdown feature */ |
| 249 | while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ; |
| 250 | outb (nec+ds0_isr, DSIS_RDC); |
| 251 | outb (nec+ds_cmd, cmd); |
| 252 | } |
| 253 | |
| 254 | /* |
| 255 | * Reset of interface. |
| 256 | */ |
| 257 | nereset(unit, uban) |
| 258 | int unit, uban; |
| 259 | { |
| 260 | if (unit >= NNE) |
| 261 | return; |
| 262 | printf("ne%d: reset\n", unit); |
| 263 | ne_softc[unit].ns_flags &= ~DSF_LOCK; |
| 264 | neinit(unit); |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | * Interface exists: make available by filling in network interface |
| 269 | * record. System will initialize the interface when it is ready |
| 270 | * to accept packets. We get the ethernet address here. |
| 271 | */ |
| 272 | neattach(dvp) |
| 273 | struct isa_device *dvp; |
| 274 | { |
| 275 | int unit = dvp->id_unit; |
| 276 | register struct ne_softc *ns = &ne_softc[unit]; |
| 277 | register struct ifnet *ifp = &ns->ns_if; |
| 278 | |
| 279 | ifp->if_unit = unit; |
| 280 | ifp->if_name = nedriver.name ; |
| 281 | ifp->if_mtu = ETHERMTU; |
| 282 | printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ; |
| 283 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; |
| 284 | ifp->if_init = neinit; |
| 285 | ifp->if_output = ether_output; |
| 286 | ifp->if_start = nestart; |
| 287 | ifp->if_ioctl = neioctl; |
| 288 | ifp->if_reset = nereset; |
| 289 | ifp->if_watchdog = 0; |
| 290 | if_attach(ifp); |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | * Initialization of interface; set up initialization block |
| 295 | * and transmit/receive descriptor rings. |
| 296 | */ |
| 297 | neinit(unit) |
| 298 | int unit; |
| 299 | { |
| 300 | register struct ne_softc *ns = &ne_softc[unit]; |
| 301 | struct ifnet *ifp = &ns->ns_if; |
| 302 | int s; |
| 303 | register i; char *cp; |
| 304 | |
| 305 | if (ifp->if_addrlist == (struct ifaddr *)0) return; |
| 306 | if (ifp->if_flags & IFF_RUNNING) return; |
| 307 | |
| 308 | s = splimp(); |
| 309 | |
| 310 | /* set physical address on ethernet */ |
| 311 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); |
| 312 | for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]); |
| 313 | |
| 314 | /* clr logical address hash filter for now */ |
| 315 | for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff); |
| 316 | |
| 317 | /* init regs */ |
| 318 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); |
| 319 | outb (nec+ds0_rbcr0, 0); |
| 320 | outb (nec+ds0_rbcr1, 0); |
| 321 | outb (nec+ds0_imr, 0); |
| 322 | outb (nec+ds0_isr, 0xff); |
| 323 | |
| 324 | /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */ |
| 325 | outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1); |
| 326 | outb(nec+ds0_tcr, 0); |
| 327 | outb (nec+ds0_rcr, DSRC_MON); |
| 328 | outb (nec+ds0_tpsr, 0); |
| 329 | outb(nec+ds0_pstart, RBUF/DS_PGSIZE); |
| 330 | outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE); |
| 331 | outb(nec+ds0_bnry, RBUF/DS_PGSIZE); |
| 332 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); |
| 333 | outb(nec+ds1_curr, RBUF/DS_PGSIZE); |
| 334 | ns->ns_cur = RBUF/DS_PGSIZE; |
| 335 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); |
| 336 | outb (nec+ds0_rcr, DSRC_AB); |
| 337 | outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1); |
| 338 | outb (nec+ds0_imr, 0xff); |
| 339 | |
| 340 | ns->ns_if.if_flags |= IFF_RUNNING; |
| 341 | ns->ns_oactive = 0; ns->ns_mask = ~0; |
| 342 | nestart(ifp); |
| 343 | splx(s); |
| 344 | } |
| 345 | |
| 346 | /* |
| 347 | * Setup output on interface. |
| 348 | * Get another datagram to send off of the interface queue, |
| 349 | * and map it to the interface before starting the output. |
| 350 | * called only at splimp or interrupt level. |
| 351 | */ |
| 352 | nestart(ifp) |
| 353 | struct ifnet *ifp; |
| 354 | { |
| 355 | register struct ne_softc *ns = &ne_softc[ifp->if_unit]; |
| 356 | struct mbuf *m0, *m; |
| 357 | int buffer; |
| 358 | int len = 0, i, total,t; |
| 359 | |
| 360 | /* |
| 361 | * The DS8390 has only one transmit buffer, if it is busy we |
| 362 | * must wait until the transmit interrupt completes. |
| 363 | */ |
| 364 | outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); |
| 365 | |
| 366 | if (ns->ns_flags & DSF_LOCK) |
| 367 | return; |
| 368 | |
| 369 | if (inb(nec+ds_cmd) & DSCM_TRANS) |
| 370 | return; |
| 371 | |
| 372 | if ((ns->ns_if.if_flags & IFF_RUNNING) == 0) |
| 373 | return; |
| 374 | |
| 375 | IF_DEQUEUE(&ns->ns_if.if_snd, m); |
| 376 | |
| 377 | if (m == 0) |
| 378 | return; |
| 379 | |
| 380 | /* |
| 381 | * Copy the mbuf chain into the transmit buffer |
| 382 | */ |
| 383 | |
| 384 | ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */ |
| 385 | buffer = TBUF; len = i = 0; |
| 386 | t = 0; |
| 387 | for (m0 = m; m != 0; m = m->m_next) |
| 388 | t += m->m_len; |
| 389 | |
| 390 | m = m0; |
| 391 | total = t; |
| 392 | for (m0 = m; m != 0; ) { |
| 393 | |
| 394 | if (m->m_len&1 && t > m->m_len) { |
| 395 | neput(mtod(m, caddr_t), buffer, m->m_len - 1); |
| 396 | t -= m->m_len - 1; |
| 397 | buffer += m->m_len - 1; |
| 398 | m->m_data += m->m_len - 1; |
| 399 | m->m_len = 1; |
| 400 | m = m_pullup(m, 2); |
| 401 | } else { |
| 402 | neput(mtod(m, caddr_t), buffer, m->m_len); |
| 403 | buffer += m->m_len; |
| 404 | t -= m->m_len; |
| 405 | MFREE(m, m0); |
| 406 | m = m0; |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | /* |
| 411 | * Init transmit length registers, and set transmit start flag. |
| 412 | */ |
| 413 | |
| 414 | len = total; |
| 415 | if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN; |
| 416 | outb(nec+ds0_tbcr0,len&0xff); |
| 417 | outb(nec+ds0_tbcr1,(len>>8)&0xff); |
| 418 | outb(nec+ds0_tpsr, TBUF/DS_PGSIZE); |
| 419 | outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); |
| 420 | } |
| 421 | |
| 422 | /* buffer successor/predecessor in ring? */ |
| 423 | #define succ(n) (((n)+1 >= RBUFEND/DS_PGSIZE) ? RBUF/DS_PGSIZE : (n)+1) |
| 424 | #define pred(n) (((n)-1 < RBUF/DS_PGSIZE) ? RBUFEND/DS_PGSIZE-1 : (n)-1) |
| 425 | |
| 426 | /* |
| 427 | * Controller interrupt. |
| 428 | */ |
| 429 | neintr(unit) |
| 430 | { |
| 431 | register struct ne_softc *ns = &ne_softc[unit]; |
| 432 | u_char cmd,isr; |
| 433 | |
| 434 | /* Save cmd, clear interrupt */ |
| 435 | cmd = inb (nec+ds_cmd); |
| 436 | loop: |
| 437 | isr = inb (nec+ds0_isr); |
| 438 | outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); |
| 439 | outb(nec+ds0_isr, isr); |
| 440 | |
| 441 | /* Receiver error */ |
| 442 | if (isr & DSIS_RXE) { |
| 443 | /* need to read these registers to clear status */ |
| 444 | (void) inb(nec+ ds0_rsr); |
| 445 | (void) inb(nec+ 0xD); |
| 446 | (void) inb(nec + 0xE); |
| 447 | (void) inb(nec + 0xF); |
| 448 | ns->ns_if.if_ierrors++; |
| 449 | } |
| 450 | |
| 451 | /* We received something; rummage thru tiny ring buffer */ |
| 452 | if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) { |
| 453 | u_char pend,lastfree; |
| 454 | |
| 455 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); |
| 456 | pend = inb(nec+ds1_curr); |
| 457 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); |
| 458 | lastfree = inb(nec+ds0_bnry); |
| 459 | |
| 460 | /* Have we wrapped? */ |
| 461 | if (lastfree >= RBUFEND/DS_PGSIZE) |
| 462 | lastfree = RBUF/DS_PGSIZE; |
| 463 | if (pend < lastfree && ns->ns_cur < pend) |
| 464 | lastfree = ns->ns_cur; |
| 465 | else if (ns->ns_cur > lastfree) |
| 466 | lastfree = ns->ns_cur; |
| 467 | |
| 468 | /* Something in the buffer? */ |
| 469 | while (pend != lastfree) { |
| 470 | u_char nxt; |
| 471 | |
| 472 | /* Extract header from microcephalic board */ |
| 473 | nefetch(&ns->ns_ph,lastfree*DS_PGSIZE, |
| 474 | sizeof(ns->ns_ph)); |
| 475 | ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph); |
| 476 | |
| 477 | /* Incipient paranoia */ |
| 478 | if (ns->ns_ph.pr_status == DSRS_RPC || |
| 479 | /* for dequna's */ |
| 480 | ns->ns_ph.pr_status == 0x21) |
| 481 | nerecv (ns); |
| 482 | #ifdef NEDEBUG |
| 483 | else { |
| 484 | printf("cur %x pnd %x lfr %x ", |
| 485 | ns->ns_cur, pend, lastfree); |
| 486 | printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg, |
| 487 | (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0); |
| 488 | printf("Bogus Sts %x\n", ns->ns_ph.pr_status); |
| 489 | } |
| 490 | #endif |
| 491 | |
| 492 | nxt = ns->ns_ph.pr_nxtpg ; |
| 493 | |
| 494 | /* Sanity check */ |
| 495 | if ( nxt >= RBUF/DS_PGSIZE && nxt <= RBUFEND/DS_PGSIZE |
| 496 | && nxt <= pend) |
| 497 | ns->ns_cur = nxt; |
| 498 | else ns->ns_cur = nxt = pend; |
| 499 | |
| 500 | /* Set the boundaries */ |
| 501 | lastfree = nxt; |
| 502 | outb(nec+ds0_bnry, pred(nxt)); |
| 503 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); |
| 504 | pend = inb(nec+ds1_curr); |
| 505 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); |
| 506 | } |
| 507 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); |
| 508 | } |
| 509 | |
| 510 | /* Transmit error */ |
| 511 | if (isr & DSIS_TXE) { |
| 512 | ns->ns_flags &= ~DSF_LOCK; |
| 513 | /* Need to read these registers to clear status */ |
| 514 | ns->ns_if.if_collisions += inb(nec+ds0_tbcr0); |
| 515 | ns->ns_if.if_oerrors++; |
| 516 | } |
| 517 | |
| 518 | /* Packet Transmitted */ |
| 519 | if (isr & DSIS_TX) { |
| 520 | ns->ns_flags &= ~DSF_LOCK; |
| 521 | ++ns->ns_if.if_opackets; |
| 522 | ns->ns_if.if_collisions += inb(nec+ds0_tbcr0); |
| 523 | } |
| 524 | |
| 525 | /* Receiver ovverun? */ |
| 526 | if (isr & DSIS_ROVRN) { |
| 527 | log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr |
| 528 | /*, DSIS_BITS*/); |
| 529 | outb(nec+ds0_rbcr0, 0); |
| 530 | outb(nec+ds0_rbcr1, 0); |
| 531 | outb(nec+ds0_tcr, DSTC_LB0); |
| 532 | outb(nec+ds0_rcr, DSRC_MON); |
| 533 | outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); |
| 534 | outb(nec+ds0_rcr, DSRC_AB); |
| 535 | outb(nec+ds0_tcr, 0); |
| 536 | } |
| 537 | |
| 538 | /* Any more to send? */ |
| 539 | outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); |
| 540 | nestart(&ns->ns_if); |
| 541 | outb (nec+ds_cmd, cmd); |
| 542 | outb (nec+ds0_imr, 0xff); |
| 543 | |
| 544 | /* Still more to do? */ |
| 545 | isr = inb (nec+ds0_isr); |
| 546 | if(isr) goto loop; |
| 547 | } |
| 548 | |
| 549 | /* |
| 550 | * Ethernet interface receiver interface. |
| 551 | * If input error just drop packet. |
| 552 | * Otherwise examine packet to determine type. If can't determine length |
| 553 | * from type, then have to drop packet. Othewise decapsulate |
| 554 | * packet based on type and pass to type specific higher-level |
| 555 | * input routine. |
| 556 | */ |
| 557 | nerecv(ns) |
| 558 | register struct ne_softc *ns; |
| 559 | { |
| 560 | int len,i; |
| 561 | |
| 562 | ns->ns_if.if_ipackets++; |
| 563 | len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8); |
| 564 | if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN) |
| 565 | return; |
| 566 | |
| 567 | /* this need not be so torturous - one/two bcopys at most into mbufs */ |
| 568 | nefetch(ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph))); |
| 569 | if (len > DS_PGSIZE-sizeof(ns->ns_ph)) { |
| 570 | int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ; |
| 571 | u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph)); |
| 572 | |
| 573 | if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46; |
| 574 | b = ns->ns_cur*DS_PGSIZE; |
| 575 | |
| 576 | while (l >= DS_PGSIZE) { |
| 577 | nefetch(p, b, DS_PGSIZE); |
| 578 | p += DS_PGSIZE; l -= DS_PGSIZE; |
| 579 | if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46; |
| 580 | b = ns->ns_cur*DS_PGSIZE; |
| 581 | } |
| 582 | if (l > 0) |
| 583 | nefetch(p, b, l); |
| 584 | } |
| 585 | /* don't forget checksum! */ |
| 586 | len -= sizeof(struct ether_header) + sizeof(long); |
| 587 | |
| 588 | neread(ns,(caddr_t)(ns->ns_pb), len); |
| 589 | } |
| 590 | |
| 591 | /* |
| 592 | * Pass a packet to the higher levels. |
| 593 | * We deal with the trailer protocol here. |
| 594 | */ |
| 595 | neread(ns, buf, len) |
| 596 | register struct ne_softc *ns; |
| 597 | char *buf; |
| 598 | int len; |
| 599 | { |
| 600 | register struct ether_header *eh; |
| 601 | struct mbuf *m; |
| 602 | int off, resid; |
| 603 | register struct ifqueue *inq; |
| 604 | |
| 605 | /* |
| 606 | * Deal with trailer protocol: if type is trailer type |
| 607 | * get true type from first 16-bit word past data. |
| 608 | * Remember that type was trailer by setting off. |
| 609 | */ |
| 610 | eh = (struct ether_header *)buf; |
| 611 | eh->ether_type = ntohs((u_short)eh->ether_type); |
| 612 | #define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) |
| 613 | if (eh->ether_type >= ETHERTYPE_TRAIL && |
| 614 | eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { |
| 615 | off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; |
| 616 | if (off >= ETHERMTU) return; /* sanity */ |
| 617 | eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *)); |
| 618 | resid = ntohs(*(nedataaddr(eh, off+2, u_short *))); |
| 619 | if (off + resid > len) return; /* sanity */ |
| 620 | len = off + resid; |
| 621 | } else off = 0; |
| 622 | |
| 623 | if (len == 0) return; |
| 624 | |
| 625 | /* |
| 626 | * Pull packet off interface. Off is nonzero if packet |
| 627 | * has trailing header; neget will then force this header |
| 628 | * information to be at the front, but we still have to drop |
| 629 | * the type and length which are at the front of any trailer data. |
| 630 | */ |
| 631 | m = neget(buf, len, off, &ns->ns_if); |
| 632 | if (m == 0) return; |
| 633 | |
| 634 | ether_input(&ns->ns_if, eh, m); |
| 635 | } |
| 636 | |
| 637 | /* |
| 638 | * Supporting routines |
| 639 | */ |
| 640 | |
| 641 | /* |
| 642 | * Pull read data off a interface. |
| 643 | * Len is length of data, with local net header stripped. |
| 644 | * Off is non-zero if a trailer protocol was used, and |
| 645 | * gives the offset of the trailer information. |
| 646 | * We copy the trailer information and then all the normal |
| 647 | * data into mbufs. When full cluster sized units are present |
| 648 | * we copy into clusters. |
| 649 | */ |
| 650 | struct mbuf * |
| 651 | neget(buf, totlen, off0, ifp) |
| 652 | caddr_t buf; |
| 653 | int totlen, off0; |
| 654 | struct ifnet *ifp; |
| 655 | { |
| 656 | struct mbuf *top, **mp, *m, *p; |
| 657 | int off = off0, len; |
| 658 | register caddr_t cp = buf; |
| 659 | char *epkt; |
| 660 | |
| 661 | buf += sizeof(struct ether_header); |
| 662 | cp = buf; |
| 663 | epkt = cp + totlen; |
| 664 | |
| 665 | |
| 666 | if (off) { |
| 667 | cp += off + 2 * sizeof(u_short); |
| 668 | totlen -= 2 * sizeof(u_short); |
| 669 | } |
| 670 | |
| 671 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
| 672 | if (m == 0) |
| 673 | return (0); |
| 674 | m->m_pkthdr.rcvif = ifp; |
| 675 | m->m_pkthdr.len = totlen; |
| 676 | m->m_len = MHLEN; |
| 677 | |
| 678 | top = 0; |
| 679 | mp = ⊤ |
| 680 | while (totlen > 0) { |
| 681 | if (top) { |
| 682 | MGET(m, M_DONTWAIT, MT_DATA); |
| 683 | if (m == 0) { |
| 684 | m_freem(top); |
| 685 | return (0); |
| 686 | } |
| 687 | m->m_len = MLEN; |
| 688 | } |
| 689 | len = min(totlen, epkt - cp); |
| 690 | if (len >= MINCLSIZE) { |
| 691 | MCLGET(m, M_DONTWAIT); |
| 692 | if (m->m_flags & M_EXT) |
| 693 | m->m_len = len = min(len, MCLBYTES); |
| 694 | else |
| 695 | len = m->m_len; |
| 696 | } else { |
| 697 | /* |
| 698 | * Place initial small packet/header at end of mbuf. |
| 699 | */ |
| 700 | if (len < m->m_len) { |
| 701 | if (top == 0 && len + max_linkhdr <= m->m_len) |
| 702 | m->m_data += max_linkhdr; |
| 703 | m->m_len = len; |
| 704 | } else |
| 705 | len = m->m_len; |
| 706 | } |
| 707 | bcopy(cp, mtod(m, caddr_t), (unsigned)len); |
| 708 | cp += len; |
| 709 | *mp = m; |
| 710 | mp = &m->m_next; |
| 711 | totlen -= len; |
| 712 | if (cp == epkt) |
| 713 | cp = buf; |
| 714 | } |
| 715 | return (top); |
| 716 | } |
| 717 | |
| 718 | /* |
| 719 | * Process an ioctl request. |
| 720 | */ |
| 721 | neioctl(ifp, cmd, data) |
| 722 | register struct ifnet *ifp; |
| 723 | int cmd; |
| 724 | caddr_t data; |
| 725 | { |
| 726 | register struct ifaddr *ifa = (struct ifaddr *)data; |
| 727 | struct ne_softc *ns = &ne_softc[ifp->if_unit]; |
| 728 | struct ifreq *ifr = (struct ifreq *)data; |
| 729 | int s = splimp(), error = 0; |
| 730 | |
| 731 | |
| 732 | switch (cmd) { |
| 733 | |
| 734 | case SIOCSIFADDR: |
| 735 | ifp->if_flags |= IFF_UP; |
| 736 | |
| 737 | switch (ifa->ifa_addr->sa_family) { |
| 738 | #ifdef INET |
| 739 | case AF_INET: |
| 740 | neinit(ifp->if_unit); /* before arpwhohas */ |
| 741 | ((struct arpcom *)ifp)->ac_ipaddr = |
| 742 | IA_SIN(ifa)->sin_addr; |
| 743 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); |
| 744 | break; |
| 745 | #endif |
| 746 | #ifdef NS |
| 747 | case AF_NS: |
| 748 | { |
| 749 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); |
| 750 | |
| 751 | if (ns_nullhost(*ina)) |
| 752 | ina->x_host = *(union ns_host *)(ns->ns_addr); |
| 753 | else { |
| 754 | /* |
| 755 | * The manual says we can't change the address |
| 756 | * while the receiver is armed, |
| 757 | * so reset everything |
| 758 | */ |
| 759 | ifp->if_flags &= ~IFF_RUNNING; |
| 760 | bcopy((caddr_t)ina->x_host.c_host, |
| 761 | (caddr_t)ns->ns_addr, sizeof(ns->ns_addr)); |
| 762 | } |
| 763 | neinit(ifp->if_unit); /* does ne_setaddr() */ |
| 764 | break; |
| 765 | } |
| 766 | #endif |
| 767 | default: |
| 768 | neinit(ifp->if_unit); |
| 769 | break; |
| 770 | } |
| 771 | break; |
| 772 | |
| 773 | case SIOCSIFFLAGS: |
| 774 | if ((ifp->if_flags & IFF_UP) == 0 && |
| 775 | ifp->if_flags & IFF_RUNNING) { |
| 776 | ifp->if_flags &= ~IFF_RUNNING; |
| 777 | outb(nec+ds_cmd,DSCM_STOP|DSCM_NODMA); |
| 778 | } else if (ifp->if_flags & IFF_UP && |
| 779 | (ifp->if_flags & IFF_RUNNING) == 0) |
| 780 | neinit(ifp->if_unit); |
| 781 | break; |
| 782 | |
| 783 | #ifdef notdef |
| 784 | case SIOCGHWADDR: |
| 785 | bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data, |
| 786 | sizeof(ns->ns_addr)); |
| 787 | break; |
| 788 | #endif |
| 789 | |
| 790 | default: |
| 791 | error = EINVAL; |
| 792 | } |
| 793 | splx(s); |
| 794 | return (error); |
| 795 | } |
| 796 | #endif |