146f3ff79SMike Smith /*- 246f3ff79SMike Smith * Copyright (c) 1997 Poul-Henning Kamp 346f3ff79SMike Smith * All rights reserved. 446f3ff79SMike Smith * 546f3ff79SMike Smith * Redistribution and use in source and binary forms, with or without 646f3ff79SMike Smith * modification, are permitted provided that the following conditions 746f3ff79SMike Smith * are met: 846f3ff79SMike Smith * 1. Redistributions of source code must retain the above copyright 946f3ff79SMike Smith * notice, this list of conditions and the following disclaimer. 1046f3ff79SMike Smith * 2. Redistributions in binary form must reproduce the above copyright 1146f3ff79SMike Smith * notice, this list of conditions and the following disclaimer in the 1246f3ff79SMike Smith * documentation and/or other materials provided with the distribution. 1346f3ff79SMike Smith * 1446f3ff79SMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1546f3ff79SMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1646f3ff79SMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1746f3ff79SMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1846f3ff79SMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1946f3ff79SMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2046f3ff79SMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2146f3ff79SMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2246f3ff79SMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2346f3ff79SMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2446f3ff79SMike Smith * SUCH DAMAGE. 2546f3ff79SMike Smith * 2646f3ff79SMike Smith * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp 2746f3ff79SMike Smith */ 2846f3ff79SMike Smith 29aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 30aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 31aad970f1SDavid E. O'Brien 3246f3ff79SMike Smith /* 3346f3ff79SMike Smith * Parallel port TCP/IP interfaces added. I looked at the driver from 3446f3ff79SMike Smith * MACH but this is a complete rewrite, and btw. incompatible, and it 3546f3ff79SMike Smith * should perform better too. I have never run the MACH driver though. 3646f3ff79SMike Smith * 3746f3ff79SMike Smith * This driver sends two bytes (0x08, 0x00) in front of each packet, 3846f3ff79SMike Smith * to allow us to distinguish another format later. 3946f3ff79SMike Smith * 40d64ada50SJens Schweikhardt * Now added a Linux/Crynwr compatibility mode which is enabled using 4146f3ff79SMike Smith * IF_LINK0 - Tim Wilkinson. 4246f3ff79SMike Smith * 4346f3ff79SMike Smith * TODO: 4446f3ff79SMike Smith * Make HDLC/PPP mode, use IF_LLC1 to enable. 4546f3ff79SMike Smith * 4646f3ff79SMike Smith * Connect the two computers using a Laplink parallel cable to use this 4746f3ff79SMike Smith * feature: 4846f3ff79SMike Smith * 4946f3ff79SMike Smith * +----------------------------------------+ 5046f3ff79SMike Smith * |A-name A-End B-End Descr. Port/Bit | 5146f3ff79SMike Smith * +----------------------------------------+ 5246f3ff79SMike Smith * |DATA0 2 15 Data 0/0x01 | 5346f3ff79SMike Smith * |-ERROR 15 2 1/0x08 | 5446f3ff79SMike Smith * +----------------------------------------+ 5546f3ff79SMike Smith * |DATA1 3 13 Data 0/0x02 | 5646f3ff79SMike Smith * |+SLCT 13 3 1/0x10 | 5746f3ff79SMike Smith * +----------------------------------------+ 5846f3ff79SMike Smith * |DATA2 4 12 Data 0/0x04 | 5946f3ff79SMike Smith * |+PE 12 4 1/0x20 | 6046f3ff79SMike Smith * +----------------------------------------+ 6146f3ff79SMike Smith * |DATA3 5 10 Strobe 0/0x08 | 6246f3ff79SMike Smith * |-ACK 10 5 1/0x40 | 6346f3ff79SMike Smith * +----------------------------------------+ 6446f3ff79SMike Smith * |DATA4 6 11 Data 0/0x10 | 6546f3ff79SMike Smith * |BUSY 11 6 1/~0x80 | 6646f3ff79SMike Smith * +----------------------------------------+ 6746f3ff79SMike Smith * |GND 18-25 18-25 GND - | 6846f3ff79SMike Smith * +----------------------------------------+ 6946f3ff79SMike Smith * 7046f3ff79SMike Smith * Expect transfer-rates up to 75 kbyte/sec. 7146f3ff79SMike Smith * 7246f3ff79SMike Smith * If GCC could correctly grok 7346f3ff79SMike Smith * register int port asm("edx") 7446f3ff79SMike Smith * the code would be cleaner 7546f3ff79SMike Smith * 7646f3ff79SMike Smith * Poul-Henning Kamp <phk@freebsd.org> 7746f3ff79SMike Smith */ 7846f3ff79SMike Smith 7946f3ff79SMike Smith /* 8046f3ff79SMike Smith * Update for ppbus, PLIP support only - Nicolas Souchu 8146f3ff79SMike Smith */ 820f210c92SNicolas Souchu 830f210c92SNicolas Souchu #include "opt_plip.h" 8446f3ff79SMike Smith 8546f3ff79SMike Smith #include <sys/param.h> 8646f3ff79SMike Smith #include <sys/systm.h> 870f210c92SNicolas Souchu #include <sys/module.h> 880f210c92SNicolas Souchu #include <sys/bus.h> 8946f3ff79SMike Smith #include <sys/mbuf.h> 9046f3ff79SMike Smith #include <sys/socket.h> 9146f3ff79SMike Smith #include <sys/sockio.h> 9246f3ff79SMike Smith #include <sys/kernel.h> 9346f3ff79SMike Smith #include <sys/malloc.h> 9446f3ff79SMike Smith 950f210c92SNicolas Souchu #include <machine/bus.h> 960f210c92SNicolas Souchu #include <machine/resource.h> 970f210c92SNicolas Souchu #include <sys/rman.h> 980f210c92SNicolas Souchu 9946f3ff79SMike Smith #include <net/if.h> 10046f3ff79SMike Smith #include <net/if_types.h> 10146f3ff79SMike Smith #include <net/netisr.h> 10246f3ff79SMike Smith 10346f3ff79SMike Smith #include <netinet/in.h> 10446f3ff79SMike Smith #include <netinet/in_var.h> 10546f3ff79SMike Smith 10646f3ff79SMike Smith #include <net/bpf.h> 10746f3ff79SMike Smith 10846f3ff79SMike Smith #include <dev/ppbus/ppbconf.h> 1090f210c92SNicolas Souchu #include "ppbus_if.h" 1100f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 1117b7bf77eSNicolas Souchu 11246f3ff79SMike Smith #ifndef LPMTU /* MTU for the lp# interfaces */ 11346f3ff79SMike Smith #define LPMTU 1500 11446f3ff79SMike Smith #endif 11546f3ff79SMike Smith 11646f3ff79SMike Smith #ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */ 11746f3ff79SMike Smith #define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */ 11846f3ff79SMike Smith #endif 11946f3ff79SMike Smith 12046f3ff79SMike Smith #ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */ 12146f3ff79SMike Smith #define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */ 12246f3ff79SMike Smith #endif 12346f3ff79SMike Smith 12446f3ff79SMike Smith #ifndef LPMAXERRS /* Max errors before !RUNNING */ 12546f3ff79SMike Smith #define LPMAXERRS 100 12646f3ff79SMike Smith #endif 12746f3ff79SMike Smith 12846f3ff79SMike Smith #define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ 12946f3ff79SMike Smith #define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ 13046f3ff79SMike Smith #define MLPIPHDRLEN CLPIPHDRLEN 13146f3ff79SMike Smith 13246f3ff79SMike Smith #define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */ 13346f3ff79SMike Smith #define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */ 13446f3ff79SMike Smith #if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN 13546f3ff79SMike Smith #define MLPIPHDRLEN LPIPHDRLEN 13646f3ff79SMike Smith #endif 13746f3ff79SMike Smith 13846f3ff79SMike Smith #define LPIPTBLSIZE 256 /* Size of octet translation table */ 13946f3ff79SMike Smith 14046f3ff79SMike Smith #define lprintf if (lptflag) printf 14120240fa3SNicolas Souchu 14220240fa3SNicolas Souchu #ifdef PLIP_DEBUG 14346f3ff79SMike Smith static int volatile lptflag = 1; 14420240fa3SNicolas Souchu #else 14520240fa3SNicolas Souchu static int volatile lptflag = 0; 14646f3ff79SMike Smith #endif 14746f3ff79SMike Smith 1480f210c92SNicolas Souchu struct lp_data { 149fc74a9f9SBrooks Davis struct ifnet *sc_ifp; 15046f3ff79SMike Smith u_char *sc_ifbuf; 15146f3ff79SMike Smith int sc_iferrs; 1520f210c92SNicolas Souchu 1530f210c92SNicolas Souchu struct resource *res_irq; 15446f3ff79SMike Smith }; 15546f3ff79SMike Smith 15646f3ff79SMike Smith /* Tables for the lp# interface */ 15746f3ff79SMike Smith static u_char *txmith; 15846f3ff79SMike Smith #define txmitl (txmith+(1*LPIPTBLSIZE)) 15946f3ff79SMike Smith #define trecvh (txmith+(2*LPIPTBLSIZE)) 16046f3ff79SMike Smith #define trecvl (txmith+(3*LPIPTBLSIZE)) 16146f3ff79SMike Smith 16246f3ff79SMike Smith static u_char *ctxmith; 16346f3ff79SMike Smith #define ctxmitl (ctxmith+(1*LPIPTBLSIZE)) 16446f3ff79SMike Smith #define ctrecvh (ctxmith+(2*LPIPTBLSIZE)) 16546f3ff79SMike Smith #define ctrecvl (ctxmith+(3*LPIPTBLSIZE)) 16646f3ff79SMike Smith 16746f3ff79SMike Smith /* Functions for the lp# interface */ 16846f3ff79SMike Smith static int lpinittables(void); 16946f3ff79SMike Smith static int lpioctl(struct ifnet *, u_long, caddr_t); 17046f3ff79SMike Smith static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 17146f3ff79SMike Smith struct rtentry *); 1720f210c92SNicolas Souchu static void lp_intr(void *); 17346f3ff79SMike Smith 1740f210c92SNicolas Souchu #define DEVTOSOFTC(dev) \ 1750f210c92SNicolas Souchu ((struct lp_data *)device_get_softc(dev)) 1760f210c92SNicolas Souchu #define UNITOSOFTC(unit) \ 1770f210c92SNicolas Souchu ((struct lp_data *)devclass_get_softc(lp_devclass, (unit))) 1780f210c92SNicolas Souchu #define UNITODEVICE(unit) \ 1790f210c92SNicolas Souchu (devclass_get_device(lp_devclass, (unit))) 18046f3ff79SMike Smith 1810f210c92SNicolas Souchu static devclass_t lp_devclass; 1820f210c92SNicolas Souchu 1830f063508SPeter Wemm static void 1840f063508SPeter Wemm lp_identify(driver_t *driver, device_t parent) 1850f063508SPeter Wemm { 186a5c7e3bbSGuido van Rooij device_t dev; 1870f210c92SNicolas Souchu 188a5c7e3bbSGuido van Rooij dev = device_find_child(parent, "plip", 0); 189a5c7e3bbSGuido van Rooij if (!dev) 190338cad62SBernd Walter BUS_ADD_CHILD(parent, 0, "plip", -1); 1910f063508SPeter Wemm } 19246f3ff79SMike Smith /* 19346f3ff79SMike Smith * lpprobe() 19446f3ff79SMike Smith */ 1950f210c92SNicolas Souchu static int 1960f210c92SNicolas Souchu lp_probe(device_t dev) 19746f3ff79SMike Smith { 1980f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 1990f210c92SNicolas Souchu struct lp_data *lp; 200150630c4SDoug Rabson int zero = 0; 20184a5b015SPeter Wemm uintptr_t irq; 2020f210c92SNicolas Souchu 2030f210c92SNicolas Souchu lp = DEVTOSOFTC(dev); 2040f210c92SNicolas Souchu 2050f210c92SNicolas Souchu /* retrieve the ppbus irq */ 2060f210c92SNicolas Souchu BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 20746f3ff79SMike Smith 20846f3ff79SMike Smith /* if we haven't interrupts, the probe fails */ 2090f210c92SNicolas Souchu if (irq == -1) { 2100f210c92SNicolas Souchu device_printf(dev, "not an interrupt driven port, failed.\n"); 2110f210c92SNicolas Souchu return (ENXIO); 212fdf94d1aSNicolas Souchu } 21346f3ff79SMike Smith 2140f210c92SNicolas Souchu /* reserve the interrupt resource, expecting irq is available to continue */ 2150f210c92SNicolas Souchu lp->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, 2160f210c92SNicolas Souchu RF_SHAREABLE); 2170f210c92SNicolas Souchu if (lp->res_irq == 0) { 2180f210c92SNicolas Souchu device_printf(dev, "cannot reserve interrupt, failed.\n"); 2190f210c92SNicolas Souchu return (ENXIO); 22046f3ff79SMike Smith } 22146f3ff79SMike Smith 22246f3ff79SMike Smith /* 22346f3ff79SMike Smith * lp dependent initialisation. 22446f3ff79SMike Smith */ 22546f3ff79SMike Smith 2260f210c92SNicolas Souchu device_set_desc(dev, "PLIP network interface"); 22746f3ff79SMike Smith 2280f210c92SNicolas Souchu return (0); 22946f3ff79SMike Smith } 23046f3ff79SMike Smith 23146f3ff79SMike Smith static int 2320f210c92SNicolas Souchu lp_attach (device_t dev) 23346f3ff79SMike Smith { 2340f210c92SNicolas Souchu struct lp_data *lp = DEVTOSOFTC(dev); 235fc74a9f9SBrooks Davis struct ifnet *ifp; 236fc74a9f9SBrooks Davis 237fc74a9f9SBrooks Davis ifp = lp->sc_ifp = if_alloc(IFT_PARA); 238fc74a9f9SBrooks Davis if (ifp == NULL) { 239fc74a9f9SBrooks Davis return (ENOSPC); 240fc74a9f9SBrooks Davis } 24146f3ff79SMike Smith 2420f210c92SNicolas Souchu ifp->if_softc = lp; 2439bf40edeSBrooks Davis if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 24446f3ff79SMike Smith ifp->if_mtu = LPMTU; 24568c7bc6dSRobert Watson ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST | 24668c7bc6dSRobert Watson IFF_NEEDSGIANT; 24746f3ff79SMike Smith ifp->if_ioctl = lpioctl; 24846f3ff79SMike Smith ifp->if_output = lpoutput; 24946f3ff79SMike Smith ifp->if_hdrlen = 0; 25046f3ff79SMike Smith ifp->if_addrlen = 0; 25146f3ff79SMike Smith ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 25246f3ff79SMike Smith if_attach(ifp); 25346f3ff79SMike Smith 2548f84257eSDag-Erling Smørgrav bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); 25546f3ff79SMike Smith 2560f210c92SNicolas Souchu return (0); 25746f3ff79SMike Smith } 25846f3ff79SMike Smith /* 25946f3ff79SMike Smith * Build the translation tables for the LPIP (BSD unix) protocol. 26046f3ff79SMike Smith * We don't want to calculate these nasties in our tight loop, so we 26146f3ff79SMike Smith * precalculate them when we initialize. 26246f3ff79SMike Smith */ 26346f3ff79SMike Smith static int 26446f3ff79SMike Smith lpinittables (void) 26546f3ff79SMike Smith { 26646f3ff79SMike Smith int i; 26746f3ff79SMike Smith 26846f3ff79SMike Smith if (!txmith) 26946f3ff79SMike Smith txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 27046f3ff79SMike Smith 27146f3ff79SMike Smith if (!txmith) 27246f3ff79SMike Smith return 1; 27346f3ff79SMike Smith 27446f3ff79SMike Smith if (!ctxmith) 27546f3ff79SMike Smith ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 27646f3ff79SMike Smith 27746f3ff79SMike Smith if (!ctxmith) 27846f3ff79SMike Smith return 1; 27946f3ff79SMike Smith 28046f3ff79SMike Smith for (i=0; i < LPIPTBLSIZE; i++) { 28146f3ff79SMike Smith ctxmith[i] = (i & 0xF0) >> 4; 28246f3ff79SMike Smith ctxmitl[i] = 0x10 | (i & 0x0F); 28346f3ff79SMike Smith ctrecvh[i] = (i & 0x78) << 1; 28446f3ff79SMike Smith ctrecvl[i] = (i & 0x78) >> 3; 28546f3ff79SMike Smith } 28646f3ff79SMike Smith 28746f3ff79SMike Smith for (i=0; i < LPIPTBLSIZE; i++) { 28846f3ff79SMike Smith txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; 28946f3ff79SMike Smith txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); 29046f3ff79SMike Smith trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); 29146f3ff79SMike Smith trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); 29246f3ff79SMike Smith } 29346f3ff79SMike Smith 29446f3ff79SMike Smith return 0; 29546f3ff79SMike Smith } 29646f3ff79SMike Smith 29746f3ff79SMike Smith /* 29846f3ff79SMike Smith * Process an ioctl request. 29946f3ff79SMike Smith */ 30046f3ff79SMike Smith 30146f3ff79SMike Smith static int 30246f3ff79SMike Smith lpioctl (struct ifnet *ifp, u_long cmd, caddr_t data) 30346f3ff79SMike Smith { 3049bf40edeSBrooks Davis device_t dev = UNITODEVICE(ifp->if_dunit); 3050f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 3060f210c92SNicolas Souchu struct lp_data *sc = DEVTOSOFTC(dev); 30746f3ff79SMike Smith struct ifaddr *ifa = (struct ifaddr *)data; 30846f3ff79SMike Smith struct ifreq *ifr = (struct ifreq *)data; 30946f3ff79SMike Smith u_char *ptr; 3100f210c92SNicolas Souchu void *ih; 31146f3ff79SMike Smith int error; 31246f3ff79SMike Smith 31346f3ff79SMike Smith switch (cmd) { 31446f3ff79SMike Smith 31546f3ff79SMike Smith case SIOCSIFDSTADDR: 31646f3ff79SMike Smith case SIOCAIFADDR: 31746f3ff79SMike Smith case SIOCSIFADDR: 31846f3ff79SMike Smith if (ifa->ifa_addr->sa_family != AF_INET) 31946f3ff79SMike Smith return EAFNOSUPPORT; 32046f3ff79SMike Smith 32146f3ff79SMike Smith ifp->if_flags |= IFF_UP; 32246f3ff79SMike Smith /* FALLTHROUGH */ 32346f3ff79SMike Smith case SIOCSIFFLAGS: 32413f4c340SRobert Watson if ((!(ifp->if_flags & IFF_UP)) && 32513f4c340SRobert Watson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 32646f3ff79SMike Smith 3270f210c92SNicolas Souchu ppb_wctr(ppbus, 0x00); 32813f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 32946f3ff79SMike Smith 33046f3ff79SMike Smith /* IFF_UP is not set, try to release the bus anyway */ 3310f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 33246f3ff79SMike Smith break; 33346f3ff79SMike Smith } 33413f4c340SRobert Watson if (((ifp->if_flags & IFF_UP)) && 33513f4c340SRobert Watson (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 33646f3ff79SMike Smith 33724063475SNicolas Souchu /* XXX 33846f3ff79SMike Smith * Should the request be interruptible? 33946f3ff79SMike Smith */ 3400f210c92SNicolas Souchu if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR))) 34146f3ff79SMike Smith return (error); 34246f3ff79SMike Smith 34324063475SNicolas Souchu /* Now IFF_UP means that we own the bus */ 34424063475SNicolas Souchu 3450f210c92SNicolas Souchu ppb_set_mode(ppbus, PPB_COMPATIBLE); 34624063475SNicolas Souchu 34746f3ff79SMike Smith if (lpinittables()) { 3480f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 34946f3ff79SMike Smith return ENOBUFS; 35046f3ff79SMike Smith } 35146f3ff79SMike Smith 352fc74a9f9SBrooks Davis sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN, 353a163d034SWarner Losh M_DEVBUF, M_WAITOK); 35446f3ff79SMike Smith if (!sc->sc_ifbuf) { 3550f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 35646f3ff79SMike Smith return ENOBUFS; 35746f3ff79SMike Smith } 35846f3ff79SMike Smith 3590f210c92SNicolas Souchu /* attach our interrupt handler, later detached when the bus is released */ 3600f210c92SNicolas Souchu if ((error = BUS_SETUP_INTR(ppbus, dev, sc->res_irq, 3610f210c92SNicolas Souchu INTR_TYPE_NET, lp_intr, dev, &ih))) { 3620f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 3630f210c92SNicolas Souchu return (error); 3640f210c92SNicolas Souchu } 3650f210c92SNicolas Souchu 3660f210c92SNicolas Souchu ppb_wctr(ppbus, IRQENABLE); 36713f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 36846f3ff79SMike Smith } 36946f3ff79SMike Smith break; 37046f3ff79SMike Smith 37146f3ff79SMike Smith case SIOCSIFMTU: 37246f3ff79SMike Smith ptr = sc->sc_ifbuf; 37346f3ff79SMike Smith sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT); 37446f3ff79SMike Smith if (!sc->sc_ifbuf) { 37546f3ff79SMike Smith sc->sc_ifbuf = ptr; 37646f3ff79SMike Smith return ENOBUFS; 37746f3ff79SMike Smith } 37846f3ff79SMike Smith if (ptr) 37946f3ff79SMike Smith free(ptr,M_DEVBUF); 380fc74a9f9SBrooks Davis sc->sc_ifp->if_mtu = ifr->ifr_mtu; 38146f3ff79SMike Smith break; 38246f3ff79SMike Smith 38346f3ff79SMike Smith case SIOCGIFMTU: 384fc74a9f9SBrooks Davis ifr->ifr_mtu = sc->sc_ifp->if_mtu; 38546f3ff79SMike Smith break; 38646f3ff79SMike Smith 38746f3ff79SMike Smith case SIOCADDMULTI: 38846f3ff79SMike Smith case SIOCDELMULTI: 38946f3ff79SMike Smith if (ifr == 0) { 39046f3ff79SMike Smith return EAFNOSUPPORT; /* XXX */ 39146f3ff79SMike Smith } 39246f3ff79SMike Smith switch (ifr->ifr_addr.sa_family) { 39346f3ff79SMike Smith 39446f3ff79SMike Smith case AF_INET: 39546f3ff79SMike Smith break; 39646f3ff79SMike Smith 39746f3ff79SMike Smith default: 39846f3ff79SMike Smith return EAFNOSUPPORT; 39946f3ff79SMike Smith } 40046f3ff79SMike Smith break; 40146f3ff79SMike Smith 40280015f13SMike Smith case SIOCGIFMEDIA: 40380015f13SMike Smith /* 40480015f13SMike Smith * No ifmedia support at this stage; maybe use it 40580015f13SMike Smith * in future for eg. protocol selection. 40680015f13SMike Smith */ 40780015f13SMike Smith return EINVAL; 40880015f13SMike Smith 40946f3ff79SMike Smith default: 410162886e2SBruce Evans lprintf("LP:ioctl(0x%lx)\n", cmd); 41146f3ff79SMike Smith return EINVAL; 41246f3ff79SMike Smith } 41346f3ff79SMike Smith return 0; 41446f3ff79SMike Smith } 41546f3ff79SMike Smith 41646f3ff79SMike Smith static __inline int 4170f210c92SNicolas Souchu clpoutbyte (u_char byte, int spin, device_t ppbus) 41846f3ff79SMike Smith { 4190f210c92SNicolas Souchu ppb_wdtr(ppbus, ctxmitl[byte]); 4200f210c92SNicolas Souchu while (ppb_rstr(ppbus) & CLPIP_SHAKE) 42146f3ff79SMike Smith if (--spin == 0) { 42246f3ff79SMike Smith return 1; 42346f3ff79SMike Smith } 4240f210c92SNicolas Souchu ppb_wdtr(ppbus, ctxmith[byte]); 4250f210c92SNicolas Souchu while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 42646f3ff79SMike Smith if (--spin == 0) { 42746f3ff79SMike Smith return 1; 42846f3ff79SMike Smith } 42946f3ff79SMike Smith return 0; 43046f3ff79SMike Smith } 43146f3ff79SMike Smith 43246f3ff79SMike Smith static __inline int 4330f210c92SNicolas Souchu clpinbyte (int spin, device_t ppbus) 43446f3ff79SMike Smith { 43524063475SNicolas Souchu u_char c, cl; 43646f3ff79SMike Smith 4370f210c92SNicolas Souchu while((ppb_rstr(ppbus) & CLPIP_SHAKE)) 43846f3ff79SMike Smith if(!--spin) { 43946f3ff79SMike Smith return -1; 44046f3ff79SMike Smith } 4410f210c92SNicolas Souchu cl = ppb_rstr(ppbus); 4420f210c92SNicolas Souchu ppb_wdtr(ppbus, 0x10); 44346f3ff79SMike Smith 4440f210c92SNicolas Souchu while(!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 44546f3ff79SMike Smith if(!--spin) { 44646f3ff79SMike Smith return -1; 44746f3ff79SMike Smith } 4480f210c92SNicolas Souchu c = ppb_rstr(ppbus); 4490f210c92SNicolas Souchu ppb_wdtr(ppbus, 0x00); 45046f3ff79SMike Smith 45146f3ff79SMike Smith return (ctrecvl[cl] | ctrecvh[c]); 45246f3ff79SMike Smith } 45346f3ff79SMike Smith 4548f84257eSDag-Erling Smørgrav static void 4558f84257eSDag-Erling Smørgrav lptap(struct ifnet *ifp, struct mbuf *m) 4568f84257eSDag-Erling Smørgrav { 4578f84257eSDag-Erling Smørgrav u_int32_t af = AF_INET; 458437ffe18SSam Leffler BPF_MTAP2(ifp, &af, sizeof(af), m); 4598f84257eSDag-Erling Smørgrav } 4608f84257eSDag-Erling Smørgrav 46146f3ff79SMike Smith static void 4620f210c92SNicolas Souchu lp_intr (void *arg) 46346f3ff79SMike Smith { 4640f210c92SNicolas Souchu device_t dev = (device_t)arg; 4650f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 4660f210c92SNicolas Souchu struct lp_data *sc = DEVTOSOFTC(dev); 46746f3ff79SMike Smith int len, s, j; 46846f3ff79SMike Smith u_char *bp; 46946f3ff79SMike Smith u_char c, cl; 47046f3ff79SMike Smith struct mbuf *top; 47146f3ff79SMike Smith 47246f3ff79SMike Smith s = splhigh(); 47346f3ff79SMike Smith 474fc74a9f9SBrooks Davis if (sc->sc_ifp->if_flags & IFF_LINK0) { 47546f3ff79SMike Smith 47646f3ff79SMike Smith /* Ack. the request */ 4770f210c92SNicolas Souchu ppb_wdtr(ppbus, 0x01); 47846f3ff79SMike Smith 47946f3ff79SMike Smith /* Get the packet length */ 4800f210c92SNicolas Souchu j = clpinbyte(LPMAXSPIN2, ppbus); 48146f3ff79SMike Smith if (j == -1) 48246f3ff79SMike Smith goto err; 48346f3ff79SMike Smith len = j; 4840f210c92SNicolas Souchu j = clpinbyte(LPMAXSPIN2, ppbus); 48546f3ff79SMike Smith if (j == -1) 48646f3ff79SMike Smith goto err; 48746f3ff79SMike Smith len = len + (j << 8); 488fc74a9f9SBrooks Davis if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN) 48946f3ff79SMike Smith goto err; 49046f3ff79SMike Smith 49146f3ff79SMike Smith bp = sc->sc_ifbuf; 49246f3ff79SMike Smith 49346f3ff79SMike Smith while (len--) { 4940f210c92SNicolas Souchu j = clpinbyte(LPMAXSPIN2, ppbus); 49546f3ff79SMike Smith if (j == -1) { 49646f3ff79SMike Smith goto err; 49746f3ff79SMike Smith } 49846f3ff79SMike Smith *bp++ = j; 49946f3ff79SMike Smith } 50046f3ff79SMike Smith /* Get and ignore checksum */ 5010f210c92SNicolas Souchu j = clpinbyte(LPMAXSPIN2, ppbus); 50246f3ff79SMike Smith if (j == -1) { 50346f3ff79SMike Smith goto err; 50446f3ff79SMike Smith } 50546f3ff79SMike Smith 50646f3ff79SMike Smith len = bp - sc->sc_ifbuf; 50746f3ff79SMike Smith if (len <= CLPIPHDRLEN) 50846f3ff79SMike Smith goto err; 50946f3ff79SMike Smith 51046f3ff79SMike Smith sc->sc_iferrs = 0; 51146f3ff79SMike Smith 51246f3ff79SMike Smith len -= CLPIPHDRLEN; 513fc74a9f9SBrooks Davis sc->sc_ifp->if_ipackets++; 514fc74a9f9SBrooks Davis sc->sc_ifp->if_ibytes += len; 515fc74a9f9SBrooks Davis top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 0); 51646f3ff79SMike Smith if (top) { 517fc74a9f9SBrooks Davis if (sc->sc_ifp->if_bpf) 518fc74a9f9SBrooks Davis lptap(sc->sc_ifp, top); 5193161f583SAndre Oppermann netisr_queue(NETISR_IP, top); /* mbuf is free'd on failure. */ 520df5e1987SJonathan Lemon } 52146f3ff79SMike Smith goto done; 52246f3ff79SMike Smith } 5230f210c92SNicolas Souchu while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { 524fc74a9f9SBrooks Davis len = sc->sc_ifp->if_mtu + LPIPHDRLEN; 52546f3ff79SMike Smith bp = sc->sc_ifbuf; 52646f3ff79SMike Smith while (len--) { 52746f3ff79SMike Smith 5280f210c92SNicolas Souchu cl = ppb_rstr(ppbus); 5290f210c92SNicolas Souchu ppb_wdtr(ppbus, 8); 53046f3ff79SMike Smith 53146f3ff79SMike Smith j = LPMAXSPIN2; 5320f210c92SNicolas Souchu while((ppb_rstr(ppbus) & LPIP_SHAKE)) 53346f3ff79SMike Smith if(!--j) goto err; 53446f3ff79SMike Smith 5350f210c92SNicolas Souchu c = ppb_rstr(ppbus); 5360f210c92SNicolas Souchu ppb_wdtr(ppbus, 0); 53746f3ff79SMike Smith 53846f3ff79SMike Smith *bp++= trecvh[cl] | trecvl[c]; 53946f3ff79SMike Smith 54046f3ff79SMike Smith j = LPMAXSPIN2; 5410f210c92SNicolas Souchu while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) { 54246f3ff79SMike Smith if (cl != c && 5430f210c92SNicolas Souchu (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == 54446f3ff79SMike Smith (c & 0xf8)) 54546f3ff79SMike Smith goto end; 54646f3ff79SMike Smith if (!--j) goto err; 54746f3ff79SMike Smith } 54846f3ff79SMike Smith } 54946f3ff79SMike Smith 55046f3ff79SMike Smith end: 55146f3ff79SMike Smith len = bp - sc->sc_ifbuf; 55246f3ff79SMike Smith if (len <= LPIPHDRLEN) 55346f3ff79SMike Smith goto err; 55446f3ff79SMike Smith 55546f3ff79SMike Smith sc->sc_iferrs = 0; 55646f3ff79SMike Smith 55746f3ff79SMike Smith len -= LPIPHDRLEN; 558fc74a9f9SBrooks Davis sc->sc_ifp->if_ipackets++; 559fc74a9f9SBrooks Davis sc->sc_ifp->if_ibytes += len; 560fc74a9f9SBrooks Davis top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 0); 56146f3ff79SMike Smith if (top) { 562fc74a9f9SBrooks Davis if (sc->sc_ifp->if_bpf) 563fc74a9f9SBrooks Davis lptap(sc->sc_ifp, top); 5643161f583SAndre Oppermann netisr_queue(NETISR_IP, top); /* mbuf is free'd on failure. */ 56546f3ff79SMike Smith } 566df5e1987SJonathan Lemon } 56746f3ff79SMike Smith goto done; 56846f3ff79SMike Smith 56946f3ff79SMike Smith err: 5700f210c92SNicolas Souchu ppb_wdtr(ppbus, 0); 57146f3ff79SMike Smith lprintf("R"); 572fc74a9f9SBrooks Davis sc->sc_ifp->if_ierrors++; 57346f3ff79SMike Smith sc->sc_iferrs++; 57446f3ff79SMike Smith 57546f3ff79SMike Smith /* 57646f3ff79SMike Smith * We are not able to send receive anything for now, 57746f3ff79SMike Smith * so stop wasting our time 57846f3ff79SMike Smith */ 57946f3ff79SMike Smith if (sc->sc_iferrs > LPMAXERRS) { 5800f210c92SNicolas Souchu printf("lp%d: Too many errors, Going off-line.\n", device_get_unit(dev)); 5810f210c92SNicolas Souchu ppb_wctr(ppbus, 0x00); 58213f4c340SRobert Watson sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 58346f3ff79SMike Smith sc->sc_iferrs=0; 58446f3ff79SMike Smith } 58546f3ff79SMike Smith 58646f3ff79SMike Smith done: 58746f3ff79SMike Smith splx(s); 58846f3ff79SMike Smith return; 58946f3ff79SMike Smith } 59046f3ff79SMike Smith 59146f3ff79SMike Smith static __inline int 5920f210c92SNicolas Souchu lpoutbyte (u_char byte, int spin, device_t ppbus) 59346f3ff79SMike Smith { 5940f210c92SNicolas Souchu ppb_wdtr(ppbus, txmith[byte]); 5950f210c92SNicolas Souchu while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) 59646f3ff79SMike Smith if (--spin == 0) 59746f3ff79SMike Smith return 1; 5980f210c92SNicolas Souchu ppb_wdtr(ppbus, txmitl[byte]); 5990f210c92SNicolas Souchu while (ppb_rstr(ppbus) & LPIP_SHAKE) 60046f3ff79SMike Smith if (--spin == 0) 60146f3ff79SMike Smith return 1; 60246f3ff79SMike Smith return 0; 60346f3ff79SMike Smith } 60446f3ff79SMike Smith 60546f3ff79SMike Smith static int 60646f3ff79SMike Smith lpoutput (struct ifnet *ifp, struct mbuf *m, 60746f3ff79SMike Smith struct sockaddr *dst, struct rtentry *rt) 60846f3ff79SMike Smith { 6099bf40edeSBrooks Davis device_t dev = UNITODEVICE(ifp->if_dunit); 6100f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 61146f3ff79SMike Smith int s, err; 61246f3ff79SMike Smith struct mbuf *mm; 61346f3ff79SMike Smith u_char *cp = "\0\0"; 61446f3ff79SMike Smith u_char chksum = 0; 61546f3ff79SMike Smith int count = 0; 6168f84257eSDag-Erling Smørgrav int i, len, spin; 61746f3ff79SMike Smith 61846f3ff79SMike Smith /* We need a sensible value if we abort */ 61946f3ff79SMike Smith cp++; 62013f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 62146f3ff79SMike Smith 62246f3ff79SMike Smith err = 1; /* assume we're aborting because of an error */ 62346f3ff79SMike Smith 62446f3ff79SMike Smith s = splhigh(); 62546f3ff79SMike Smith 62646f3ff79SMike Smith /* Suspend (on laptops) or receive-errors might have taken us offline */ 6270f210c92SNicolas Souchu ppb_wctr(ppbus, IRQENABLE); 62846f3ff79SMike Smith 62946f3ff79SMike Smith if (ifp->if_flags & IFF_LINK0) { 63046f3ff79SMike Smith 6310f210c92SNicolas Souchu if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 63246f3ff79SMike Smith lprintf("&"); 6330f210c92SNicolas Souchu lp_intr(dev); 63446f3ff79SMike Smith } 63546f3ff79SMike Smith 63646f3ff79SMike Smith /* Alert other end to pending packet */ 63746f3ff79SMike Smith spin = LPMAXSPIN1; 6380f210c92SNicolas Souchu ppb_wdtr(ppbus, 0x08); 6390f210c92SNicolas Souchu while ((ppb_rstr(ppbus) & 0x08) == 0) 64046f3ff79SMike Smith if (--spin == 0) { 64146f3ff79SMike Smith goto nend; 64246f3ff79SMike Smith } 64346f3ff79SMike Smith 64446f3ff79SMike Smith /* Calculate length of packet, then send that */ 64546f3ff79SMike Smith 64646f3ff79SMike Smith count += 14; /* Ethernet header len */ 64746f3ff79SMike Smith 64846f3ff79SMike Smith mm = m; 64946f3ff79SMike Smith for (mm = m; mm; mm = mm->m_next) { 65046f3ff79SMike Smith count += mm->m_len; 65146f3ff79SMike Smith } 6520f210c92SNicolas Souchu if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) 65346f3ff79SMike Smith goto nend; 6540f210c92SNicolas Souchu if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) 65546f3ff79SMike Smith goto nend; 65646f3ff79SMike Smith 65746f3ff79SMike Smith /* Send dummy ethernet header */ 65846f3ff79SMike Smith for (i = 0; i < 12; i++) { 6590f210c92SNicolas Souchu if (clpoutbyte(i, LPMAXSPIN1, ppbus)) 66046f3ff79SMike Smith goto nend; 66146f3ff79SMike Smith chksum += i; 66246f3ff79SMike Smith } 66346f3ff79SMike Smith 6640f210c92SNicolas Souchu if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) 66546f3ff79SMike Smith goto nend; 6660f210c92SNicolas Souchu if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) 66746f3ff79SMike Smith goto nend; 66846f3ff79SMike Smith chksum += 0x08 + 0x00; /* Add into checksum */ 66946f3ff79SMike Smith 67046f3ff79SMike Smith mm = m; 67146f3ff79SMike Smith do { 67246f3ff79SMike Smith cp = mtod(mm, u_char *); 6738f84257eSDag-Erling Smørgrav len = mm->m_len; 6748f84257eSDag-Erling Smørgrav while (len--) { 67546f3ff79SMike Smith chksum += *cp; 6760f210c92SNicolas Souchu if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 67746f3ff79SMike Smith goto nend; 67846f3ff79SMike Smith } 67946f3ff79SMike Smith } while ((mm = mm->m_next)); 68046f3ff79SMike Smith 68146f3ff79SMike Smith /* Send checksum */ 6820f210c92SNicolas Souchu if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) 68346f3ff79SMike Smith goto nend; 68446f3ff79SMike Smith 68546f3ff79SMike Smith /* Go quiescent */ 6860f210c92SNicolas Souchu ppb_wdtr(ppbus, 0); 68746f3ff79SMike Smith 68846f3ff79SMike Smith err = 0; /* No errors */ 68946f3ff79SMike Smith 69046f3ff79SMike Smith nend: 69146f3ff79SMike Smith if (err) { /* if we didn't timeout... */ 69246f3ff79SMike Smith ifp->if_oerrors++; 69346f3ff79SMike Smith lprintf("X"); 69446f3ff79SMike Smith } else { 69546f3ff79SMike Smith ifp->if_opackets++; 69646f3ff79SMike Smith ifp->if_obytes += m->m_pkthdr.len; 6978f84257eSDag-Erling Smørgrav if (ifp->if_bpf) 6988f84257eSDag-Erling Smørgrav lptap(ifp, m); 69946f3ff79SMike Smith } 70046f3ff79SMike Smith 70146f3ff79SMike Smith m_freem(m); 70246f3ff79SMike Smith 7030f210c92SNicolas Souchu if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 70446f3ff79SMike Smith lprintf("^"); 7050f210c92SNicolas Souchu lp_intr(dev); 70646f3ff79SMike Smith } 70746f3ff79SMike Smith (void) splx(s); 70846f3ff79SMike Smith return 0; 70946f3ff79SMike Smith } 71046f3ff79SMike Smith 7110f210c92SNicolas Souchu if (ppb_rstr(ppbus) & LPIP_SHAKE) { 71246f3ff79SMike Smith lprintf("&"); 7130f210c92SNicolas Souchu lp_intr(dev); 71446f3ff79SMike Smith } 71546f3ff79SMike Smith 7160f210c92SNicolas Souchu if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) 71746f3ff79SMike Smith goto end; 7180f210c92SNicolas Souchu if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) 71946f3ff79SMike Smith goto end; 72046f3ff79SMike Smith 72146f3ff79SMike Smith mm = m; 72246f3ff79SMike Smith do { 72346f3ff79SMike Smith cp = mtod(mm,u_char *); 7248f84257eSDag-Erling Smørgrav len = mm->m_len; 7258f84257eSDag-Erling Smørgrav while (len--) 7260f210c92SNicolas Souchu if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 72746f3ff79SMike Smith goto end; 72846f3ff79SMike Smith } while ((mm = mm->m_next)); 72946f3ff79SMike Smith 73046f3ff79SMike Smith err = 0; /* no errors were encountered */ 73146f3ff79SMike Smith 73246f3ff79SMike Smith end: 73346f3ff79SMike Smith --cp; 7340f210c92SNicolas Souchu ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); 73546f3ff79SMike Smith 73646f3ff79SMike Smith if (err) { /* if we didn't timeout... */ 73746f3ff79SMike Smith ifp->if_oerrors++; 73846f3ff79SMike Smith lprintf("X"); 73946f3ff79SMike Smith } else { 74046f3ff79SMike Smith ifp->if_opackets++; 74146f3ff79SMike Smith ifp->if_obytes += m->m_pkthdr.len; 7428f84257eSDag-Erling Smørgrav if (ifp->if_bpf) 7438f84257eSDag-Erling Smørgrav lptap(ifp, m); 74446f3ff79SMike Smith } 74546f3ff79SMike Smith 74646f3ff79SMike Smith m_freem(m); 74746f3ff79SMike Smith 7480f210c92SNicolas Souchu if (ppb_rstr(ppbus) & LPIP_SHAKE) { 74946f3ff79SMike Smith lprintf("^"); 7500f210c92SNicolas Souchu lp_intr(dev); 75146f3ff79SMike Smith } 75246f3ff79SMike Smith 75346f3ff79SMike Smith (void) splx(s); 75446f3ff79SMike Smith return 0; 75546f3ff79SMike Smith } 7560f210c92SNicolas Souchu 7570f063508SPeter Wemm static device_method_t lp_methods[] = { 7580f063508SPeter Wemm /* device interface */ 7590f063508SPeter Wemm DEVMETHOD(device_identify, lp_identify), 7600f063508SPeter Wemm DEVMETHOD(device_probe, lp_probe), 7610f063508SPeter Wemm DEVMETHOD(device_attach, lp_attach), 7620f210c92SNicolas Souchu 7630f063508SPeter Wemm { 0, 0 } 7640f063508SPeter Wemm }; 7650f063508SPeter Wemm 7660f063508SPeter Wemm static driver_t lp_driver = { 7670f063508SPeter Wemm "plip", 7680f063508SPeter Wemm lp_methods, 7690f063508SPeter Wemm sizeof(struct lp_data), 7700f063508SPeter Wemm }; 7710f063508SPeter Wemm 7720f063508SPeter Wemm DRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0); 773f5fd5611SRuslan Ermilov MODULE_DEPEND(plip, ppbus, 1, 1, 1); 774