if_plip.c revision 185003
138061Smsmith/*- 238061Smsmith * Copyright (c) 1997 Poul-Henning Kamp 338061Smsmith * All rights reserved. 438061Smsmith * 538061Smsmith * Redistribution and use in source and binary forms, with or without 638061Smsmith * modification, are permitted provided that the following conditions 738061Smsmith * are met: 838061Smsmith * 1. Redistributions of source code must retain the above copyright 938061Smsmith * notice, this list of conditions and the following disclaimer. 1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138061Smsmith * notice, this list of conditions and the following disclaimer in the 1238061Smsmith * documentation and/or other materials provided with the distribution. 1338061Smsmith * 1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738061Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438061Smsmith * SUCH DAMAGE. 2538061Smsmith * 2638061Smsmith * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp 2738061Smsmith */ 2838061Smsmith 29119418Sobrien#include <sys/cdefs.h> 30119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ppbus/if_plip.c 185003 2008-11-16 17:42:02Z jhb $"); 31119418Sobrien 3238061Smsmith/* 3338061Smsmith * Parallel port TCP/IP interfaces added. I looked at the driver from 3438061Smsmith * MACH but this is a complete rewrite, and btw. incompatible, and it 3538061Smsmith * should perform better too. I have never run the MACH driver though. 3638061Smsmith * 3738061Smsmith * This driver sends two bytes (0x08, 0x00) in front of each packet, 3838061Smsmith * to allow us to distinguish another format later. 3938061Smsmith * 40108470Sschweikh * Now added a Linux/Crynwr compatibility mode which is enabled using 4138061Smsmith * IF_LINK0 - Tim Wilkinson. 4238061Smsmith * 4338061Smsmith * TODO: 4438061Smsmith * Make HDLC/PPP mode, use IF_LLC1 to enable. 4538061Smsmith * 4638061Smsmith * Connect the two computers using a Laplink parallel cable to use this 4738061Smsmith * feature: 4838061Smsmith * 4938061Smsmith * +----------------------------------------+ 5038061Smsmith * |A-name A-End B-End Descr. Port/Bit | 5138061Smsmith * +----------------------------------------+ 5238061Smsmith * |DATA0 2 15 Data 0/0x01 | 5338061Smsmith * |-ERROR 15 2 1/0x08 | 5438061Smsmith * +----------------------------------------+ 5538061Smsmith * |DATA1 3 13 Data 0/0x02 | 5638061Smsmith * |+SLCT 13 3 1/0x10 | 5738061Smsmith * +----------------------------------------+ 5838061Smsmith * |DATA2 4 12 Data 0/0x04 | 5938061Smsmith * |+PE 12 4 1/0x20 | 6038061Smsmith * +----------------------------------------+ 6138061Smsmith * |DATA3 5 10 Strobe 0/0x08 | 6238061Smsmith * |-ACK 10 5 1/0x40 | 6338061Smsmith * +----------------------------------------+ 6438061Smsmith * |DATA4 6 11 Data 0/0x10 | 6538061Smsmith * |BUSY 11 6 1/~0x80 | 6638061Smsmith * +----------------------------------------+ 6738061Smsmith * |GND 18-25 18-25 GND - | 6838061Smsmith * +----------------------------------------+ 6938061Smsmith * 7038061Smsmith * Expect transfer-rates up to 75 kbyte/sec. 7138061Smsmith * 7238061Smsmith * If GCC could correctly grok 7338061Smsmith * register int port asm("edx") 7438061Smsmith * the code would be cleaner 7538061Smsmith * 7638061Smsmith * Poul-Henning Kamp <phk@freebsd.org> 7738061Smsmith */ 7838061Smsmith 7938061Smsmith/* 8038061Smsmith * Update for ppbus, PLIP support only - Nicolas Souchu 81184896Sjhb */ 8238061Smsmith 8355939Snsouch#include "opt_plip.h" 8455939Snsouch 8538061Smsmith#include <sys/param.h> 8638061Smsmith#include <sys/systm.h> 8755939Snsouch#include <sys/module.h> 8855939Snsouch#include <sys/bus.h> 8938061Smsmith#include <sys/mbuf.h> 9038061Smsmith#include <sys/socket.h> 9138061Smsmith#include <sys/sockio.h> 9238061Smsmith#include <sys/kernel.h> 9338061Smsmith#include <sys/malloc.h> 9438061Smsmith 9555939Snsouch#include <machine/bus.h> 9655939Snsouch#include <machine/resource.h> 9755939Snsouch#include <sys/rman.h> 9855939Snsouch 9938061Smsmith#include <net/if.h> 10038061Smsmith#include <net/if_types.h> 10138061Smsmith#include <net/netisr.h> 10238061Smsmith 10338061Smsmith#include <netinet/in.h> 10438061Smsmith#include <netinet/in_var.h> 10538061Smsmith 10638061Smsmith#include <net/bpf.h> 10738061Smsmith 10838061Smsmith#include <dev/ppbus/ppbconf.h> 10955939Snsouch#include "ppbus_if.h" 11055939Snsouch#include <dev/ppbus/ppbio.h> 11138061Smsmith 11238061Smsmith#ifndef LPMTU /* MTU for the lp# interfaces */ 113184896Sjhb#define LPMTU 1500 11438061Smsmith#endif 11538061Smsmith 11638061Smsmith#ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */ 11738061Smsmith#define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */ 11838061Smsmith#endif 11938061Smsmith 12038061Smsmith#ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */ 12138061Smsmith#define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */ 12238061Smsmith#endif 12338061Smsmith 12438061Smsmith#ifndef LPMAXERRS /* Max errors before !RUNNING */ 12538061Smsmith#define LPMAXERRS 100 12638061Smsmith#endif 12738061Smsmith 128185003Sjhb#define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ 12938061Smsmith#define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ 130185003Sjhb#define MLPIPHDRLEN CLPIPHDRLEN 13138061Smsmith 132185003Sjhb#define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */ 13338061Smsmith#define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */ 13438061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN 135185003Sjhb#define MLPIPHDRLEN LPIPHDRLEN 13638061Smsmith#endif 13738061Smsmith 13838061Smsmith#define LPIPTBLSIZE 256 /* Size of octet translation table */ 13938061Smsmith 140185003Sjhb#define lprintf if (lptflag) printf 14143433Snsouch 14243433Snsouch#ifdef PLIP_DEBUG 14338061Smsmithstatic int volatile lptflag = 1; 14443433Snsouch#else 14543433Snsouchstatic int volatile lptflag = 0; 14638061Smsmith#endif 14738061Smsmith 14855939Snsouchstruct lp_data { 149147256Sbrooks struct ifnet *sc_ifp; 150184130Sjhb device_t sc_dev; 15138061Smsmith u_char *sc_ifbuf; 15238061Smsmith int sc_iferrs; 15355939Snsouch 15455939Snsouch struct resource *res_irq; 15538061Smsmith}; 15638061Smsmith 15738061Smsmith/* Tables for the lp# interface */ 15838061Smsmithstatic u_char *txmith; 159185003Sjhb#define txmitl (txmith + (1 * LPIPTBLSIZE)) 160185003Sjhb#define trecvh (txmith + (2 * LPIPTBLSIZE)) 161185003Sjhb#define trecvl (txmith + (3 * LPIPTBLSIZE)) 16238061Smsmith 16338061Smsmithstatic u_char *ctxmith; 164185003Sjhb#define ctxmitl (ctxmith + (1 * LPIPTBLSIZE)) 165185003Sjhb#define ctrecvh (ctxmith + (2 * LPIPTBLSIZE)) 166185003Sjhb#define ctrecvl (ctxmith + (3 * LPIPTBLSIZE)) 16738061Smsmith 16838061Smsmith/* Functions for the lp# interface */ 16938061Smsmithstatic int lpinittables(void); 17038061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t); 17138061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 17238061Smsmith struct rtentry *); 17355939Snsouchstatic void lp_intr(void *); 17438061Smsmith 175185003Sjhb#define DEVTOSOFTC(dev) \ 17655939Snsouch ((struct lp_data *)device_get_softc(dev)) 17738061Smsmith 17855939Snsouchstatic devclass_t lp_devclass; 17955939Snsouch 18056455Speterstatic void 18156455Speterlp_identify(driver_t *driver, device_t parent) 18256455Speter{ 183127189Sguido device_t dev; 18455939Snsouch 185155606Sjhb dev = device_find_child(parent, "plip", -1); 186127189Sguido if (!dev) 187127189Sguido BUS_ADD_CHILD(parent, 0, "plip", -1); 18856455Speter} 189184896Sjhb 19055939Snsouchstatic int 19155939Snsouchlp_probe(device_t dev) 19238061Smsmith{ 19338061Smsmith 19455939Snsouch device_set_desc(dev, "PLIP network interface"); 19538061Smsmith 19655939Snsouch return (0); 19738061Smsmith} 19838061Smsmith 19938061Smsmithstatic int 200184896Sjhblp_attach(device_t dev) 20138061Smsmith{ 20255939Snsouch struct lp_data *lp = DEVTOSOFTC(dev); 203147256Sbrooks struct ifnet *ifp; 204183053Sjhb int rid = 0; 20538061Smsmith 206184130Sjhb lp->sc_dev = dev; 207184130Sjhb 208183053Sjhb /* 209183053Sjhb * Reserve the interrupt resource. If we don't have one, the 210183053Sjhb * attach fails. 211183053Sjhb */ 212184896Sjhb lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 213183053Sjhb RF_SHAREABLE); 214183053Sjhb if (lp->res_irq == 0) { 215183053Sjhb device_printf(dev, "cannot reserve interrupt, failed.\n"); 216183053Sjhb return (ENXIO); 217183053Sjhb } 218183053Sjhb 219147256Sbrooks ifp = lp->sc_ifp = if_alloc(IFT_PARA); 220147256Sbrooks if (ifp == NULL) { 221147256Sbrooks return (ENOSPC); 222147256Sbrooks } 223147256Sbrooks 22455939Snsouch ifp->if_softc = lp; 225121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 22638061Smsmith ifp->if_mtu = LPMTU; 227133695Srwatson ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST | 228133695Srwatson IFF_NEEDSGIANT; 22938061Smsmith ifp->if_ioctl = lpioctl; 23038061Smsmith ifp->if_output = lpoutput; 23138061Smsmith ifp->if_hdrlen = 0; 23238061Smsmith ifp->if_addrlen = 0; 23338061Smsmith ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 23438061Smsmith if_attach(ifp); 23538061Smsmith 23643773Sdes bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); 23738061Smsmith 23855939Snsouch return (0); 23938061Smsmith} 24038061Smsmith/* 24138061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol. 24238061Smsmith * We don't want to calculate these nasties in our tight loop, so we 24338061Smsmith * precalculate them when we initialize. 24438061Smsmith */ 24538061Smsmithstatic int 246184896Sjhblpinittables(void) 24738061Smsmith{ 248184896Sjhb int i; 24938061Smsmith 250184896Sjhb if (txmith == NULL) 251184896Sjhb txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 25238061Smsmith 253184896Sjhb if (txmith == NULL) 254184896Sjhb return (1); 25538061Smsmith 256184896Sjhb if (ctxmith == NULL) 257185003Sjhb ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 25838061Smsmith 259184896Sjhb if (ctxmith == NULL) 260184896Sjhb return (1); 26138061Smsmith 262184896Sjhb for (i = 0; i < LPIPTBLSIZE; i++) { 263184896Sjhb ctxmith[i] = (i & 0xF0) >> 4; 264184896Sjhb ctxmitl[i] = 0x10 | (i & 0x0F); 265184896Sjhb ctrecvh[i] = (i & 0x78) << 1; 266184896Sjhb ctrecvl[i] = (i & 0x78) >> 3; 267184896Sjhb } 26838061Smsmith 269184896Sjhb for (i = 0; i < LPIPTBLSIZE; i++) { 270184896Sjhb txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; 271184896Sjhb txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); 272184896Sjhb trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); 273184896Sjhb trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); 274184896Sjhb } 27538061Smsmith 276184896Sjhb return (0); 27738061Smsmith} 27838061Smsmith 27938061Smsmith/* 28038061Smsmith * Process an ioctl request. 28138061Smsmith */ 28238061Smsmithstatic int 283184896Sjhblpioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 28438061Smsmith{ 285184896Sjhb struct lp_data *sc = ifp->if_softc; 286184896Sjhb device_t dev = sc->sc_dev; 287184896Sjhb device_t ppbus = device_get_parent(dev); 288184896Sjhb struct ifaddr *ifa = (struct ifaddr *)data; 289184896Sjhb struct ifreq *ifr = (struct ifreq *)data; 290184896Sjhb u_char *ptr; 291184896Sjhb void *ih; 292184896Sjhb int error; 29338061Smsmith 294184896Sjhb switch (cmd) { 295184896Sjhb case SIOCSIFDSTADDR: 296184896Sjhb case SIOCAIFADDR: 297184896Sjhb case SIOCSIFADDR: 298184896Sjhb if (ifa->ifa_addr->sa_family != AF_INET) 299184896Sjhb return (EAFNOSUPPORT); 30038061Smsmith 301184896Sjhb ifp->if_flags |= IFF_UP; 302184896Sjhb /* FALLTHROUGH */ 303184896Sjhb case SIOCSIFFLAGS: 304184896Sjhb if ((!(ifp->if_flags & IFF_UP)) && 305184896Sjhb (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 30638061Smsmith 307184896Sjhb ppb_wctr(ppbus, 0x00); 308184896Sjhb ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 30938061Smsmith 310184896Sjhb /* IFF_UP is not set, try to release the bus anyway */ 311184896Sjhb ppb_release_bus(ppbus, dev); 312184896Sjhb break; 313184896Sjhb } 314184896Sjhb if (((ifp->if_flags & IFF_UP)) && 315184896Sjhb (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 31638061Smsmith 317184896Sjhb /* XXX 318184896Sjhb * Should the request be interruptible? 319184896Sjhb */ 320184896Sjhb if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT | 321184896Sjhb PPB_INTR))) 322184896Sjhb return (error); 32338061Smsmith 324184896Sjhb /* Now IFF_UP means that we own the bus */ 325184896Sjhb ppb_set_mode(ppbus, PPB_COMPATIBLE); 32638061Smsmith 327184896Sjhb if (lpinittables()) { 328184896Sjhb ppb_release_bus(ppbus, dev); 329184896Sjhb return (ENOBUFS); 330184896Sjhb } 33142443Snsouch 332184896Sjhb sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN, 333184896Sjhb M_DEVBUF, M_WAITOK); 334184896Sjhb if (sc->sc_ifbuf == NULL) { 335184896Sjhb ppb_release_bus(ppbus, dev); 336184896Sjhb return (ENOBUFS); 337184896Sjhb } 33842443Snsouch 339184896Sjhb /* 340184896Sjhb * Attach our interrupt handler. It is 341184896Sjhb * detached later when the bus is released. 342184896Sjhb */ 343184896Sjhb if ((error = bus_setup_intr(dev, sc->res_irq, 344184896Sjhb INTR_TYPE_NET, NULL, lp_intr, dev, &ih))) { 345184896Sjhb ppb_release_bus(ppbus, dev); 346184896Sjhb return (error); 347184896Sjhb } 34838061Smsmith 349184896Sjhb ppb_wctr(ppbus, IRQENABLE); 350184896Sjhb ifp->if_drv_flags |= IFF_DRV_RUNNING; 351184896Sjhb } 352184896Sjhb break; 35338061Smsmith 354184896Sjhb case SIOCSIFMTU: 355184896Sjhb ptr = sc->sc_ifbuf; 356184896Sjhb sc->sc_ifbuf = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF, 357184896Sjhb M_NOWAIT); 358184896Sjhb if (sc->sc_ifbuf == NULL) { 359184896Sjhb sc->sc_ifbuf = ptr; 360184896Sjhb return (ENOBUFS); 361184896Sjhb } 362184896Sjhb if (ptr) 363184896Sjhb free(ptr, M_DEVBUF); 364184896Sjhb sc->sc_ifp->if_mtu = ifr->ifr_mtu; 365184896Sjhb break; 36655939Snsouch 367184896Sjhb case SIOCGIFMTU: 368184896Sjhb ifr->ifr_mtu = sc->sc_ifp->if_mtu; 369184896Sjhb break; 37038061Smsmith 371184896Sjhb case SIOCADDMULTI: 372184896Sjhb case SIOCDELMULTI: 373184896Sjhb if (ifr == 0) { 374184896Sjhb return (EAFNOSUPPORT); /* XXX */ 375184896Sjhb } 376184896Sjhb switch (ifr->ifr_addr.sa_family) { 377184896Sjhb case AF_INET: 378184896Sjhb break; 379184896Sjhb default: 380184896Sjhb return (EAFNOSUPPORT); 381184896Sjhb } 382184896Sjhb break; 38338061Smsmith 384184896Sjhb case SIOCGIFMEDIA: 385184896Sjhb /* 386184896Sjhb * No ifmedia support at this stage; maybe use it 387184896Sjhb * in future for eg. protocol selection. 388184896Sjhb */ 389184896Sjhb return (EINVAL); 39038061Smsmith 39138061Smsmith default: 392184896Sjhb lprintf("LP:ioctl(0x%lx)\n", cmd); 393184896Sjhb return (EINVAL); 39438061Smsmith } 395184896Sjhb return (0); 39638061Smsmith} 39738061Smsmith 39838061Smsmithstatic __inline int 399184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus) 40038061Smsmith{ 401184896Sjhb 40255939Snsouch ppb_wdtr(ppbus, ctxmitl[byte]); 40355939Snsouch while (ppb_rstr(ppbus) & CLPIP_SHAKE) 40438061Smsmith if (--spin == 0) { 405184896Sjhb return (1); 40638061Smsmith } 40755939Snsouch ppb_wdtr(ppbus, ctxmith[byte]); 40855939Snsouch while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 40938061Smsmith if (--spin == 0) { 410184896Sjhb return (1); 41138061Smsmith } 412184896Sjhb return (0); 41338061Smsmith} 41438061Smsmith 41538061Smsmithstatic __inline int 416184896Sjhbclpinbyte(int spin, device_t ppbus) 41738061Smsmith{ 41842443Snsouch u_char c, cl; 41938061Smsmith 42055939Snsouch while((ppb_rstr(ppbus) & CLPIP_SHAKE)) 421184896Sjhb if (!--spin) { 422184896Sjhb return (-1); 423184896Sjhb } 42455939Snsouch cl = ppb_rstr(ppbus); 42555939Snsouch ppb_wdtr(ppbus, 0x10); 42638061Smsmith 42755939Snsouch while(!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 428184896Sjhb if (!--spin) { 429184896Sjhb return (-1); 430184896Sjhb } 43155939Snsouch c = ppb_rstr(ppbus); 43255939Snsouch ppb_wdtr(ppbus, 0x00); 43338061Smsmith 43438061Smsmith return (ctrecvl[cl] | ctrecvh[c]); 43538061Smsmith} 43638061Smsmith 43738061Smsmithstatic void 43843773Sdeslptap(struct ifnet *ifp, struct mbuf *m) 43943773Sdes{ 44043773Sdes u_int32_t af = AF_INET; 441184896Sjhb 442165640Sjhb bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); 44343773Sdes} 44443773Sdes 44543773Sdesstatic void 446184896Sjhblp_intr(void *arg) 44738061Smsmith{ 44855939Snsouch device_t dev = (device_t)arg; 449185003Sjhb device_t ppbus = device_get_parent(dev); 45055939Snsouch struct lp_data *sc = DEVTOSOFTC(dev); 45138061Smsmith int len, s, j; 45238061Smsmith u_char *bp; 45338061Smsmith u_char c, cl; 45438061Smsmith struct mbuf *top; 45538061Smsmith 45638061Smsmith s = splhigh(); 45738061Smsmith 458147256Sbrooks if (sc->sc_ifp->if_flags & IFF_LINK0) { 45938061Smsmith 460184896Sjhb /* Ack. the request */ 461184896Sjhb ppb_wdtr(ppbus, 0x01); 46238061Smsmith 463184896Sjhb /* Get the packet length */ 464184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 465184896Sjhb if (j == -1) 466184896Sjhb goto err; 467184896Sjhb len = j; 468184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 469184896Sjhb if (j == -1) 470184896Sjhb goto err; 471184896Sjhb len = len + (j << 8); 472184896Sjhb if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN) 473184896Sjhb goto err; 47438061Smsmith 475184896Sjhb bp = sc->sc_ifbuf; 47638061Smsmith 477184896Sjhb while (len--) { 478184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 479184896Sjhb if (j == -1) { 480184896Sjhb goto err; 481184896Sjhb } 482184896Sjhb *bp++ = j; 483184896Sjhb } 48438061Smsmith 485184896Sjhb /* Get and ignore checksum */ 486184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 487184896Sjhb if (j == -1) { 488184896Sjhb goto err; 489184896Sjhb } 49038061Smsmith 491184896Sjhb len = bp - sc->sc_ifbuf; 492184896Sjhb if (len <= CLPIPHDRLEN) 493184896Sjhb goto err; 494184896Sjhb 495184896Sjhb sc->sc_iferrs = 0; 496184896Sjhb 497184896Sjhb len -= CLPIPHDRLEN; 498184896Sjhb sc->sc_ifp->if_ipackets++; 499184896Sjhb sc->sc_ifp->if_ibytes += len; 500184896Sjhb top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 501184896Sjhb 0); 502184896Sjhb if (top) { 503184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 504184896Sjhb lptap(sc->sc_ifp, top); 505184896Sjhb 506184896Sjhb /* mbuf is free'd on failure. */ 507184896Sjhb netisr_queue(NETISR_IP, top); 508184896Sjhb } 509184896Sjhb goto done; 51038061Smsmith } 51155939Snsouch while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { 512184896Sjhb len = sc->sc_ifp->if_mtu + LPIPHDRLEN; 513184896Sjhb bp = sc->sc_ifbuf; 514184896Sjhb while (len--) { 51538061Smsmith 516184896Sjhb cl = ppb_rstr(ppbus); 517184896Sjhb ppb_wdtr(ppbus, 8); 51838061Smsmith 519184896Sjhb j = LPMAXSPIN2; 520184896Sjhb while((ppb_rstr(ppbus) & LPIP_SHAKE)) 521184896Sjhb if (!--j) 522184896Sjhb goto err; 52338061Smsmith 524184896Sjhb c = ppb_rstr(ppbus); 525184896Sjhb ppb_wdtr(ppbus, 0); 52638061Smsmith 527184896Sjhb *bp++= trecvh[cl] | trecvl[c]; 52838061Smsmith 529184896Sjhb j = LPMAXSPIN2; 530184896Sjhb while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) { 531184896Sjhb if (cl != c && 532184896Sjhb (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == 533184896Sjhb (c & 0xf8)) 534184896Sjhb goto end; 535184896Sjhb if (!--j) 536184896Sjhb goto err; 537184896Sjhb } 53838061Smsmith } 53938061Smsmith 54038061Smsmith end: 541184896Sjhb len = bp - sc->sc_ifbuf; 542184896Sjhb if (len <= LPIPHDRLEN) 543184896Sjhb goto err; 54438061Smsmith 545184896Sjhb sc->sc_iferrs = 0; 54638061Smsmith 547184896Sjhb len -= LPIPHDRLEN; 548184896Sjhb sc->sc_ifp->if_ipackets++; 549184896Sjhb sc->sc_ifp->if_ibytes += len; 550184896Sjhb top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 551184896Sjhb 0); 552184896Sjhb if (top) { 553184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 554184896Sjhb lptap(sc->sc_ifp, top); 555184896Sjhb 556184896Sjhb /* mbuf is free'd on failure. */ 557184896Sjhb netisr_queue(NETISR_IP, top); 558184896Sjhb } 55938061Smsmith } 56038061Smsmith goto done; 56138061Smsmith 562185003Sjhberr: 56355939Snsouch ppb_wdtr(ppbus, 0); 56438061Smsmith lprintf("R"); 565147256Sbrooks sc->sc_ifp->if_ierrors++; 56638061Smsmith sc->sc_iferrs++; 56738061Smsmith 56838061Smsmith /* 56938061Smsmith * We are not able to send receive anything for now, 57038061Smsmith * so stop wasting our time 57138061Smsmith */ 57238061Smsmith if (sc->sc_iferrs > LPMAXERRS) { 573184896Sjhb if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n"); 574184896Sjhb ppb_wctr(ppbus, 0x00); 575184896Sjhb sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 576184896Sjhb sc->sc_iferrs = 0; 57738061Smsmith } 57838061Smsmith 579185003Sjhbdone: 58038061Smsmith splx(s); 58138061Smsmith} 58238061Smsmith 58338061Smsmithstatic __inline int 584185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus) 58538061Smsmith{ 586184896Sjhb 587184896Sjhb ppb_wdtr(ppbus, txmith[byte]); 588184896Sjhb while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) 589184896Sjhb if (--spin == 0) 590184896Sjhb return (1); 591184896Sjhb ppb_wdtr(ppbus, txmitl[byte]); 592184896Sjhb while (ppb_rstr(ppbus) & LPIP_SHAKE) 593184896Sjhb if (--spin == 0) 594184896Sjhb return (1); 595184896Sjhb return (0); 59638061Smsmith} 59738061Smsmith 59838061Smsmithstatic int 599184896Sjhblpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 600184896Sjhb struct rtentry *rt) 60138061Smsmith{ 602184896Sjhb struct lp_data *sc = ifp->if_softc; 603184896Sjhb device_t dev = sc->sc_dev; 604184896Sjhb device_t ppbus = device_get_parent(dev); 605184896Sjhb int s, err; 606184896Sjhb struct mbuf *mm; 607184896Sjhb u_char *cp = "\0\0"; 608184896Sjhb u_char chksum = 0; 609184896Sjhb int count = 0; 610184896Sjhb int i, len, spin; 61138061Smsmith 612184896Sjhb /* We need a sensible value if we abort */ 613184896Sjhb cp++; 614184896Sjhb ifp->if_drv_flags |= IFF_DRV_RUNNING; 61538061Smsmith 616184896Sjhb err = 1; /* assume we're aborting because of an error */ 61738061Smsmith 618184896Sjhb s = splhigh(); 61938061Smsmith 620184896Sjhb /* Suspend (on laptops) or receive-errors might have taken us offline */ 621184896Sjhb ppb_wctr(ppbus, IRQENABLE); 62238061Smsmith 623184896Sjhb if (ifp->if_flags & IFF_LINK0) { 624184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 625184896Sjhb lprintf("&"); 626184896Sjhb lp_intr(dev); 627184896Sjhb } 62838061Smsmith 629184896Sjhb /* Alert other end to pending packet */ 630184896Sjhb spin = LPMAXSPIN1; 631184896Sjhb ppb_wdtr(ppbus, 0x08); 632184896Sjhb while ((ppb_rstr(ppbus) & 0x08) == 0) 633184896Sjhb if (--spin == 0) { 634184896Sjhb goto nend; 635184896Sjhb } 63638061Smsmith 637184896Sjhb /* Calculate length of packet, then send that */ 638184896Sjhb 639184896Sjhb count += 14; /* Ethernet header len */ 640184896Sjhb 641184896Sjhb mm = m; 642184896Sjhb for (mm = m; mm; mm = mm->m_next) { 643184896Sjhb count += mm->m_len; 644184896Sjhb } 645184896Sjhb if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) 64638061Smsmith goto nend; 647184896Sjhb if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) 648184896Sjhb goto nend; 649184896Sjhb 650184896Sjhb /* Send dummy ethernet header */ 651184896Sjhb for (i = 0; i < 12; i++) { 652184896Sjhb if (clpoutbyte(i, LPMAXSPIN1, ppbus)) 653184896Sjhb goto nend; 654184896Sjhb chksum += i; 65538061Smsmith } 65638061Smsmith 657184896Sjhb if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) 658184896Sjhb goto nend; 659184896Sjhb if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) 660184896Sjhb goto nend; 661184896Sjhb chksum += 0x08 + 0x00; /* Add into checksum */ 66238061Smsmith 663184896Sjhb mm = m; 664184896Sjhb do { 665184896Sjhb cp = mtod(mm, u_char *); 666184896Sjhb len = mm->m_len; 667184896Sjhb while (len--) { 668184896Sjhb chksum += *cp; 669184896Sjhb if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 670184896Sjhb goto nend; 671184896Sjhb } 672184896Sjhb } while ((mm = mm->m_next)); 67338061Smsmith 674184896Sjhb /* Send checksum */ 675184896Sjhb if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) 676184896Sjhb goto nend; 677184896Sjhb 678184896Sjhb /* Go quiescent */ 679184896Sjhb ppb_wdtr(ppbus, 0); 680184896Sjhb 681184896Sjhb err = 0; /* No errors */ 682184896Sjhb 683184896Sjhb nend: 684184896Sjhb if (err) { /* if we didn't timeout... */ 685184896Sjhb ifp->if_oerrors++; 686184896Sjhb lprintf("X"); 687184896Sjhb } else { 688184896Sjhb ifp->if_opackets++; 689184896Sjhb ifp->if_obytes += m->m_pkthdr.len; 690184896Sjhb if (bpf_peers_present(ifp->if_bpf)) 691184896Sjhb lptap(ifp, m); 692184896Sjhb } 693184896Sjhb 694184896Sjhb m_freem(m); 695184896Sjhb 696184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 697184896Sjhb lprintf("^"); 698184896Sjhb lp_intr(dev); 699184896Sjhb } 700184896Sjhb (void) splx(s); 701184896Sjhb return (0); 70238061Smsmith } 70338061Smsmith 704184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 705184896Sjhb lprintf("&"); 706184896Sjhb lp_intr(dev); 70738061Smsmith } 70838061Smsmith 709184896Sjhb if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) 710184896Sjhb goto end; 711184896Sjhb if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) 712184896Sjhb goto end; 71338061Smsmith 71438061Smsmith mm = m; 71538061Smsmith do { 71638061Smsmith cp = mtod(mm, u_char *); 71743773Sdes len = mm->m_len; 718184896Sjhb while (len--) 719184896Sjhb if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 720184896Sjhb goto end; 72138061Smsmith } while ((mm = mm->m_next)); 72238061Smsmith 723184896Sjhb err = 0; /* no errors were encountered */ 72438061Smsmith 725184896Sjhbend: 726184896Sjhb --cp; 727184896Sjhb ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); 72838061Smsmith 729184896Sjhb if (err) { /* if we didn't timeout... */ 73038061Smsmith ifp->if_oerrors++; 73138061Smsmith lprintf("X"); 73238061Smsmith } else { 73338061Smsmith ifp->if_opackets++; 73438061Smsmith ifp->if_obytes += m->m_pkthdr.len; 735165632Sjhb if (bpf_peers_present(ifp->if_bpf)) 736184896Sjhb lptap(ifp, m); 73738061Smsmith } 73838061Smsmith 73938061Smsmith m_freem(m); 74038061Smsmith 741184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 74238061Smsmith lprintf("^"); 74355939Snsouch lp_intr(dev); 74438061Smsmith } 745184896Sjhb 74638061Smsmith (void) splx(s); 747184896Sjhb return (0); 74838061Smsmith} 74955939Snsouch 75056455Speterstatic device_method_t lp_methods[] = { 75156455Speter /* device interface */ 75256455Speter DEVMETHOD(device_identify, lp_identify), 75356455Speter DEVMETHOD(device_probe, lp_probe), 75456455Speter DEVMETHOD(device_attach, lp_attach), 75556455Speter 75656455Speter { 0, 0 } 75756455Speter}; 75856455Speter 75956455Speterstatic driver_t lp_driver = { 760184896Sjhb "plip", 761184896Sjhb lp_methods, 762184896Sjhb sizeof(struct lp_data), 76356455Speter}; 76456455Speter 76555939SnsouchDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0); 766153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1); 767