if_plip.c revision 183053
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 183053 2008-09-15 22:26:32Z 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 8138061Smsmith */ 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 */ 11338061Smsmith#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 12838061Smsmith#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 */ 13038061Smsmith#define MLPIPHDRLEN CLPIPHDRLEN 13138061Smsmith 13238061Smsmith#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 13538061Smsmith#define MLPIPHDRLEN LPIPHDRLEN 13638061Smsmith#endif 13738061Smsmith 13838061Smsmith#define LPIPTBLSIZE 256 /* Size of octet translation table */ 13938061Smsmith 14038061Smsmith#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; 15038061Smsmith u_char *sc_ifbuf; 15138061Smsmith int sc_iferrs; 15255939Snsouch 15355939Snsouch struct resource *res_irq; 15438061Smsmith}; 15538061Smsmith 15638061Smsmith/* Tables for the lp# interface */ 15738061Smsmithstatic u_char *txmith; 15838061Smsmith#define txmitl (txmith+(1*LPIPTBLSIZE)) 15938061Smsmith#define trecvh (txmith+(2*LPIPTBLSIZE)) 16038061Smsmith#define trecvl (txmith+(3*LPIPTBLSIZE)) 16138061Smsmith 16238061Smsmithstatic u_char *ctxmith; 16338061Smsmith#define ctxmitl (ctxmith+(1*LPIPTBLSIZE)) 16438061Smsmith#define ctrecvh (ctxmith+(2*LPIPTBLSIZE)) 16538061Smsmith#define ctrecvl (ctxmith+(3*LPIPTBLSIZE)) 16638061Smsmith 16738061Smsmith/* Functions for the lp# interface */ 16838061Smsmithstatic int lpinittables(void); 16938061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t); 17038061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 17138061Smsmith struct rtentry *); 17255939Snsouchstatic void lp_intr(void *); 17338061Smsmith 17455939Snsouch#define DEVTOSOFTC(dev) \ 17555939Snsouch ((struct lp_data *)device_get_softc(dev)) 17655939Snsouch#define UNITODEVICE(unit) \ 17755939Snsouch (devclass_get_device(lp_devclass, (unit))) 17838061Smsmith 17955939Snsouchstatic devclass_t lp_devclass; 18055939Snsouch 18156455Speterstatic void 18256455Speterlp_identify(driver_t *driver, device_t parent) 18356455Speter{ 184127189Sguido device_t dev; 18555939Snsouch 186155606Sjhb dev = device_find_child(parent, "plip", -1); 187127189Sguido if (!dev) 188127189Sguido BUS_ADD_CHILD(parent, 0, "plip", -1); 18956455Speter} 19038061Smsmith/* 19138061Smsmith * lpprobe() 19238061Smsmith */ 19355939Snsouchstatic int 19455939Snsouchlp_probe(device_t dev) 19538061Smsmith{ 19638061Smsmith 19755939Snsouch device_set_desc(dev, "PLIP network interface"); 19838061Smsmith 19955939Snsouch return (0); 20038061Smsmith} 20138061Smsmith 20238061Smsmithstatic int 20355939Snsouchlp_attach (device_t dev) 20438061Smsmith{ 20555939Snsouch struct lp_data *lp = DEVTOSOFTC(dev); 206147256Sbrooks struct ifnet *ifp; 207183053Sjhb int rid = 0; 20838061Smsmith 209183053Sjhb /* 210183053Sjhb * Reserve the interrupt resource. If we don't have one, the 211183053Sjhb * attach fails. 212183053Sjhb */ 213183053Sjhb lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 214183053Sjhb RF_SHAREABLE); 215183053Sjhb if (lp->res_irq == 0) { 216183053Sjhb device_printf(dev, "cannot reserve interrupt, failed.\n"); 217183053Sjhb return (ENXIO); 218183053Sjhb } 219183053Sjhb 220147256Sbrooks ifp = lp->sc_ifp = if_alloc(IFT_PARA); 221147256Sbrooks if (ifp == NULL) { 222147256Sbrooks return (ENOSPC); 223147256Sbrooks } 224147256Sbrooks 22555939Snsouch ifp->if_softc = lp; 226121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 22738061Smsmith ifp->if_mtu = LPMTU; 228133695Srwatson ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST | 229133695Srwatson IFF_NEEDSGIANT; 23038061Smsmith ifp->if_ioctl = lpioctl; 23138061Smsmith ifp->if_output = lpoutput; 23238061Smsmith ifp->if_hdrlen = 0; 23338061Smsmith ifp->if_addrlen = 0; 23438061Smsmith ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 23538061Smsmith if_attach(ifp); 23638061Smsmith 23743773Sdes bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); 23838061Smsmith 23955939Snsouch return (0); 24038061Smsmith} 24138061Smsmith/* 24238061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol. 24338061Smsmith * We don't want to calculate these nasties in our tight loop, so we 24438061Smsmith * precalculate them when we initialize. 24538061Smsmith */ 24638061Smsmithstatic int 24738061Smsmithlpinittables (void) 24838061Smsmith{ 24938061Smsmith int i; 25038061Smsmith 25138061Smsmith if (!txmith) 25238061Smsmith txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 25338061Smsmith 25438061Smsmith if (!txmith) 25538061Smsmith return 1; 25638061Smsmith 25738061Smsmith if (!ctxmith) 25838061Smsmith ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 25938061Smsmith 26038061Smsmith if (!ctxmith) 26138061Smsmith return 1; 26238061Smsmith 26338061Smsmith for (i=0; i < LPIPTBLSIZE; i++) { 26438061Smsmith ctxmith[i] = (i & 0xF0) >> 4; 26538061Smsmith ctxmitl[i] = 0x10 | (i & 0x0F); 26638061Smsmith ctrecvh[i] = (i & 0x78) << 1; 26738061Smsmith ctrecvl[i] = (i & 0x78) >> 3; 26838061Smsmith } 26938061Smsmith 27038061Smsmith for (i=0; i < LPIPTBLSIZE; i++) { 27138061Smsmith txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; 27238061Smsmith txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); 27338061Smsmith trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); 27438061Smsmith trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); 27538061Smsmith } 27638061Smsmith 27738061Smsmith return 0; 27838061Smsmith} 27938061Smsmith 28038061Smsmith/* 28138061Smsmith * Process an ioctl request. 28238061Smsmith */ 28338061Smsmith 28438061Smsmithstatic int 28538061Smsmithlpioctl (struct ifnet *ifp, u_long cmd, caddr_t data) 28638061Smsmith{ 287121816Sbrooks device_t dev = UNITODEVICE(ifp->if_dunit); 28855939Snsouch device_t ppbus = device_get_parent(dev); 28955939Snsouch struct lp_data *sc = DEVTOSOFTC(dev); 29038061Smsmith struct ifaddr *ifa = (struct ifaddr *)data; 29138061Smsmith struct ifreq *ifr = (struct ifreq *)data; 29238061Smsmith u_char *ptr; 29355939Snsouch void *ih; 29438061Smsmith int error; 29538061Smsmith 29638061Smsmith switch (cmd) { 29738061Smsmith 29838061Smsmith case SIOCSIFDSTADDR: 29938061Smsmith case SIOCAIFADDR: 30038061Smsmith case SIOCSIFADDR: 30138061Smsmith if (ifa->ifa_addr->sa_family != AF_INET) 30238061Smsmith return EAFNOSUPPORT; 30338061Smsmith 30438061Smsmith ifp->if_flags |= IFF_UP; 30538061Smsmith /* FALLTHROUGH */ 30638061Smsmith case SIOCSIFFLAGS: 307148887Srwatson if ((!(ifp->if_flags & IFF_UP)) && 308148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 30938061Smsmith 31055939Snsouch ppb_wctr(ppbus, 0x00); 311148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 31238061Smsmith 31338061Smsmith /* IFF_UP is not set, try to release the bus anyway */ 31455939Snsouch ppb_release_bus(ppbus, dev); 31538061Smsmith break; 31638061Smsmith } 317148887Srwatson if (((ifp->if_flags & IFF_UP)) && 318148887Srwatson (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 31938061Smsmith 32042443Snsouch /* XXX 32138061Smsmith * Should the request be interruptible? 32238061Smsmith */ 32355939Snsouch if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR))) 32438061Smsmith return (error); 32538061Smsmith 32642443Snsouch /* Now IFF_UP means that we own the bus */ 32742443Snsouch 32855939Snsouch ppb_set_mode(ppbus, PPB_COMPATIBLE); 32942443Snsouch 33038061Smsmith if (lpinittables()) { 33155939Snsouch ppb_release_bus(ppbus, dev); 33238061Smsmith return ENOBUFS; 33338061Smsmith } 33438061Smsmith 335147256Sbrooks sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN, 336111119Simp M_DEVBUF, M_WAITOK); 33738061Smsmith if (!sc->sc_ifbuf) { 33855939Snsouch ppb_release_bus(ppbus, dev); 33938061Smsmith return ENOBUFS; 34038061Smsmith } 34138061Smsmith 34255939Snsouch /* attach our interrupt handler, later detached when the bus is released */ 343155921Sjhb if ((error = bus_setup_intr(dev, sc->res_irq, 344166901Spiso INTR_TYPE_NET, NULL, lp_intr, dev, &ih))) { 34555939Snsouch ppb_release_bus(ppbus, dev); 34655939Snsouch return (error); 34755939Snsouch } 34855939Snsouch 34955939Snsouch ppb_wctr(ppbus, IRQENABLE); 350148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 35138061Smsmith } 35238061Smsmith break; 35338061Smsmith 35438061Smsmith case SIOCSIFMTU: 35538061Smsmith ptr = sc->sc_ifbuf; 35638061Smsmith sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT); 35738061Smsmith if (!sc->sc_ifbuf) { 35838061Smsmith sc->sc_ifbuf = ptr; 35938061Smsmith return ENOBUFS; 36038061Smsmith } 36138061Smsmith if (ptr) 36238061Smsmith free(ptr,M_DEVBUF); 363147256Sbrooks sc->sc_ifp->if_mtu = ifr->ifr_mtu; 36438061Smsmith break; 36538061Smsmith 36638061Smsmith case SIOCGIFMTU: 367147256Sbrooks ifr->ifr_mtu = sc->sc_ifp->if_mtu; 36838061Smsmith break; 36938061Smsmith 37038061Smsmith case SIOCADDMULTI: 37138061Smsmith case SIOCDELMULTI: 37238061Smsmith if (ifr == 0) { 37338061Smsmith return EAFNOSUPPORT; /* XXX */ 37438061Smsmith } 37538061Smsmith switch (ifr->ifr_addr.sa_family) { 37638061Smsmith 37738061Smsmith case AF_INET: 37838061Smsmith break; 37938061Smsmith 38038061Smsmith default: 38138061Smsmith return EAFNOSUPPORT; 38238061Smsmith } 38338061Smsmith break; 38438061Smsmith 38540626Smsmith case SIOCGIFMEDIA: 38640626Smsmith /* 38740626Smsmith * No ifmedia support at this stage; maybe use it 38840626Smsmith * in future for eg. protocol selection. 38940626Smsmith */ 39040626Smsmith return EINVAL; 39140626Smsmith 39238061Smsmith default: 39338373Sbde lprintf("LP:ioctl(0x%lx)\n", cmd); 39438061Smsmith return EINVAL; 39538061Smsmith } 39638061Smsmith return 0; 39738061Smsmith} 39838061Smsmith 39938061Smsmithstatic __inline int 40055939Snsouchclpoutbyte (u_char byte, int spin, device_t ppbus) 40138061Smsmith{ 40255939Snsouch ppb_wdtr(ppbus, ctxmitl[byte]); 40355939Snsouch while (ppb_rstr(ppbus) & CLPIP_SHAKE) 40438061Smsmith if (--spin == 0) { 40538061Smsmith return 1; 40638061Smsmith } 40755939Snsouch ppb_wdtr(ppbus, ctxmith[byte]); 40855939Snsouch while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 40938061Smsmith if (--spin == 0) { 41038061Smsmith return 1; 41138061Smsmith } 41238061Smsmith return 0; 41338061Smsmith} 41438061Smsmith 41538061Smsmithstatic __inline int 41655939Snsouchclpinbyte (int spin, device_t ppbus) 41738061Smsmith{ 41842443Snsouch u_char c, cl; 41938061Smsmith 42055939Snsouch while((ppb_rstr(ppbus) & CLPIP_SHAKE)) 42138061Smsmith if(!--spin) { 42238061Smsmith return -1; 42338061Smsmith } 42455939Snsouch cl = ppb_rstr(ppbus); 42555939Snsouch ppb_wdtr(ppbus, 0x10); 42638061Smsmith 42755939Snsouch while(!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 42838061Smsmith if(!--spin) { 42938061Smsmith return -1; 43038061Smsmith } 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; 441165640Sjhb bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); 44243773Sdes} 44343773Sdes 44443773Sdesstatic void 44555939Snsouchlp_intr (void *arg) 44638061Smsmith{ 44755939Snsouch device_t dev = (device_t)arg; 44855939Snsouch device_t ppbus = device_get_parent(dev); 44955939Snsouch struct lp_data *sc = DEVTOSOFTC(dev); 45038061Smsmith int len, s, j; 45138061Smsmith u_char *bp; 45238061Smsmith u_char c, cl; 45338061Smsmith struct mbuf *top; 45438061Smsmith 45538061Smsmith s = splhigh(); 45638061Smsmith 457147256Sbrooks if (sc->sc_ifp->if_flags & IFF_LINK0) { 45838061Smsmith 45938061Smsmith /* Ack. the request */ 46055939Snsouch ppb_wdtr(ppbus, 0x01); 46138061Smsmith 46238061Smsmith /* Get the packet length */ 46355939Snsouch j = clpinbyte(LPMAXSPIN2, ppbus); 46438061Smsmith if (j == -1) 46538061Smsmith goto err; 46638061Smsmith len = j; 46755939Snsouch j = clpinbyte(LPMAXSPIN2, ppbus); 46838061Smsmith if (j == -1) 46938061Smsmith goto err; 47038061Smsmith len = len + (j << 8); 471147256Sbrooks if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN) 47238061Smsmith goto err; 47338061Smsmith 47438061Smsmith bp = sc->sc_ifbuf; 47538061Smsmith 47638061Smsmith while (len--) { 47755939Snsouch j = clpinbyte(LPMAXSPIN2, ppbus); 47838061Smsmith if (j == -1) { 47938061Smsmith goto err; 48038061Smsmith } 48138061Smsmith *bp++ = j; 48238061Smsmith } 48338061Smsmith /* Get and ignore checksum */ 48455939Snsouch j = clpinbyte(LPMAXSPIN2, ppbus); 48538061Smsmith if (j == -1) { 48638061Smsmith goto err; 48738061Smsmith } 48838061Smsmith 48938061Smsmith len = bp - sc->sc_ifbuf; 49038061Smsmith if (len <= CLPIPHDRLEN) 49138061Smsmith goto err; 49238061Smsmith 49338061Smsmith sc->sc_iferrs = 0; 49438061Smsmith 49538061Smsmith len -= CLPIPHDRLEN; 496147256Sbrooks sc->sc_ifp->if_ipackets++; 497147256Sbrooks sc->sc_ifp->if_ibytes += len; 498147256Sbrooks top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 0); 49938061Smsmith if (top) { 500165632Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 501147256Sbrooks lptap(sc->sc_ifp, top); 502134391Sandre netisr_queue(NETISR_IP, top); /* mbuf is free'd on failure. */ 50338061Smsmith } 50438061Smsmith goto done; 50538061Smsmith } 50655939Snsouch while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { 507147256Sbrooks len = sc->sc_ifp->if_mtu + LPIPHDRLEN; 50838061Smsmith bp = sc->sc_ifbuf; 50938061Smsmith while (len--) { 51038061Smsmith 51155939Snsouch cl = ppb_rstr(ppbus); 51255939Snsouch ppb_wdtr(ppbus, 8); 51338061Smsmith 51438061Smsmith j = LPMAXSPIN2; 51555939Snsouch while((ppb_rstr(ppbus) & LPIP_SHAKE)) 51638061Smsmith if(!--j) goto err; 51738061Smsmith 51855939Snsouch c = ppb_rstr(ppbus); 51955939Snsouch ppb_wdtr(ppbus, 0); 52038061Smsmith 52138061Smsmith *bp++= trecvh[cl] | trecvl[c]; 52238061Smsmith 52338061Smsmith j = LPMAXSPIN2; 52455939Snsouch while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) { 52538061Smsmith if (cl != c && 52655939Snsouch (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == 52738061Smsmith (c & 0xf8)) 52838061Smsmith goto end; 52938061Smsmith if (!--j) goto err; 53038061Smsmith } 53138061Smsmith } 53238061Smsmith 53338061Smsmith end: 53438061Smsmith len = bp - sc->sc_ifbuf; 53538061Smsmith if (len <= LPIPHDRLEN) 53638061Smsmith goto err; 53738061Smsmith 53838061Smsmith sc->sc_iferrs = 0; 53938061Smsmith 54038061Smsmith len -= LPIPHDRLEN; 541147256Sbrooks sc->sc_ifp->if_ipackets++; 542147256Sbrooks sc->sc_ifp->if_ibytes += len; 543147256Sbrooks top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 0); 54438061Smsmith if (top) { 545165632Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 546147256Sbrooks lptap(sc->sc_ifp, top); 547134391Sandre netisr_queue(NETISR_IP, top); /* mbuf is free'd on failure. */ 54838061Smsmith } 54938061Smsmith } 55038061Smsmith goto done; 55138061Smsmith 55238061Smsmith err: 55355939Snsouch ppb_wdtr(ppbus, 0); 55438061Smsmith lprintf("R"); 555147256Sbrooks sc->sc_ifp->if_ierrors++; 55638061Smsmith sc->sc_iferrs++; 55738061Smsmith 55838061Smsmith /* 55938061Smsmith * We are not able to send receive anything for now, 56038061Smsmith * so stop wasting our time 56138061Smsmith */ 56238061Smsmith if (sc->sc_iferrs > LPMAXERRS) { 56355939Snsouch printf("lp%d: Too many errors, Going off-line.\n", device_get_unit(dev)); 56455939Snsouch ppb_wctr(ppbus, 0x00); 565148887Srwatson sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 56638061Smsmith sc->sc_iferrs=0; 56738061Smsmith } 56838061Smsmith 56938061Smsmith done: 57038061Smsmith splx(s); 57138061Smsmith return; 57238061Smsmith} 57338061Smsmith 57438061Smsmithstatic __inline int 57555939Snsouchlpoutbyte (u_char byte, int spin, device_t ppbus) 57638061Smsmith{ 57755939Snsouch ppb_wdtr(ppbus, txmith[byte]); 57855939Snsouch while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) 57938061Smsmith if (--spin == 0) 58038061Smsmith return 1; 58155939Snsouch ppb_wdtr(ppbus, txmitl[byte]); 58255939Snsouch while (ppb_rstr(ppbus) & LPIP_SHAKE) 58338061Smsmith if (--spin == 0) 58438061Smsmith return 1; 58538061Smsmith return 0; 58638061Smsmith} 58738061Smsmith 58838061Smsmithstatic int 58938061Smsmithlpoutput (struct ifnet *ifp, struct mbuf *m, 59038061Smsmith struct sockaddr *dst, struct rtentry *rt) 59138061Smsmith{ 592121816Sbrooks device_t dev = UNITODEVICE(ifp->if_dunit); 59355939Snsouch device_t ppbus = device_get_parent(dev); 59438061Smsmith int s, err; 59538061Smsmith struct mbuf *mm; 59638061Smsmith u_char *cp = "\0\0"; 59738061Smsmith u_char chksum = 0; 59838061Smsmith int count = 0; 59943773Sdes int i, len, spin; 60038061Smsmith 60138061Smsmith /* We need a sensible value if we abort */ 60238061Smsmith cp++; 603148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 60438061Smsmith 60538061Smsmith err = 1; /* assume we're aborting because of an error */ 60638061Smsmith 60738061Smsmith s = splhigh(); 60838061Smsmith 60938061Smsmith /* Suspend (on laptops) or receive-errors might have taken us offline */ 61055939Snsouch ppb_wctr(ppbus, IRQENABLE); 61138061Smsmith 61238061Smsmith if (ifp->if_flags & IFF_LINK0) { 61338061Smsmith 61455939Snsouch if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 61538061Smsmith lprintf("&"); 61655939Snsouch lp_intr(dev); 61738061Smsmith } 61838061Smsmith 61938061Smsmith /* Alert other end to pending packet */ 62038061Smsmith spin = LPMAXSPIN1; 62155939Snsouch ppb_wdtr(ppbus, 0x08); 62255939Snsouch while ((ppb_rstr(ppbus) & 0x08) == 0) 62338061Smsmith if (--spin == 0) { 62438061Smsmith goto nend; 62538061Smsmith } 62638061Smsmith 62738061Smsmith /* Calculate length of packet, then send that */ 62838061Smsmith 62938061Smsmith count += 14; /* Ethernet header len */ 63038061Smsmith 63138061Smsmith mm = m; 63238061Smsmith for (mm = m; mm; mm = mm->m_next) { 63338061Smsmith count += mm->m_len; 63438061Smsmith } 63555939Snsouch if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) 63638061Smsmith goto nend; 63755939Snsouch if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) 63838061Smsmith goto nend; 63938061Smsmith 64038061Smsmith /* Send dummy ethernet header */ 64138061Smsmith for (i = 0; i < 12; i++) { 64255939Snsouch if (clpoutbyte(i, LPMAXSPIN1, ppbus)) 64338061Smsmith goto nend; 64438061Smsmith chksum += i; 64538061Smsmith } 64638061Smsmith 64755939Snsouch if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) 64838061Smsmith goto nend; 64955939Snsouch if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) 65038061Smsmith goto nend; 65138061Smsmith chksum += 0x08 + 0x00; /* Add into checksum */ 65238061Smsmith 65338061Smsmith mm = m; 65438061Smsmith do { 65538061Smsmith cp = mtod(mm, u_char *); 65643773Sdes len = mm->m_len; 65743773Sdes while (len--) { 65838061Smsmith chksum += *cp; 65955939Snsouch if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 66038061Smsmith goto nend; 66138061Smsmith } 66238061Smsmith } while ((mm = mm->m_next)); 66338061Smsmith 66438061Smsmith /* Send checksum */ 66555939Snsouch if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) 66638061Smsmith goto nend; 66738061Smsmith 66838061Smsmith /* Go quiescent */ 66955939Snsouch ppb_wdtr(ppbus, 0); 67038061Smsmith 67138061Smsmith err = 0; /* No errors */ 67238061Smsmith 67338061Smsmith nend: 67438061Smsmith if (err) { /* if we didn't timeout... */ 67538061Smsmith ifp->if_oerrors++; 67638061Smsmith lprintf("X"); 67738061Smsmith } else { 67838061Smsmith ifp->if_opackets++; 67938061Smsmith ifp->if_obytes += m->m_pkthdr.len; 680165632Sjhb if (bpf_peers_present(ifp->if_bpf)) 68143773Sdes lptap(ifp, m); 68238061Smsmith } 68338061Smsmith 68438061Smsmith m_freem(m); 68538061Smsmith 68655939Snsouch if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 68738061Smsmith lprintf("^"); 68855939Snsouch lp_intr(dev); 68938061Smsmith } 69038061Smsmith (void) splx(s); 69138061Smsmith return 0; 69238061Smsmith } 69338061Smsmith 69455939Snsouch if (ppb_rstr(ppbus) & LPIP_SHAKE) { 69538061Smsmith lprintf("&"); 69655939Snsouch lp_intr(dev); 69738061Smsmith } 69838061Smsmith 69955939Snsouch if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) 70038061Smsmith goto end; 70155939Snsouch if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) 70238061Smsmith goto end; 70338061Smsmith 70438061Smsmith mm = m; 70538061Smsmith do { 70638061Smsmith cp = mtod(mm,u_char *); 70743773Sdes len = mm->m_len; 70843773Sdes while (len--) 70955939Snsouch if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 71038061Smsmith goto end; 71138061Smsmith } while ((mm = mm->m_next)); 71238061Smsmith 71338061Smsmith err = 0; /* no errors were encountered */ 71438061Smsmith 71538061Smsmith end: 71638061Smsmith --cp; 71755939Snsouch ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); 71838061Smsmith 71938061Smsmith if (err) { /* if we didn't timeout... */ 72038061Smsmith ifp->if_oerrors++; 72138061Smsmith lprintf("X"); 72238061Smsmith } else { 72338061Smsmith ifp->if_opackets++; 72438061Smsmith ifp->if_obytes += m->m_pkthdr.len; 725165632Sjhb if (bpf_peers_present(ifp->if_bpf)) 72643773Sdes lptap(ifp, m); 72738061Smsmith } 72838061Smsmith 72938061Smsmith m_freem(m); 73038061Smsmith 73155939Snsouch if (ppb_rstr(ppbus) & LPIP_SHAKE) { 73238061Smsmith lprintf("^"); 73355939Snsouch lp_intr(dev); 73438061Smsmith } 73538061Smsmith 73638061Smsmith (void) splx(s); 73738061Smsmith return 0; 73838061Smsmith} 73955939Snsouch 74056455Speterstatic device_method_t lp_methods[] = { 74156455Speter /* device interface */ 74256455Speter DEVMETHOD(device_identify, lp_identify), 74356455Speter DEVMETHOD(device_probe, lp_probe), 74456455Speter DEVMETHOD(device_attach, lp_attach), 74556455Speter 74656455Speter { 0, 0 } 74756455Speter}; 74856455Speter 74956455Speterstatic driver_t lp_driver = { 75056455Speter "plip", 75156455Speter lp_methods, 75256455Speter sizeof(struct lp_data), 75356455Speter}; 75456455Speter 75555939SnsouchDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0); 756153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1); 757