1 /* 2 * Copyright (c) 1982, 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)if_apx.c 7.2 (Berkeley) 08/09/91 8 */ 9 10 /* 11 * Driver for SGS-THOMSON MK5025 based Link level controller. 12 * The chip will do LAPB in hardware, although this driver only 13 * attempts to use it for HDLC framing. 14 * 15 * Driver written by Keith Sklower, based on lance AMD7990 16 * driver by Van Jacobsen, and information graciously supplied 17 * by the ADAX corporation of Berkeley, CA. 18 */ 19 20 #include "apx.h" 21 #if NAPX > 0 22 23 #include "param.h" 24 #include "mbuf.h" 25 #include "socket.h" 26 #include "ioctl.h" 27 #include "errno.h" 28 #include "syslog.h" 29 30 #include "net/if.h" 31 #include "net/netisr.h" 32 #include "net/if_types.h" 33 #include "netccitt/x25.h" 34 35 #include "if_apxreg.h" 36 37 int apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit(); 38 int apxinit(), x25_ifoutput(), x25_rtrequest(), apxioctl(), apxreset(); 39 void apx_ifattach(), apxinput(), apxintr(), apxtint(), apaxrint(); 40 41 struct apx_softc { 42 struct ifnet apx_if; 43 caddr_t apx_device; /* e.g. isa_device */ 44 u_short apx_csr4; /* byte gender, set in mach dep code */ 45 struct apc_reg *apx_reg; /* control regs for both subunits */ 46 struct apc_mem *apx_hmem; /* Host addr for shared memory */ 47 struct apc_mem *apx_dmem; /* Device (chip) addr for shared mem */ 48 struct sgcp *apx_sgcp; /* IO control port for this subunit */ 49 struct apc_modes apx_modes; /* Parameters, as amended by ioctls */ 50 int apx_rxnum; /* Last receiver dx we looked at */ 51 int apx_txnum; /* Last tranmistter dx we stomped on */ 52 int apx_txcnt; /* Number of packets queued for tx*/ 53 } apx_softc[2 * NAPX], *apx_lastsoftc = apx_softc; 54 55 struct apxstat { 56 int nulltx; 57 int pint; 58 } apxstat; 59 60 /* default operating paramters for devices */ 61 struct apc_modes apx_default_modes = { 62 { 1, /* apm_sgob.lsaddr; */ 63 3, /* apm_sgob.rsaddr; */ 64 -SGMTU, /* apm_sgob.n1; */ 65 ((-10)<<8), /* apm_sgob.n2_scale; */ 66 -1250, /* apm_sgob.t1; */ 67 -10000, /* apm_sgob.t3; */ 68 -80, /* apm_sgob.tp; */ 69 }, 70 2, /* apm_txwin; */ 71 5, /* apm_apxmode; */ 72 0, /* apm_apxaltmode; */ 73 IFT_X25, /* apm_iftype; */ 74 }; 75 76 /* Begin bus & endian dependence */ 77 78 #include "isa_device.h" 79 80 struct isa_driver apxdriver = { 81 apxprobe, apxattach, "apx", 82 }; 83 84 #define SG_RCSR(apx, csrnum) \ 85 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \ 86 inw(&(apx->apx_sgcp->sgcp_rdp))) 87 88 #define SG_WCSR(apx, csrnum, data) \ 89 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \ 90 outw(&(apx->apx_sgcp->sgcp_rdp), data)) 91 92 #define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname)) 93 #define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data) 94 95 #define TIMO 10000 /* used in apx_uprim */ 96 97 apxprobe(id) 98 register struct isa_device *id; 99 { 100 int moffset, subunit, unit = id->id_unit << 1; 101 struct apc_reg *reg = (struct apc_reg *)id->id_iobase; 102 register struct apx_softc *apx = apx_softc + unit; 103 104 /* Set and read DTR defeat in channel 0 to test presence of apc */ 105 outb(®->axr_altmode, 4); 106 if (inb(®->axr_altmode) == 0) 107 return 0; /* No board present */ 108 109 for (subunit = 0; subunit < 2; subunit++, apx++) { 110 /* Set and read DTR mode to test present of SGS thompson chip */ 111 apx->apx_if.if_unit = unit++; 112 apx->apx_sgcp = reg->axr_sgcp + subunit; 113 SG_WCSR(apx, 5, 0x08); 114 if (((SG_RCSR(apx, 5) & 0xff08) != 0x08)) { 115 apxerror(apx, "no mk5025 for channel", subunit); 116 continue; 117 } 118 moffset = subunit ? id->id_msize >> 1 : 0; 119 apx->apx_hmem = (struct apc_mem *) (id->id_maddr + moffset); 120 apx->apx_dmem = (struct apc_mem *) (moffset); 121 apx->apx_modes = apx_default_modes; 122 apx->apx_device = (caddr_t) id; 123 apx->apx_reg = reg; 124 apx->apx_csr4 = 0x0110; /* no byte swapping for PC-AT */ 125 } 126 return 1; 127 } 128 129 apxattach(id) 130 register struct isa_device *id; 131 { 132 int unit = id->id_unit + id->id_unit; 133 134 apx_ifattach(unit); 135 apx_ifattach(unit + 1); 136 return (0); 137 } 138 139 /* End bus & endian dependence */ 140 141 /* 142 * Interface exists: make available by filling in network interface 143 * record. System will initialize the interface when it is ready 144 * to accept packets. 145 */ 146 void 147 apx_ifattach(unit) 148 { 149 register struct ifnet *ifp = &(apx_softc[unit].apx_if); 150 /* 151 * Initialize ifnet structure 152 */ 153 if (apx_softc[unit].apx_device == 0) 154 return; 155 ifp->if_name = "apc"; 156 ifp->if_mtu = SGMTU; 157 ifp->if_init = apxinit; 158 ifp->if_output = x25_ifoutput; 159 ifp->if_start = apxstart; 160 ifp->if_ioctl = apxioctl; 161 ifp->if_reset = apxreset; 162 ifp->if_type = apx_default_modes.apm_iftype; 163 ifp->if_hdrlen = 5; 164 ifp->if_addrlen = 8; 165 if_attach(ifp); 166 } 167 /* 168 * Initialization of interface 169 */ 170 apxinit(unit) 171 int unit; 172 { 173 struct ifnet *ifp = &apx_softc[unit].apx_if; 174 int s = splimp(); 175 176 ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 177 if (apxreset(unit) && (ifp->if_flags & IFF_UP)) { 178 ifp->if_flags |= IFF_RUNNING; 179 (void)apxstart(ifp); 180 } 181 splx(s); 182 return 0; 183 } 184 185 apxreset(unit) 186 int unit; 187 { 188 register struct apx_softc *apx = &apx_softc[unit ^ 1]; 189 u_char apm_apxmode = 0, apm_apxaltmode = 0; 190 #define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0)) 191 192 MODE(apm_apxmode); 193 MODE(apm_apxaltmode); 194 apx = apx_softc + unit; 195 MODE(apm_apxmode); 196 MODE(apm_apxaltmode); 197 APX_WCSR(apx, axr_mode, apm_apxmode); 198 APX_WCSR(apx, axr_altmode, apm_apxaltmode); 199 apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = 0; 200 201 if (apx_uprim(apx, SG_STOP, "stop") || 202 !(apx->apx_if.if_flags & IFF_UP)) 203 return 0; 204 apx_meminit(apx->apx_hmem, apx); /* also sets CSR2 */ 205 SG_WCSR(apx, 3, (int)apx->apx_dmem); 206 SG_WCSR(apx, 4, apx->apx_csr4); 207 if (apx_uprim(apx, SG_INIT, "init request") || 208 apx_uprim(apx, SG_STAT, "status request") || 209 apx_uprim(apx, SG_TRANS, "transparent mode")) 210 return 0; 211 SG_WCSR(apx, 0, SG_INEA); 212 return 1; 213 } 214 215 apx_uprim(apx, request, ident) 216 int request; 217 char *ident; 218 register struct apx_softc *apx; 219 { 220 register int timo = 0; 221 int reply = SG_RCSR(apx, 1); 222 223 if (reply & 0x8040) 224 SG_WCRS(1, 0x8040); /* Magic! */ 225 SG_WCSR(apx, 1, request | SG_UAV); 226 do { 227 reply = SG_RCRS(1); 228 if (timo >= TIMO | reply & 0x8000) { 229 apxerror(apx, ident, reply); 230 return 1; 231 } 232 } while (reply & SG_UAV); 233 return 0; 234 } 235 236 apx_meminit(apc, apx) 237 register struct apc_mem *apc; 238 struct apx_softc *apx; 239 { 240 register struct apc_mem *apcbase = apx->apx_dmem; 241 register int i; 242 #define LOWADDR(e) (((u_long)&(apcbase->e)) & 0xffff) 243 #define HIADDR(e) ((((u_long)&(apcbase->e)) >> 16) & 0xff) 244 #define SET_SGDX(dx, f, a, b, m) \ 245 { (dx).sgdx_addr = LOWADDR(a); (dx).sgdx_bcnt = (b);\ 246 (dx).sgdx_mcnt = (m); (dx).sgdx_flags = (f) | HIADDR(a); } 247 248 bzero((caddr_t)apc, LOWADDR(apc_rxmd[0])); 249 apc->apc_mode = 0x8040; /* 2 flag spacing, trans mode, 16bit FCS */ 250 apc->apc_sgop = apx->apx_modes.apm_sgop; 251 apc->apc_rlen = SG_RLEN | HIADDR(apc_rxmd[0]); 252 apc->apc_rdra = LOWADDR(apc_rxmd[0]); 253 apc->apc_rlen = SG_TLEN | apx->apx_modes.apm_txwin |HIADDR(apc_txmd[0]); 254 apc->apc_tdra = LOWADDR(apc_txmd[0]); 255 SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf, -SGMTU, 0); 256 SET_SGDX(apc->apc_txtid, 0, apc_txidbuf, -SGMTU, 0); 257 apc->apc_stathi = HIADDR(apc_sgsb); 258 apc->apc_statlo = LOWADDR(apc_sgsb); 259 for (i = 0; i < SGRBUF; i++) 260 SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU, 0) 261 for (i = 0; i < SGTBUF; i++) 262 SET_SGDX(apc->apc_txmd[i], SG_TUI, apc_tbuf[i][0], 0, 0) 263 SG_WCSR(apx, 2, SG_UIE | SG_PROM | HIADDR(apc_mode)); 264 } 265 266 /* 267 * Start output on interface. Get another datagram to send 268 * off of the interface queue, and copy it to the interface 269 * before starting the output. 270 */ 271 apxstart(ifp) 272 struct ifnet *ifp; 273 { 274 register struct apx_softc *apx = &apx_softc[ifp->if_unit]; 275 register struct sgdx *dx; 276 struct apc_mem *apc = apx->apx_hmem; 277 struct mbuf *m; 278 int len; 279 280 if ((ifp->if_flags & IFF_RUNNING) == 0) 281 return (0); 282 do { 283 dx = apc->apc_txmd + apx->apx_txnum; 284 if (dx->sgdx_flags & SG_OWN) 285 return (0); 286 IF_DEQUEUE(&ifp->if_snd, m); 287 if (m == 0) 288 return (0); 289 len = min(m->m_pkthdr.len, SGMTU); 290 m_copydata(m, 0, len, apc->apc_tbuf[apx->apx_txnum]); 291 dx->sgdx_mcnt = -len; 292 dx->sgdx_flags = SG_OWN | SG_TUI | (0xff & dx->sgdx_flags); 293 SG_WCSR(apx, 0, SG_INEA | SG_TDMD); 294 if (++apx->apx_txnum >= SGTBUF) 295 apx->apx_txnum = 0; 296 } while (++apx->apx_txcnt < SGTBUF); 297 apx->apx_txcnt = SGTBUF; 298 ifp->if_flags |= IFF_OACTIVE; 299 return (0); 300 } 301 302 void 303 apxintr() 304 { 305 register struct apx_softc *apx = apx_lastsoftc; 306 struct apx_softc *apxlim = apx_softc + NAPX + NAPX; 307 int reply; 308 309 do { 310 if (apx->apx_if.if_flags & IFF_UP) 311 /* Try to turn off interrupt cause */ 312 while ((reply = SG_RCSR(apx, 0)) & 0xff) { 313 SG_WCSR(apx, 0, SG_INEA | 0xfe); 314 if (reply & (SG_MERR|SG_TUR|SG_ROR)) { 315 apxerror(apx, "mem, rx, or tx error", reply); 316 apxinit(apx->apx_if.if_unit); 317 break; 318 } 319 if (reply & SG_RINT) 320 apxrint(apx); 321 if (reply & SG_TINT) 322 apxtint(apx); 323 if (reply & SG_PINT) 324 apxstat.pint++; 325 } 326 if (++apx >= apxlim) 327 apx = apx_softc; 328 } while (apx != apx_lastsoftc); 329 } 330 331 void 332 apxtint(apx) 333 register struct apx_softc *apx; 334 { 335 register struct apc_mem *apc = apx->apx_hmem; 336 int i, loopcount = 0; 337 338 do { 339 if ((i = apx->apx_txnum - apx->apx_txcnt) < 0) 340 i += SGTBUF; 341 if (apc->apc_txmd[i].sgdx_flags & SG_OWN) { 342 if (loopcount) 343 break; 344 apxstat.nulltx++; 345 return; 346 } 347 loopcount++; 348 apx->apx_if.if_flags &= ~IFF_OACTIVE; 349 } while (--apx->apx_txcnt > 0); 350 apxstart(&apx->apx_if); 351 } 352 353 apxrint(apx) 354 register struct apx_softc *apx; 355 { 356 register struct apc_mem *apc = apx->apx_hmem; 357 register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum; 358 #define SGNEXTRXMD \ 359 dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1; 360 361 /* 362 * Out of sync with hardware, should never happen? 363 */ 364 if (dx->sgdx_flags & SG_OWN) { 365 apxerror(apx, "out of sync"); 366 return; 367 } 368 /* 369 * Process all buffers with valid data 370 */ 371 while ((dx->sgdx_flags & SG_OWN) == 0) { 372 if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) { 373 /* 374 * Find the end of the packet so we can see how long 375 * it was. We still throw it away. 376 */ 377 apxerror(apx, "chained buffer", dx->sgdx_flags); 378 do { 379 dx->sgdx_bcnt = 0; 380 dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags); 381 SGNEXTRXMD; 382 } while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF))); 383 /* 384 * If search terminated without successful completion 385 * we reset the hardware (conservative). 386 */ 387 if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) != 388 SG_ELF) { 389 apxreset(apx->apx_if.if_unit); 390 return; 391 } 392 } else 393 apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum], 394 -dx->sgdx_bcnt); 395 dx->sgdx_bcnt = 0; 396 dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags); 397 SGNEXTRXMD; 398 } 399 } 400 401 void 402 apxinput(ifp, buffer, len) 403 register struct ifnet *ifp; 404 caddr_t buffer; 405 { 406 register struct ifqueue *inq; 407 struct mbuf *m, *apxget(); 408 extern struct ifqueue hdintrq, ipintrq; 409 int isr; 410 411 ifp->if_ipackets++; 412 { 413 register u_char *cp = (u_char *)buffer; 414 415 if (cp[0] == 0xff && cp[1] == 0x3) { 416 /* This is a UI HDLC Packet, so we'll assume PPP 417 protocol. for now, IP only. */ 418 buffer += 4; 419 len -= 4; 420 inq = &ipintrq; 421 isr = NETISR_IP; 422 } else { 423 inq = &hdintrq; 424 isr = NETISR_CCITT; 425 } 426 } 427 if (len <= 0) 428 return; 429 430 m = apxget(buffer, len , 0, ifp); 431 if (m == 0) 432 return; 433 434 if(IF_QFULL(inq)) { 435 IF_DROP(inq); 436 m_freem(m); 437 } else { 438 IF_ENQUEUE(inq, m); 439 schednetisr(isr); 440 } 441 } 442 443 /* 444 * Routine to copy from board local memory into mbufs. 445 */ 446 struct mbuf * 447 apxget(buf, totlen, off0, ifp) 448 char *buf; 449 int totlen, off0; 450 struct ifnet *ifp; 451 { 452 register struct mbuf *m; 453 struct mbuf *top = 0, **mp = ⊤ 454 register int off = off0, len; 455 register char *cp; 456 char *epkt; 457 458 cp = buf; 459 epkt = cp + totlen; 460 if (off) { 461 cp += off + 2 * sizeof(u_short); 462 totlen -= 2 * sizeof(u_short); 463 } 464 465 MGETHDR(m, M_DONTWAIT, MT_DATA); 466 if (m == 0) 467 return (0); 468 m->m_pkthdr.rcvif = ifp; 469 m->m_pkthdr.len = totlen; 470 m->m_len = MHLEN; 471 472 while (totlen > 0) { 473 if (top) { 474 MGET(m, M_DONTWAIT, MT_DATA); 475 if (m == 0) { 476 m_freem(top); 477 return (0); 478 } 479 m->m_len = MLEN; 480 } 481 len = min(totlen, epkt - cp); 482 if (len >= MINCLSIZE) { 483 MCLGET(m, M_DONTWAIT); 484 if (m->m_flags & M_EXT) 485 m->m_len = len = min(len, MCLBYTES); 486 else 487 len = m->m_len; 488 } else { 489 /* 490 * Place initial small packet/header at end of mbuf. 491 */ 492 if (len < m->m_len) { 493 if (top == 0 && len + max_linkhdr <= m->m_len) 494 m->m_data += max_linkhdr; 495 m->m_len = len; 496 } else 497 len = m->m_len; 498 } 499 bcopy(cp, mtod(m, caddr_t), (unsigned)len); 500 cp += len; 501 *mp = m; 502 mp = &m->m_next; 503 totlen -= len; 504 if (cp == epkt) 505 cp = buf; 506 } 507 return (top); 508 } 509 510 /* 511 * Process an ioctl request. 512 */ 513 apxioctl(ifp, cmd, data) 514 register struct ifnet *ifp; 515 int cmd; 516 caddr_t data; 517 { 518 register struct ifaddr *ifa = (struct ifaddr *)data; 519 int s = splimp(), error = 0; 520 struct apx_softc *apx = &apx_softc[ifp->if_unit]; 521 522 switch (cmd) { 523 case SIOCSIFCONF_X25: 524 ifp->if_flags |= IFF_UP; 525 error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr); 526 if (error == 0) 527 apxinit(ifp->if_unit); 528 break; 529 530 case SIOCSIFADDR: 531 ifa->ifa_rtrequest = x25_rtrequest; 532 break; 533 534 case SIOCSIFFLAGS: 535 if (((ifp->if_flags & IFF_UP) == 0 && 536 (ifp->if_flags & IFF_RUNNING)) || 537 (ifp->if_flags & IFF_UP) && 538 (ifp->if_flags & IFF_RUNNING) == 0) 539 apxinit(ifp->if_unit); 540 break; 541 542 case SIOCSIFMODE: 543 if ((ifp->if_flags & IFF_UP) == 0) 544 apx->apx_modes = *(struct apc_modes *)data; 545 else 546 default: 547 error = EINVAL; 548 549 } 550 splx(s); 551 return (error); 552 } 553 554 apxerror(apx, msg, data) 555 register struct apx_softc *apx; 556 char *msg; 557 { 558 log(LOG_WARNING, "apc%d: %s, stat=0x%x\n", 559 apx->apx_if.if_unit, msg, data); 560 } 561 #endif /* NAPX */ 562