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