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$"); 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); 176249925Sglebiusstatic int lpoutput(struct ifnet *, struct mbuf *, const 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 SIOCAIFADDR: 414184896Sjhb case SIOCSIFADDR: 415184896Sjhb if (ifa->ifa_addr->sa_family != AF_INET) 416184896Sjhb return (EAFNOSUPPORT); 41738061Smsmith 418184896Sjhb ifp->if_flags |= IFF_UP; 419184896Sjhb /* FALLTHROUGH */ 420184896Sjhb case SIOCSIFFLAGS: 421187576Sjhb error = 0; 422187576Sjhb ppb_lock(ppbus); 423184896Sjhb if ((!(ifp->if_flags & IFF_UP)) && 424187576Sjhb (ifp->if_drv_flags & IFF_DRV_RUNNING)) 425187576Sjhb lpstop(sc); 426187576Sjhb else if (((ifp->if_flags & IFF_UP)) && 427187576Sjhb (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) 428187576Sjhb error = lpinit_locked(ifp); 429187576Sjhb ppb_unlock(ppbus); 430187576Sjhb return (error); 43138061Smsmith 432187576Sjhb case SIOCSIFMTU: 433187576Sjhb ppb_lock(ppbus); 434187576Sjhb if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 435187576Sjhb ptr = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF, 436187576Sjhb M_NOWAIT); 437187576Sjhb if (ptr == NULL) { 438187576Sjhb ppb_unlock(ppbus); 439184896Sjhb return (ENOBUFS); 440184896Sjhb } 441187576Sjhb if (sc->sc_ifbuf) 442187576Sjhb free(sc->sc_ifbuf, M_DEVBUF); 443184896Sjhb sc->sc_ifbuf = ptr; 444184896Sjhb } 445184896Sjhb sc->sc_ifp->if_mtu = ifr->ifr_mtu; 446187576Sjhb ppb_unlock(ppbus); 447184896Sjhb break; 44855939Snsouch 449184896Sjhb case SIOCGIFMTU: 450184896Sjhb ifr->ifr_mtu = sc->sc_ifp->if_mtu; 451184896Sjhb break; 45238061Smsmith 453184896Sjhb case SIOCADDMULTI: 454184896Sjhb case SIOCDELMULTI: 455184896Sjhb if (ifr == 0) { 456184896Sjhb return (EAFNOSUPPORT); /* XXX */ 457184896Sjhb } 458184896Sjhb switch (ifr->ifr_addr.sa_family) { 459184896Sjhb case AF_INET: 460184896Sjhb break; 461184896Sjhb default: 462184896Sjhb return (EAFNOSUPPORT); 463184896Sjhb } 464184896Sjhb break; 46538061Smsmith 466184896Sjhb case SIOCGIFMEDIA: 467184896Sjhb /* 468184896Sjhb * No ifmedia support at this stage; maybe use it 469184896Sjhb * in future for eg. protocol selection. 470184896Sjhb */ 471184896Sjhb return (EINVAL); 47238061Smsmith 47338061Smsmith default: 474184896Sjhb lprintf("LP:ioctl(0x%lx)\n", cmd); 475184896Sjhb return (EINVAL); 47638061Smsmith } 477184896Sjhb return (0); 47838061Smsmith} 47938061Smsmith 48038061Smsmithstatic __inline int 481184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus) 48238061Smsmith{ 483184896Sjhb 48455939Snsouch ppb_wdtr(ppbus, ctxmitl[byte]); 48555939Snsouch while (ppb_rstr(ppbus) & CLPIP_SHAKE) 48638061Smsmith if (--spin == 0) { 487184896Sjhb return (1); 48838061Smsmith } 48955939Snsouch ppb_wdtr(ppbus, ctxmith[byte]); 49055939Snsouch while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 49138061Smsmith if (--spin == 0) { 492184896Sjhb return (1); 49338061Smsmith } 494184896Sjhb return (0); 49538061Smsmith} 49638061Smsmith 49738061Smsmithstatic __inline int 498184896Sjhbclpinbyte(int spin, device_t ppbus) 49938061Smsmith{ 50042443Snsouch u_char c, cl; 50138061Smsmith 502187576Sjhb while ((ppb_rstr(ppbus) & CLPIP_SHAKE)) 503184896Sjhb if (!--spin) { 504184896Sjhb return (-1); 505184896Sjhb } 50655939Snsouch cl = ppb_rstr(ppbus); 50755939Snsouch ppb_wdtr(ppbus, 0x10); 50838061Smsmith 509187576Sjhb while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) 510184896Sjhb if (!--spin) { 511184896Sjhb return (-1); 512184896Sjhb } 51355939Snsouch c = ppb_rstr(ppbus); 51455939Snsouch ppb_wdtr(ppbus, 0x00); 51538061Smsmith 51638061Smsmith return (ctrecvl[cl] | ctrecvh[c]); 51738061Smsmith} 51838061Smsmith 51938061Smsmithstatic void 52043773Sdeslptap(struct ifnet *ifp, struct mbuf *m) 52143773Sdes{ 52243773Sdes u_int32_t af = AF_INET; 523184896Sjhb 524165640Sjhb bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); 52543773Sdes} 52643773Sdes 52743773Sdesstatic void 528184896Sjhblp_intr(void *arg) 52938061Smsmith{ 530187576Sjhb struct lp_data *sc = arg; 531187576Sjhb device_t ppbus = device_get_parent(sc->sc_dev); 532187576Sjhb int len, j; 53338061Smsmith u_char *bp; 53438061Smsmith u_char c, cl; 53538061Smsmith struct mbuf *top; 53638061Smsmith 537187576Sjhb ppb_assert_locked(ppbus); 538147256Sbrooks if (sc->sc_ifp->if_flags & IFF_LINK0) { 53938061Smsmith 540184896Sjhb /* Ack. the request */ 541184896Sjhb ppb_wdtr(ppbus, 0x01); 54238061Smsmith 543184896Sjhb /* Get the packet length */ 544184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 545184896Sjhb if (j == -1) 546184896Sjhb goto err; 547184896Sjhb len = j; 548184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 549184896Sjhb if (j == -1) 550184896Sjhb goto err; 551184896Sjhb len = len + (j << 8); 552184896Sjhb if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN) 553184896Sjhb goto err; 55438061Smsmith 555184896Sjhb bp = sc->sc_ifbuf; 55638061Smsmith 557184896Sjhb while (len--) { 558184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 559184896Sjhb if (j == -1) { 560184896Sjhb goto err; 561184896Sjhb } 562184896Sjhb *bp++ = j; 563184896Sjhb } 56438061Smsmith 565184896Sjhb /* Get and ignore checksum */ 566184896Sjhb j = clpinbyte(LPMAXSPIN2, ppbus); 567184896Sjhb if (j == -1) { 568184896Sjhb goto err; 569184896Sjhb } 57038061Smsmith 571184896Sjhb len = bp - sc->sc_ifbuf; 572184896Sjhb if (len <= CLPIPHDRLEN) 573184896Sjhb goto err; 574184896Sjhb 575184896Sjhb sc->sc_iferrs = 0; 576184896Sjhb 577184896Sjhb len -= CLPIPHDRLEN; 578184896Sjhb sc->sc_ifp->if_ipackets++; 579184896Sjhb sc->sc_ifp->if_ibytes += len; 580184896Sjhb top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 581184896Sjhb 0); 582184896Sjhb if (top) { 583187576Sjhb ppb_unlock(ppbus); 584184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 585184896Sjhb lptap(sc->sc_ifp, top); 586184896Sjhb 587223741Sbz M_SETFIB(top, sc->sc_ifp->if_fib); 588223741Sbz 589184896Sjhb /* mbuf is free'd on failure. */ 590184896Sjhb netisr_queue(NETISR_IP, top); 591187576Sjhb ppb_lock(ppbus); 592184896Sjhb } 593187576Sjhb return; 59438061Smsmith } 59555939Snsouch while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { 596184896Sjhb len = sc->sc_ifp->if_mtu + LPIPHDRLEN; 597184896Sjhb bp = sc->sc_ifbuf; 598184896Sjhb while (len--) { 59938061Smsmith 600184896Sjhb cl = ppb_rstr(ppbus); 601184896Sjhb ppb_wdtr(ppbus, 8); 60238061Smsmith 603184896Sjhb j = LPMAXSPIN2; 604187576Sjhb while ((ppb_rstr(ppbus) & LPIP_SHAKE)) 605184896Sjhb if (!--j) 606184896Sjhb goto err; 60738061Smsmith 608184896Sjhb c = ppb_rstr(ppbus); 609184896Sjhb ppb_wdtr(ppbus, 0); 61038061Smsmith 611184896Sjhb *bp++= trecvh[cl] | trecvl[c]; 61238061Smsmith 613184896Sjhb j = LPMAXSPIN2; 614184896Sjhb while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) { 615184896Sjhb if (cl != c && 616184896Sjhb (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == 617184896Sjhb (c & 0xf8)) 618184896Sjhb goto end; 619184896Sjhb if (!--j) 620184896Sjhb goto err; 621184896Sjhb } 62238061Smsmith } 62338061Smsmith 62438061Smsmith end: 625184896Sjhb len = bp - sc->sc_ifbuf; 626184896Sjhb if (len <= LPIPHDRLEN) 627184896Sjhb goto err; 62838061Smsmith 629184896Sjhb sc->sc_iferrs = 0; 63038061Smsmith 631184896Sjhb len -= LPIPHDRLEN; 632184896Sjhb sc->sc_ifp->if_ipackets++; 633184896Sjhb sc->sc_ifp->if_ibytes += len; 634184896Sjhb top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 635184896Sjhb 0); 636184896Sjhb if (top) { 637187576Sjhb ppb_unlock(ppbus); 638184896Sjhb if (bpf_peers_present(sc->sc_ifp->if_bpf)) 639184896Sjhb lptap(sc->sc_ifp, top); 640184896Sjhb 641223741Sbz M_SETFIB(top, sc->sc_ifp->if_fib); 642223741Sbz 643184896Sjhb /* mbuf is free'd on failure. */ 644184896Sjhb netisr_queue(NETISR_IP, top); 645187576Sjhb ppb_lock(ppbus); 646184896Sjhb } 64738061Smsmith } 648187576Sjhb return; 64938061Smsmith 650185003Sjhberr: 65155939Snsouch ppb_wdtr(ppbus, 0); 65238061Smsmith lprintf("R"); 653147256Sbrooks sc->sc_ifp->if_ierrors++; 65438061Smsmith sc->sc_iferrs++; 65538061Smsmith 65638061Smsmith /* 65738061Smsmith * We are not able to send receive anything for now, 65838061Smsmith * so stop wasting our time 65938061Smsmith */ 66038061Smsmith if (sc->sc_iferrs > LPMAXERRS) { 661184896Sjhb if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n"); 662184896Sjhb ppb_wctr(ppbus, 0x00); 663184896Sjhb sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 664184896Sjhb sc->sc_iferrs = 0; 66538061Smsmith } 66638061Smsmith} 66738061Smsmith 66838061Smsmithstatic __inline int 669185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus) 67038061Smsmith{ 671184896Sjhb 672184896Sjhb ppb_wdtr(ppbus, txmith[byte]); 673184896Sjhb while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) 674184896Sjhb if (--spin == 0) 675184896Sjhb return (1); 676184896Sjhb ppb_wdtr(ppbus, txmitl[byte]); 677184896Sjhb while (ppb_rstr(ppbus) & LPIP_SHAKE) 678184896Sjhb if (--spin == 0) 679184896Sjhb return (1); 680184896Sjhb return (0); 68138061Smsmith} 68238061Smsmith 68338061Smsmithstatic int 684249925Sglebiuslpoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 685191148Skmacy struct route *ro) 68638061Smsmith{ 687184896Sjhb struct lp_data *sc = ifp->if_softc; 688184896Sjhb device_t dev = sc->sc_dev; 689184896Sjhb device_t ppbus = device_get_parent(dev); 690187576Sjhb int err; 691184896Sjhb struct mbuf *mm; 692184896Sjhb u_char *cp = "\0\0"; 693184896Sjhb u_char chksum = 0; 694184896Sjhb int count = 0; 695184896Sjhb int i, len, spin; 69638061Smsmith 697184896Sjhb /* We need a sensible value if we abort */ 698184896Sjhb cp++; 699187576Sjhb ppb_lock(ppbus); 700187576Sjhb ifp->if_drv_flags |= IFF_DRV_OACTIVE; 70138061Smsmith 702184896Sjhb err = 1; /* assume we're aborting because of an error */ 70338061Smsmith 704184896Sjhb /* Suspend (on laptops) or receive-errors might have taken us offline */ 705184896Sjhb ppb_wctr(ppbus, IRQENABLE); 70638061Smsmith 707184896Sjhb if (ifp->if_flags & IFF_LINK0) { 708184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 709184896Sjhb lprintf("&"); 710187576Sjhb lp_intr(sc); 711184896Sjhb } 71238061Smsmith 713184896Sjhb /* Alert other end to pending packet */ 714184896Sjhb spin = LPMAXSPIN1; 715184896Sjhb ppb_wdtr(ppbus, 0x08); 716184896Sjhb while ((ppb_rstr(ppbus) & 0x08) == 0) 717184896Sjhb if (--spin == 0) { 718184896Sjhb goto nend; 719184896Sjhb } 72038061Smsmith 721184896Sjhb /* Calculate length of packet, then send that */ 722184896Sjhb 723184896Sjhb count += 14; /* Ethernet header len */ 724184896Sjhb 725184896Sjhb mm = m; 726184896Sjhb for (mm = m; mm; mm = mm->m_next) { 727184896Sjhb count += mm->m_len; 728184896Sjhb } 729184896Sjhb if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) 73038061Smsmith goto nend; 731184896Sjhb if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) 732184896Sjhb goto nend; 733184896Sjhb 734184896Sjhb /* Send dummy ethernet header */ 735184896Sjhb for (i = 0; i < 12; i++) { 736184896Sjhb if (clpoutbyte(i, LPMAXSPIN1, ppbus)) 737184896Sjhb goto nend; 738184896Sjhb chksum += i; 73938061Smsmith } 74038061Smsmith 741184896Sjhb if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) 742184896Sjhb goto nend; 743184896Sjhb if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) 744184896Sjhb goto nend; 745184896Sjhb chksum += 0x08 + 0x00; /* Add into checksum */ 74638061Smsmith 747184896Sjhb mm = m; 748184896Sjhb do { 749184896Sjhb cp = mtod(mm, u_char *); 750184896Sjhb len = mm->m_len; 751184896Sjhb while (len--) { 752184896Sjhb chksum += *cp; 753184896Sjhb if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 754184896Sjhb goto nend; 755184896Sjhb } 756184896Sjhb } while ((mm = mm->m_next)); 75738061Smsmith 758184896Sjhb /* Send checksum */ 759184896Sjhb if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) 760184896Sjhb goto nend; 761184896Sjhb 762184896Sjhb /* Go quiescent */ 763184896Sjhb ppb_wdtr(ppbus, 0); 764184896Sjhb 765184896Sjhb err = 0; /* No errors */ 766184896Sjhb 767184896Sjhb nend: 768187576Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 769184896Sjhb if (err) { /* if we didn't timeout... */ 770184896Sjhb ifp->if_oerrors++; 771184896Sjhb lprintf("X"); 772184896Sjhb } else { 773184896Sjhb ifp->if_opackets++; 774184896Sjhb ifp->if_obytes += m->m_pkthdr.len; 775184896Sjhb if (bpf_peers_present(ifp->if_bpf)) 776184896Sjhb lptap(ifp, m); 777184896Sjhb } 778184896Sjhb 779184896Sjhb m_freem(m); 780184896Sjhb 781184896Sjhb if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { 782184896Sjhb lprintf("^"); 783187576Sjhb lp_intr(sc); 784184896Sjhb } 785187576Sjhb ppb_unlock(ppbus); 786184896Sjhb return (0); 78738061Smsmith } 78838061Smsmith 789184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 790184896Sjhb lprintf("&"); 791187576Sjhb lp_intr(sc); 79238061Smsmith } 79338061Smsmith 794184896Sjhb if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) 795184896Sjhb goto end; 796184896Sjhb if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) 797184896Sjhb goto end; 79838061Smsmith 79938061Smsmith mm = m; 80038061Smsmith do { 80138061Smsmith cp = mtod(mm, u_char *); 80243773Sdes len = mm->m_len; 803184896Sjhb while (len--) 804184896Sjhb if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) 805184896Sjhb goto end; 80638061Smsmith } while ((mm = mm->m_next)); 80738061Smsmith 808184896Sjhb err = 0; /* no errors were encountered */ 80938061Smsmith 810184896Sjhbend: 811184896Sjhb --cp; 812184896Sjhb ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); 81338061Smsmith 814187576Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 815184896Sjhb if (err) { /* if we didn't timeout... */ 81638061Smsmith ifp->if_oerrors++; 81738061Smsmith lprintf("X"); 81838061Smsmith } else { 81938061Smsmith ifp->if_opackets++; 82038061Smsmith ifp->if_obytes += m->m_pkthdr.len; 821165632Sjhb if (bpf_peers_present(ifp->if_bpf)) 822184896Sjhb lptap(ifp, m); 82338061Smsmith } 82438061Smsmith 82538061Smsmith m_freem(m); 82638061Smsmith 827184896Sjhb if (ppb_rstr(ppbus) & LPIP_SHAKE) { 82838061Smsmith lprintf("^"); 829187576Sjhb lp_intr(sc); 83038061Smsmith } 831184896Sjhb 832187576Sjhb ppb_unlock(ppbus); 833184896Sjhb return (0); 83438061Smsmith} 83555939Snsouch 83656455Speterstatic device_method_t lp_methods[] = { 83756455Speter /* device interface */ 83856455Speter DEVMETHOD(device_identify, lp_identify), 83956455Speter DEVMETHOD(device_probe, lp_probe), 84056455Speter DEVMETHOD(device_attach, lp_attach), 841187576Sjhb DEVMETHOD(device_detach, lp_detach), 84256455Speter 84356455Speter { 0, 0 } 84456455Speter}; 84556455Speter 84656455Speterstatic driver_t lp_driver = { 847184896Sjhb "plip", 848184896Sjhb lp_methods, 849184896Sjhb sizeof(struct lp_data), 85056455Speter}; 85156455Speter 852187576SjhbDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0); 853153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1); 854