if_plip.c revision 207554
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 207554 2010-05-03 07:32:50Z sobomax $"); 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> 102191148Skmacy#include <net/route.h> 10338061Smsmith 10438061Smsmith#include <netinet/in.h> 10538061Smsmith#include <netinet/in_var.h> 10638061Smsmith 10738061Smsmith#include <net/bpf.h> 10838061Smsmith 10938061Smsmith#include <dev/ppbus/ppbconf.h> 11055939Snsouch#include "ppbus_if.h" 11155939Snsouch#include <dev/ppbus/ppbio.h> 11238061Smsmith 11338061Smsmith#ifndef LPMTU /* MTU for the lp# interfaces */ 114184896Sjhb#define LPMTU 1500 11538061Smsmith#endif 11638061Smsmith 11738061Smsmith#ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */ 11838061Smsmith#define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */ 11938061Smsmith#endif 12038061Smsmith 12138061Smsmith#ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */ 12238061Smsmith#define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */ 12338061Smsmith#endif 12438061Smsmith 12538061Smsmith#ifndef LPMAXERRS /* Max errors before !RUNNING */ 12638061Smsmith#define LPMAXERRS 100 12738061Smsmith#endif 12838061Smsmith 129185003Sjhb#define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ 13038061Smsmith#define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ 131185003Sjhb#define MLPIPHDRLEN CLPIPHDRLEN 13238061Smsmith 133185003Sjhb#define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */ 13438061Smsmith#define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */ 13538061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN 136185003Sjhb#define MLPIPHDRLEN LPIPHDRLEN 13738061Smsmith#endif 13838061Smsmith 13938061Smsmith#define LPIPTBLSIZE 256 /* Size of octet translation table */ 14038061Smsmith 141185003Sjhb#define lprintf if (lptflag) printf 14243433Snsouch 14343433Snsouch#ifdef PLIP_DEBUG 14438061Smsmithstatic int volatile lptflag = 1; 14543433Snsouch#else 14643433Snsouchstatic int volatile lptflag = 0; 14738061Smsmith#endif 14838061Smsmith 14955939Snsouchstruct lp_data { 150147256Sbrooks struct ifnet *sc_ifp; 151184130Sjhb device_t sc_dev; 15238061Smsmith u_char *sc_ifbuf; 15338061Smsmith int sc_iferrs; 15455939Snsouch 15555939Snsouch struct resource *res_irq; 156187576Sjhb void *sc_intr_cookie; 15738061Smsmith}; 15838061Smsmith 159187576Sjhbstatic struct mtx lp_tables_lock; 160187576SjhbMTX_SYSINIT(lp_tables, &lp_tables_lock, "plip tables", MTX_DEF); 161187576Sjhb 16238061Smsmith/* Tables for the lp# interface */ 16338061Smsmithstatic u_char *txmith; 164185003Sjhb#define txmitl (txmith + (1 * LPIPTBLSIZE)) 165185003Sjhb#define trecvh (txmith + (2 * LPIPTBLSIZE)) 166185003Sjhb#define trecvl (txmith + (3 * LPIPTBLSIZE)) 16738061Smsmith 16838061Smsmithstatic u_char *ctxmith; 169185003Sjhb#define ctxmitl (ctxmith + (1 * LPIPTBLSIZE)) 170185003Sjhb#define ctrecvh (ctxmith + (2 * LPIPTBLSIZE)) 171185003Sjhb#define ctrecvl (ctxmith + (3 * LPIPTBLSIZE)) 17238061Smsmith 17338061Smsmith/* Functions for the lp# interface */ 17438061Smsmithstatic int lpinittables(void); 17538061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t); 17638061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 177191148Skmacy struct route *); 178187576Sjhbstatic void lpstop(struct lp_data *); 17955939Snsouchstatic void lp_intr(void *); 180187576Sjhbstatic int lp_module_handler(module_t, int, void *); 18138061Smsmith 182185003Sjhb#define DEVTOSOFTC(dev) \ 18355939Snsouch ((struct lp_data *)device_get_softc(dev)) 18438061Smsmith 18555939Snsouchstatic devclass_t lp_devclass; 18655939Snsouch 187187576Sjhbstatic int 188187576Sjhblp_module_handler(module_t mod, int what, void *arg) 189187576Sjhb{ 190187576Sjhb 191187576Sjhb switch (what) { 192187576Sjhb case MOD_UNLOAD: 193187576Sjhb mtx_lock(&lp_tables_lock); 194187576Sjhb if (txmith != NULL) { 195187576Sjhb free(txmith, M_DEVBUF); 196187576Sjhb txmith = NULL; 197187576Sjhb } 198187576Sjhb if (ctxmith != NULL) { 199187576Sjhb free(ctxmith, M_DEVBUF); 200187576Sjhb ctxmith = NULL; 201187576Sjhb } 202187576Sjhb mtx_unlock(&lp_tables_lock); 203187576Sjhb break; 204187576Sjhb case MOD_LOAD: 205187576Sjhb case MOD_QUIESCE: 206187576Sjhb break; 207187576Sjhb default: 208187576Sjhb return (EOPNOTSUPP); 209187576Sjhb } 210187576Sjhb return (0); 211187576Sjhb} 212187576Sjhb 21356455Speterstatic void 21456455Speterlp_identify(driver_t *driver, device_t parent) 21556455Speter{ 216127189Sguido device_t dev; 21755939Snsouch 218155606Sjhb dev = device_find_child(parent, "plip", -1); 219127189Sguido if (!dev) 220127189Sguido BUS_ADD_CHILD(parent, 0, "plip", -1); 22156455Speter} 222184896Sjhb 22355939Snsouchstatic int 22455939Snsouchlp_probe(device_t dev) 22538061Smsmith{ 22638061Smsmith 22755939Snsouch device_set_desc(dev, "PLIP network interface"); 22838061Smsmith 22955939Snsouch return (0); 23038061Smsmith} 23138061Smsmith 23238061Smsmithstatic int 233184896Sjhblp_attach(device_t dev) 23438061Smsmith{ 23555939Snsouch struct lp_data *lp = DEVTOSOFTC(dev); 236147256Sbrooks struct ifnet *ifp; 237187576Sjhb int error, rid = 0; 23838061Smsmith 239184130Sjhb lp->sc_dev = dev; 240184130Sjhb 241183053Sjhb /* 242183053Sjhb * Reserve the interrupt resource. If we don't have one, the 243183053Sjhb * attach fails. 244183053Sjhb */ 245184896Sjhb lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 246183053Sjhb RF_SHAREABLE); 247183053Sjhb if (lp->res_irq == 0) { 248183053Sjhb device_printf(dev, "cannot reserve interrupt, failed.\n"); 249183053Sjhb return (ENXIO); 250183053Sjhb } 251183053Sjhb 252147256Sbrooks ifp = lp->sc_ifp = if_alloc(IFT_PARA); 253147256Sbrooks if (ifp == NULL) { 254147256Sbrooks return (ENOSPC); 255147256Sbrooks } 256147256Sbrooks 25755939Snsouch ifp->if_softc = lp; 258121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 25938061Smsmith ifp->if_mtu = LPMTU; 260187576Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 26138061Smsmith ifp->if_ioctl = lpioctl; 26238061Smsmith ifp->if_output = lpoutput; 26338061Smsmith ifp->if_hdrlen = 0; 26438061Smsmith ifp->if_addrlen = 0; 265207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 26638061Smsmith if_attach(ifp); 26738061Smsmith 26843773Sdes bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); 26938061Smsmith 270187576Sjhb /* 271187576Sjhb * Attach our interrupt handler. It is only called while we 272187576Sjhb * own the ppbus. 273187576Sjhb */ 274187576Sjhb error = bus_setup_intr(dev, lp->res_irq, INTR_TYPE_NET | INTR_MPSAFE, 275187576Sjhb NULL, lp_intr, lp, &lp->sc_intr_cookie); 276187576Sjhb if (error) { 277187576Sjhb bpfdetach(ifp); 278187576Sjhb if_detach(ifp); 279187576Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, lp->res_irq); 280187576Sjhb device_printf(dev, "Unable to register interrupt handler\n"); 281187576Sjhb return (error); 282187576Sjhb } 283187576Sjhb 28455939Snsouch return (0); 28538061Smsmith} 286187576Sjhb 287187576Sjhbstatic int 288187576Sjhblp_detach(device_t dev) 289187576Sjhb{ 290187576Sjhb struct lp_data *sc = device_get_softc(dev); 291187576Sjhb device_t ppbus = device_get_parent(dev); 292187576Sjhb 293187576Sjhb ppb_lock(ppbus); 294187576Sjhb lpstop(sc); 295187576Sjhb ppb_unlock(ppbus); 296187576Sjhb bpfdetach(sc->sc_ifp); 297187576Sjhb if_detach(sc->sc_ifp); 298187576Sjhb bus_teardown_intr(dev, sc->res_irq, sc->sc_intr_cookie); 299187576Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->res_irq); 300187576Sjhb return (0); 301187576Sjhb} 302187576Sjhb 30338061Smsmith/* 30438061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol. 30538061Smsmith * We don't want to calculate these nasties in our tight loop, so we 30638061Smsmith * precalculate them when we initialize. 30738061Smsmith */ 30838061Smsmithstatic int 309184896Sjhblpinittables(void) 31038061Smsmith{ 311184896Sjhb int i; 31238061Smsmith 313187576Sjhb mtx_lock(&lp_tables_lock); 314184896Sjhb if (txmith == NULL) 315184896Sjhb txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 31638061Smsmith 317187576Sjhb if (txmith == NULL) { 318187576Sjhb mtx_unlock(&lp_tables_lock); 319184896Sjhb return (1); 320187576Sjhb } 32138061Smsmith 322184896Sjhb if (ctxmith == NULL) 323185003Sjhb ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); 32438061Smsmith 325187576Sjhb if (ctxmith == NULL) { 326187576Sjhb mtx_unlock(&lp_tables_lock); 327184896Sjhb return (1); 328187576Sjhb } 32938061Smsmith 330184896Sjhb for (i = 0; i < LPIPTBLSIZE; i++) { 331184896Sjhb ctxmith[i] = (i & 0xF0) >> 4; 332184896Sjhb ctxmitl[i] = 0x10 | (i & 0x0F); 333184896Sjhb ctrecvh[i] = (i & 0x78) << 1; 334184896Sjhb ctrecvl[i] = (i & 0x78) >> 3; 335184896Sjhb } 33638061Smsmith 337184896Sjhb for (i = 0; i < LPIPTBLSIZE; i++) { 338184896Sjhb txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; 339184896Sjhb txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); 340184896Sjhb trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); 341184896Sjhb trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); 342184896Sjhb } 343187576Sjhb mtx_unlock(&lp_tables_lock); 34438061Smsmith 345184896Sjhb return (0); 34638061Smsmith} 34738061Smsmith 348187576Sjhbstatic void 349187576Sjhblpstop(struct lp_data *sc) 350187576Sjhb{ 351187576Sjhb device_t ppbus = device_get_parent(sc->sc_dev); 352187576Sjhb 353187576Sjhb ppb_assert_locked(ppbus); 354187576Sjhb ppb_wctr(ppbus, 0x00); 355187576Sjhb sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 356187576Sjhb free(sc->sc_ifbuf, M_DEVBUF); 357187576Sjhb sc->sc_ifbuf = NULL; 358187576Sjhb 359187576Sjhb /* IFF_UP is not set, try to release the bus anyway */ 360187576Sjhb ppb_release_bus(ppbus, sc->sc_dev); 361187576Sjhb} 362187576Sjhb 363187576Sjhbstatic int 364187576Sjhblpinit_locked(struct ifnet *ifp) 365187576Sjhb{ 366187576Sjhb struct lp_data *sc = ifp->if_softc; 367187576Sjhb device_t dev = sc->sc_dev; 368187576Sjhb device_t ppbus = device_get_parent(dev); 369187576Sjhb int error; 370187576Sjhb 371187576Sjhb ppb_assert_locked(ppbus); 372187576Sjhb error = ppb_request_bus(ppbus, dev, PPB_DONTWAIT); 373187576Sjhb if (error) 374187576Sjhb return (error); 375187576Sjhb 376187576Sjhb /* Now IFF_UP means that we own the bus */ 377187576Sjhb ppb_set_mode(ppbus, PPB_COMPATIBLE); 378187576Sjhb 379187576Sjhb if (lpinittables()) { 380187576Sjhb ppb_release_bus(ppbus, dev); 381187576Sjhb return (ENOBUFS); 382187576Sjhb } 383187576Sjhb 384187576Sjhb sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN, 385187576Sjhb M_DEVBUF, M_NOWAIT); 386187576Sjhb if (sc->sc_ifbuf == NULL) { 387187576Sjhb ppb_release_bus(ppbus, dev); 388187576Sjhb return (ENOBUFS); 389187576Sjhb } 390187576Sjhb 391187576Sjhb ppb_wctr(ppbus, IRQENABLE); 392187576Sjhb 393187576Sjhb ifp->if_drv_flags |= IFF_DRV_RUNNING; 394187576Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 395187576Sjhb return (0); 396187576Sjhb} 397187576Sjhb 39838061Smsmith/* 39938061Smsmith * Process an ioctl request. 40038061Smsmith */ 40138061Smsmithstatic int 402184896Sjhblpioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 40338061Smsmith{ 404184896Sjhb struct lp_data *sc = ifp->if_softc; 405184896Sjhb device_t dev = sc->sc_dev; 406184896Sjhb device_t ppbus = device_get_parent(dev); 407184896Sjhb struct ifaddr *ifa = (struct ifaddr *)data; 408184896Sjhb struct ifreq *ifr = (struct ifreq *)data; 409184896Sjhb u_char *ptr; 410184896Sjhb int error; 41138061Smsmith 412184896Sjhb switch (cmd) { 413184896Sjhb case SIOCSIFDSTADDR: 414184896Sjhb case SIOCAIFADDR: 415184896Sjhb case SIOCSIFADDR: 416184896Sjhb if (ifa->ifa_addr->sa_family != AF_INET) 417184896Sjhb return (EAFNOSUPPORT); 41838061Smsmith 419184896Sjhb ifp->if_flags |= IFF_UP; 420184896Sjhb /* FALLTHROUGH */ 421184896Sjhb case SIOCSIFFLAGS: 422187576Sjhb error = 0; 423187576Sjhb ppb_lock(ppbus); 424184896Sjhb if ((!(ifp->if_flags & IFF_UP)) && 425187576Sjhb (ifp->if_drv_flags & IFF_DRV_RUNNING)) 426187576Sjhb lpstop(sc); 427187576Sjhb else if (((ifp->if_flags & IFF_UP)) && 428187576Sjhb (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) 429187576Sjhb error = lpinit_locked(ifp); 430187576Sjhb ppb_unlock(ppbus); 431187576Sjhb return (error); 43238061Smsmith 433187576Sjhb case SIOCSIFMTU: 434187576Sjhb ppb_lock(ppbus); 435187576Sjhb if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 436187576Sjhb ptr = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF, 437187576Sjhb M_NOWAIT); 438187576Sjhb if (ptr == NULL) { 439187576Sjhb ppb_unlock(ppbus); 440184896Sjhb return (ENOBUFS); 441184896Sjhb } 442187576Sjhb if (sc->sc_ifbuf) 443187576Sjhb free(sc->sc_ifbuf, M_DEVBUF); 444184896Sjhb sc->sc_ifbuf = ptr; 445184896Sjhb } 446184896Sjhb sc->sc_ifp->if_mtu = ifr->ifr_mtu; 447187576Sjhb ppb_unlock(ppbus); 448184896Sjhb break; 44955939Snsouch 450184896Sjhb case SIOCGIFMTU: 451184896Sjhb ifr->ifr_mtu = sc->sc_ifp->if_mtu; 452184896Sjhb break; 45338061Smsmith 454184896Sjhb case SIOCADDMULTI: 455184896Sjhb case SIOCDELMULTI: 456184896Sjhb if (ifr == 0) { 457184896Sjhb return (EAFNOSUPPORT); /* XXX */ 458184896Sjhb } 459184896Sjhb switch (ifr->ifr_addr.sa_family) { 460184896Sjhb case AF_INET: 461184896Sjhb break; 462184896Sjhb default: 463184896Sjhb return (EAFNOSUPPORT); 464184896Sjhb } 465184896Sjhb break; 46638061Smsmith 467184896Sjhb case SIOCGIFMEDIA: 468184896Sjhb /* 469184896Sjhb * No ifmedia support at this stage; maybe use it 470184896Sjhb * in future for eg. protocol selection. 471184896Sjhb */ 472184896Sjhb return (EINVAL); 47338061Smsmith 47438061Smsmith default: 475184896Sjhb lprintf("LP:ioctl(0x%lx)\n", cmd); 476184896Sjhb return (EINVAL); 47738061Smsmith } 478184896Sjhb return (0); 47938061Smsmith} 48038061Smsmith 48138061Smsmithstatic __inline int 482184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus) 48338061Smsmith{ 484184896Sjhb 48555939Snsouch ppb_wdtr(ppbus, ctxmitl[byte]); 48655939Snsouch while (ppb_rstr(ppbus) & CLPIP_SHAKE) 48738061Smsmith if (--spin == 0) { 488184896Sjhb return (1); 48938061Smsmith } 49055939Snsouch ppb_wdtr(ppbus, ctxmith[byte]); 49155939Snsouch while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 49238061Smsmith if (--spin == 0) { 493184896Sjhb return (1); 49438061Smsmith } 495184896Sjhb return (0); 49638061Smsmith} 49738061Smsmith 49838061Smsmithstatic __inline int 499184896Sjhbclpinbyte(int spin, device_t ppbus) 50038061Smsmith{ 50142443Snsouch u_char c, cl; 50238061Smsmith 503187576Sjhb while ((ppb_rstr(ppbus) & CLPIP_SHAKE)) 504184896Sjhb if (!--spin) { 505184896Sjhb return (-1); 506184896Sjhb } 50755939Snsouch cl = ppb_rstr(ppbus); 50855939Snsouch ppb_wdtr(ppbus, 0x10); 50938061Smsmith 510187576Sjhb while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 511184896Sjhb if (!--spin) { 512184896Sjhb return (-1); 513184896Sjhb } 51455939Snsouch c = ppb_rstr(ppbus); 51555939Snsouch ppb_wdtr(ppbus, 0x00); 51638061Smsmith 51738061Smsmith return (ctrecvl[cl] | ctrecvh[c]); 51838061Smsmith} 51938061Smsmith 52038061Smsmithstatic void 52143773Sdeslptap(struct ifnet *ifp, struct mbuf *m) 52243773Sdes{ 52343773Sdes u_int32_t af = AF_INET; 524184896Sjhb 525165640Sjhb bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); 52643773Sdes} 52743773Sdes 52843773Sdesstatic void 529184896Sjhblp_intr(void *arg) 53038061Smsmith{ 531187576Sjhb struct lp_data *sc = arg; 532187576Sjhb device_t ppbus = device_get_parent(sc->sc_dev); 533187576Sjhb int len, j; 53438061Smsmith u_char *bp; 53538061Smsmith u_char c, cl; 53638061Smsmith struct mbuf *top; 53738061Smsmith 538187576Sjhb ppb_assert_locked(ppbus); 539147256Sbrooks if (sc->sc_ifp->if_flags & IFF_LINK0) { 54038061Smsmith 541184896Sjhb /* Ack. the request */ 542184896Sjhb ppb_wdtr(ppbus, 0x01); 54338061Smsmith 544184896Sjhb /* Get the packet length */ 545184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 546184896Sjhb if (j == -1) 547184896Sjhb goto err; 548184896Sjhb len = j; 549184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 550184896Sjhb if (j == -1) 551184896Sjhb goto err; 552184896Sjhb len = len + (j << 8); 553184896Sjhb if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN) 554184896Sjhb goto err; 55538061Smsmith 556184896Sjhb bp = sc->sc_ifbuf; 55738061Smsmith 558184896Sjhb while (len--) { 559184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 560184896Sjhb if (j == -1) { 561184896Sjhb goto err; 562184896Sjhb } 563184896Sjhb *bp++ = j; 564184896Sjhb } 56538061Smsmith 566184896Sjhb /* Get and ignore checksum */ 567184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 568184896Sjhb if (j == -1) { 569184896Sjhb goto err; 570184896Sjhb } 57138061Smsmith 572184896Sjhb len = bp - sc->sc_ifbuf; 573184896Sjhb if (len <= CLPIPHDRLEN) 574184896Sjhb goto err; 575184896Sjhb 576184896Sjhb sc->sc_iferrs = 0; 577184896Sjhb 578184896Sjhb len -= CLPIPHDRLEN; 579184896Sjhb sc->sc_ifp->if_ipackets++; 580184896Sjhb sc->sc_ifp->if_ibytes += len; 581184896Sjhb top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 582184896Sjhb 0); 583184896Sjhb if (top) { 584187576Sjhb ppb_unlock(ppbus); 585184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 586184896Sjhb lptap(sc->sc_ifp, top); 587184896Sjhb 588184896Sjhb /* mbuf is free'd on failure. */ 589184896Sjhb netisr_queue(NETISR_IP, top); 590187576Sjhb ppb_lock(ppbus); 591184896Sjhb } 592187576Sjhb return; 59338061Smsmith } 59455939Snsouch while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { 595184896Sjhb len = sc->sc_ifp->if_mtu + LPIPHDRLEN; 596184896Sjhb bp = sc->sc_ifbuf; 597184896Sjhb while (len--) { 59838061Smsmith 599184896Sjhb cl = ppb_rstr(ppbus); 600184896Sjhb ppb_wdtr(ppbus, 8); 60138061Smsmith 602184896Sjhb j = LPMAXSPIN2; 603187576Sjhb while ((ppb_rstr(ppbus) & LPIP_SHAKE)) 604184896Sjhb if (!--j) 605184896Sjhb goto err; 60638061Smsmith 607184896Sjhb c = ppb_rstr(ppbus); 608184896Sjhb ppb_wdtr(ppbus, 0); 60938061Smsmith 610184896Sjhb *bp++= trecvh[cl] | trecvl[c]; 61138061Smsmith 612184896Sjhb j = LPMAXSPIN2; 613184896Sjhb while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) { 614184896Sjhb if (cl != c && 615184896Sjhb (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == 616184896Sjhb (c & 0xf8)) 617184896Sjhb goto end; 618184896Sjhb if (!--j) 619184896Sjhb goto err; 620184896Sjhb } 62138061Smsmith } 62238061Smsmith 62338061Smsmith end: 624184896Sjhb len = bp - sc->sc_ifbuf; 625184896Sjhb if (len <= LPIPHDRLEN) 626184896Sjhb goto err; 62738061Smsmith 628184896Sjhb sc->sc_iferrs = 0; 62938061Smsmith 630184896Sjhb len -= LPIPHDRLEN; 631184896Sjhb sc->sc_ifp->if_ipackets++; 632184896Sjhb sc->sc_ifp->if_ibytes += len; 633184896Sjhb top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 634184896Sjhb 0); 635184896Sjhb if (top) { 636187576Sjhb ppb_unlock(ppbus); 637184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 638184896Sjhb lptap(sc->sc_ifp, top); 639184896Sjhb 640184896Sjhb /* mbuf is free'd on failure. */ 641184896Sjhb netisr_queue(NETISR_IP, top); 642187576Sjhb ppb_lock(ppbus); 643184896Sjhb } 64438061Smsmith } 645187576Sjhb return; 64638061Smsmith 647185003Sjhberr: 64855939Snsouch ppb_wdtr(ppbus, 0); 64938061Smsmith lprintf("R"); 650147256Sbrooks sc->sc_ifp->if_ierrors++; 65138061Smsmith sc->sc_iferrs++; 65238061Smsmith 65338061Smsmith /* 65438061Smsmith * We are not able to send receive anything for now, 65538061Smsmith * so stop wasting our time 65638061Smsmith */ 65738061Smsmith if (sc->sc_iferrs > LPMAXERRS) { 658184896Sjhb if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n"); 659184896Sjhb ppb_wctr(ppbus, 0x00); 660184896Sjhb sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 661184896Sjhb sc->sc_iferrs = 0; 66238061Smsmith } 66338061Smsmith} 66438061Smsmith 66538061Smsmithstatic __inline int 666185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus) 66738061Smsmith{ 668184896Sjhb 669184896Sjhb ppb_wdtr(ppbus, txmith[byte]); 670184896Sjhb while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) 671184896Sjhb if (--spin == 0) 672184896Sjhb return (1); 673184896Sjhb ppb_wdtr(ppbus, txmitl[byte]); 674184896Sjhb while (ppb_rstr(ppbus) & LPIP_SHAKE) 675184896Sjhb if (--spin == 0) 676184896Sjhb return (1); 677184896Sjhb return (0); 67838061Smsmith} 67938061Smsmith 68038061Smsmithstatic int 681184896Sjhblpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 682191148Skmacy struct route *ro) 68338061Smsmith{ 684184896Sjhb struct lp_data *sc = ifp->if_softc; 685184896Sjhb device_t dev = sc->sc_dev; 686184896Sjhb device_t ppbus = device_get_parent(dev); 687187576Sjhb int err; 688184896Sjhb struct mbuf *mm; 689184896Sjhb u_char *cp = "\0\0"; 690184896Sjhb u_char chksum = 0; 691184896Sjhb int count = 0; 692184896Sjhb int i, len, spin; 69338061Smsmith 694184896Sjhb /* We need a sensible value if we abort */ 695184896Sjhb cp++; 696187576Sjhb ppb_lock(ppbus); 697187576Sjhb ifp->if_drv_flags |= IFF_DRV_OACTIVE; 69838061Smsmith 699184896Sjhb err = 1; /* assume we're aborting because of an error */ 70038061Smsmith 701184896Sjhb /* Suspend (on laptops) or receive-errors might have taken us offline */ 702184896Sjhb ppb_wctr(ppbus, IRQENABLE); 70338061Smsmith 704184896Sjhb if (ifp->if_flags & IFF_LINK0) { 705184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 706184896Sjhb lprintf("&"); 707187576Sjhb lp_intr(sc); 708184896Sjhb } 70938061Smsmith 710184896Sjhb /* Alert other end to pending packet */ 711184896Sjhb spin = LPMAXSPIN1; 712184896Sjhb ppb_wdtr(ppbus, 0x08); 713184896Sjhb while ((ppb_rstr(ppbus) & 0x08) == 0) 714184896Sjhb if (--spin == 0) { 715184896Sjhb goto nend; 716184896Sjhb } 71738061Smsmith 718184896Sjhb /* Calculate length of packet, then send that */ 719184896Sjhb 720184896Sjhb count += 14; /* Ethernet header len */ 721184896Sjhb 722184896Sjhb mm = m; 723184896Sjhb for (mm = m; mm; mm = mm->m_next) { 724184896Sjhb count += mm->m_len; 725184896Sjhb } 726184896Sjhb if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) 72738061Smsmith goto nend; 728184896Sjhb if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) 729184896Sjhb goto nend; 730184896Sjhb 731184896Sjhb /* Send dummy ethernet header */ 732184896Sjhb for (i = 0; i < 12; i++) { 733184896Sjhb if (clpoutbyte(i, LPMAXSPIN1, ppbus)) 734184896Sjhb goto nend; 735184896Sjhb chksum += i; 73638061Smsmith } 73738061Smsmith 738184896Sjhb if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) 739184896Sjhb goto nend; 740184896Sjhb if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) 741184896Sjhb goto nend; 742184896Sjhb chksum += 0x08 + 0x00; /* Add into checksum */ 74338061Smsmith 744184896Sjhb mm = m; 745184896Sjhb do { 746184896Sjhb cp = mtod(mm, u_char *); 747184896Sjhb len = mm->m_len; 748184896Sjhb while (len--) { 749184896Sjhb chksum += *cp; 750184896Sjhb if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 751184896Sjhb goto nend; 752184896Sjhb } 753184896Sjhb } while ((mm = mm->m_next)); 75438061Smsmith 755184896Sjhb /* Send checksum */ 756184896Sjhb if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) 757184896Sjhb goto nend; 758184896Sjhb 759184896Sjhb /* Go quiescent */ 760184896Sjhb ppb_wdtr(ppbus, 0); 761184896Sjhb 762184896Sjhb err = 0; /* No errors */ 763184896Sjhb 764184896Sjhb nend: 765187576Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 766184896Sjhb if (err) { /* if we didn't timeout... */ 767184896Sjhb ifp->if_oerrors++; 768184896Sjhb lprintf("X"); 769184896Sjhb } else { 770184896Sjhb ifp->if_opackets++; 771184896Sjhb ifp->if_obytes += m->m_pkthdr.len; 772184896Sjhb if (bpf_peers_present(ifp->if_bpf)) 773184896Sjhb lptap(ifp, m); 774184896Sjhb } 775184896Sjhb 776184896Sjhb m_freem(m); 777184896Sjhb 778184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 779184896Sjhb lprintf("^"); 780187576Sjhb lp_intr(sc); 781184896Sjhb } 782187576Sjhb ppb_unlock(ppbus); 783184896Sjhb return (0); 78438061Smsmith } 78538061Smsmith 786184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 787184896Sjhb lprintf("&"); 788187576Sjhb lp_intr(sc); 78938061Smsmith } 79038061Smsmith 791184896Sjhb if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) 792184896Sjhb goto end; 793184896Sjhb if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) 794184896Sjhb goto end; 79538061Smsmith 79638061Smsmith mm = m; 79738061Smsmith do { 79838061Smsmith cp = mtod(mm, u_char *); 79943773Sdes len = mm->m_len; 800184896Sjhb while (len--) 801184896Sjhb if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 802184896Sjhb goto end; 80338061Smsmith } while ((mm = mm->m_next)); 80438061Smsmith 805184896Sjhb err = 0; /* no errors were encountered */ 80638061Smsmith 807184896Sjhbend: 808184896Sjhb --cp; 809184896Sjhb ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); 81038061Smsmith 811187576Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 812184896Sjhb if (err) { /* if we didn't timeout... */ 81338061Smsmith ifp->if_oerrors++; 81438061Smsmith lprintf("X"); 81538061Smsmith } else { 81638061Smsmith ifp->if_opackets++; 81738061Smsmith ifp->if_obytes += m->m_pkthdr.len; 818165632Sjhb if (bpf_peers_present(ifp->if_bpf)) 819184896Sjhb lptap(ifp, m); 82038061Smsmith } 82138061Smsmith 82238061Smsmith m_freem(m); 82338061Smsmith 824184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 82538061Smsmith lprintf("^"); 826187576Sjhb lp_intr(sc); 82738061Smsmith } 828184896Sjhb 829187576Sjhb ppb_unlock(ppbus); 830184896Sjhb return (0); 83138061Smsmith} 83255939Snsouch 83356455Speterstatic device_method_t lp_methods[] = { 83456455Speter /* device interface */ 83556455Speter DEVMETHOD(device_identify, lp_identify), 83656455Speter DEVMETHOD(device_probe, lp_probe), 83756455Speter DEVMETHOD(device_attach, lp_attach), 838187576Sjhb DEVMETHOD(device_detach, lp_detach), 83956455Speter 84056455Speter { 0, 0 } 84156455Speter}; 84256455Speter 84356455Speterstatic driver_t lp_driver = { 844184896Sjhb "plip", 845184896Sjhb lp_methods, 846184896Sjhb sizeof(struct lp_data), 84756455Speter}; 84856455Speter 849187576SjhbDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0); 850153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1); 851