diff options
author | David Leonard <d@cvs.openbsd.org> | 1999-06-21 23:21:48 +0000 |
---|---|---|
committer | David Leonard <d@cvs.openbsd.org> | 1999-06-21 23:21:48 +0000 |
commit | 88db842a291c43de6f4c89d019c1379faa1b9913 (patch) | |
tree | d78266526ddb764ace7aa9c946371ee1a220bb4f | |
parent | a42c0e1f01ffd4afe90afadcbfd92ea5b6d079bf (diff) |
RangeLAN2 wireless network card
-rw-r--r-- | sys/conf/files | 7 | ||||
-rw-r--r-- | sys/dev/ic/rl2.c | 1115 | ||||
-rw-r--r-- | sys/dev/ic/rl2.h | 31 | ||||
-rw-r--r-- | sys/dev/ic/rl2cmd.h | 269 | ||||
-rw-r--r-- | sys/dev/ic/rl2reg.h | 266 | ||||
-rw-r--r-- | sys/dev/ic/rl2subr.c | 912 | ||||
-rw-r--r-- | sys/dev/ic/rl2var.h | 135 | ||||
-rw-r--r-- | sys/dev/pcmcia/files.pcmcia | 7 | ||||
-rw-r--r-- | sys/dev/pcmcia/if_rl2_pcmcia.c | 288 |
9 files changed, 3028 insertions, 2 deletions
diff --git a/sys/conf/files b/sys/conf/files index 7b029c2ce05..d957df4bf17 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.117 1999/06/18 07:23:57 deraadt Exp $ +# $OpenBSD: files,v 1.118 1999/06/21 23:21:44 d Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -86,6 +86,11 @@ file dev/ic/elink3.c ep # WaveLan # device wlp: ether, ifnet +# RangeLAN2 +device rln: ether, ifnet +file dev/ic/rl2.c rln +file dev/ic/rl2subr.c rln + # LANCE and PCnet Ethernet controllers device le: ether, ifnet, ifmedia file dev/ic/am7990.c le diff --git a/sys/dev/ic/rl2.c b/sys/dev/ic/rl2.c new file mode 100644 index 00000000000..3ec601655ff --- /dev/null +++ b/sys/dev/ic/rl2.c @@ -0,0 +1,1115 @@ +/* $OpenBSD: rl2.c,v 1.1 1999/06/21 23:21:46 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public Domain. + * + * Driver for the Proxim RangeLAN2 wireless network adaptor. + * + * Information and ideas gleaned from disassembly of Dave Koberstein's + * <davek@komacke.com> Linux driver (apparently based on Proxim source), + * from Yoichi Shinoda's <shinoda@cs.washington.edu> BSDI driver, and + * Geoff Voelker's <voelker@cs.washington.edu> Linux port of the same. + * + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/device.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/rl2.h> +#include <dev/ic/rl2var.h> +#include <dev/ic/rl2reg.h> +#include <dev/ic/rl2cmd.h> + +/* Autoconfig definition of driver back-end. */ +struct cfdriver rln_cd = { + NULL, "rln", DV_IFNET +}; + +static void rl2init __P((struct rl2_softc *)); +static void rl2start __P((struct ifnet*)); +static void rl2watchdog __P((struct ifnet*)); +static int rl2ioctl __P((struct ifnet *, u_long, caddr_t)); +static int rl2_probe __P((struct rl2_softc *)); +static void rl2stop __P((struct rl2_softc *)); + +/* Interrupt handler. */ +static void rl2softintr __P((void *)); + +/* Packet I/O. */ +static int rl2_transmit __P((struct rl2_softc *, struct mbuf *, + int, int)); +static struct mbuf * rl2get __P((struct rl2_softc *, struct rl2_mm_cmd *, + int)); + +/* Card protocol-level functions. */ +static int rl2_getenaddr __P((struct rl2_softc *, u_int8_t *)); +static int rl2_getpromvers __P((struct rl2_softc *, char *, int)); +static int rl2_setparam __P((struct rl2_softc *)); +#if 0 +static int rl2_roamconfig __P((struct rl2_softc *)); +static int rl2_roam __P((struct rl2_softc *)); +static int rl2_multicast __P((struct rl2_softc *, int)); +static int rl2_searchsync __P((struct rl2_softc *)); +#endif +#if notyet +static int rl2_iosetparam __P((struct rl2_softc *, struct rl2_param *)); +static int rl2_lockprom __P((struct rl2_softc *)); +static int rl2_ito __P((struct rl2_softc *)); +static int rl2_standby __P((struct rl2_softc *)); +#endif + +/* Back-end attach and configure. */ + +void +rl2config(sc) + struct rl2_softc * sc; +{ + struct ifnet * ifp = &sc->sc_arpcom.ac_if; + char promvers[7]; + int i; + + dprintf(" [attach %p]", sc); + + /* Use the flags supplied from config. */ + sc->sc_cardtype |= sc->sc_dev.dv_cfdata->cf_flags; + + /* Initialise values in the soft state. */ + sc->sc_pktseq = 0; + sc->sc_txseq = 0; + sc->sc_promisc = 0; + + /* Initialise user-configurable params. */ + sc->sc_param.rp_roamconfig = RL2_ROAM_NORMAL; + sc->sc_param.rp_security = RL2_SECURITY_DEFAULT; + sc->sc_param.rp_stationtype = RL2_STATIONTYPE_ALTMASTER; + sc->sc_param.rp_domain = 0; + sc->sc_param.rp_channel = 1; + sc->sc_param.rp_subchannel = 1; + + bzero(sc->sc_param.rp_master, sizeof sc->sc_param.rp_master); +#if notyet + /* XXX hostname not available at autoconf time! */ + /* Use this host's name as a master name. */ + bcopy(hostname, sc->sc_param.rp_master, + min(hostnamelen, sizeof sc->sc_param.rp_master)); +#endif + + /* Initialise the message mailboxes. */ + for (i = 0; i < RL2_NMBOX; i++) + sc->sc_mbox[i].mb_state = RL2MBOX_VOID; + + /* Keep the sys admin informed. */ + printf(", %s-piece", + (sc->sc_cardtype & RL2_CTYPE_ONE_PIECE) ? "one" : "two"); + if (sc->sc_cardtype & RL2_CTYPE_OEM) + printf(" oem"); + if (sc->sc_cardtype & RL2_CTYPE_UISA) + printf(" micro-isa"); + + /* Probe/reset the card. */ + if (rl2_probe(sc)) + return; + + /* Read the card's PROM revision. */ + if (rl2_getpromvers(sc, promvers, sizeof promvers)) { + printf(": could not read PROM version\n"); + return; + } + printf(", fw %.7s", promvers); + + /* Fetch the card's MAC address. */ + if (rl2_getenaddr(sc, sc->sc_arpcom.ac_enaddr)) { + printf(": could not read MAC address\n"); + return; + } + printf(", addr %s", ether_sprintf(sc->sc_arpcom.ac_enaddr)); + + /* Attach as a network interface. */ + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_start = rl2start; + ifp->if_ioctl = rl2ioctl; + ifp->if_watchdog = rl2watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + if_attach(ifp); + ether_ifattach(ifp); +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof (struct ether_header)); +#endif +} + +/* Bring device up. */ + +static void +rl2init(sc) + struct rl2_softc * sc; +{ + /* LLDInit() */ + struct ifnet * ifp = &sc->sc_arpcom.ac_if; + + dprintf(" [init]"); + + sc->sc_intsel = 0; + sc->sc_status = 0; + sc->sc_control = 0; + ifp->if_flags &= ~IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* Do a hard reset. */ + if (rl2_reset(sc)) { + printf("%s: could not reset card\n", sc->sc_dev.dv_xname); + goto fail; + } + sc->sc_state = 0; + + rl2_enable(sc, 1); + + /* Initialise operational params. */ + if (rl2_setparam(sc)) { + printf("%s: could not set card parameters\n", + sc->sc_dev.dv_xname); + goto fail; + } +#if 0 + rl2_roamconfig(sc); + /* rl2_lockprom(sc); */ /* XXX error? */ + + /* SendSetITO() */ + + rl2_multicast(sc, 1); + rl2_roam(sc); + + /* Synchronise with something. */ + rl2_searchsync(sc); +#endif + ifp->if_flags |= IFF_RUNNING; + rl2start(ifp); + + return; + + fail: + ifp->if_flags &= ~IFF_UP; + return; +} + +/* + * Start outputting on interface. + * This is always called at splnet(). + */ +static void +rl2start(ifp) + struct ifnet * ifp; +{ + struct rl2_softc * sc = (struct rl2_softc *)ifp->if_softc; + struct mbuf * m0; + int len, pad, ret; + + dprintf(" start["); + + /* Don't transmit if interface is busy or not running. */ + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) { + dprintf(" %s] ", (ifp->if_flags & IFF_OACTIVE) ? + "busy" : "stopped"); + return; + } + + /* Don't transmit if we are not synchronised. */ + if ((sc->sc_state & RL2_STATE_SYNC) == 0) { + dprintf(" nosync]"); + return; + } + + startagain: + IF_DEQUEUE(&ifp->if_snd, m0); + + if (m0 == NULL) { + dprintf(" empty]"); + return; + } + +#if NBPFILTER > 0 + /* Tap packet stream here for BPF listeners. */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + + /* We need to use m->m_pkthdr.len, so require the header. */ + if ((m0->m_flags & M_PKTHDR) == 0) { + printf("%s: no mbuf header\n", sc->sc_dev.dv_xname); + goto oerror; + } + + len = m0->m_pkthdr.len; + +#define PACKETMIN (sizeof (struct ether_header) + ETHERMIN) +#define PACKETMAX (sizeof (struct ether_header) + ETHERMTU + 4) + + /* Packet size has to be an even number between 60 and 1518 bytes. */ + pad = len & 1; + if (len + pad < PACKETMIN) + pad = PACKETMIN - len; + + if (len + pad > PACKETMAX) { + printf("%s: packet too big (%d > %d)\n", + sc->sc_dev.dv_xname, len + pad, + PACKETMAX); + ++ifp->if_oerrors; + m_freem(m0); + goto startagain; + } + + ret = rl2_transmit(sc, m0, len, pad); + if (ret) + goto oerror; + + ifp->if_flags |= IFF_OACTIVE; + m_freem(m0); + + dprintf(" sent]"); + return; + +oerror: + ++ifp->if_oerrors; + m_freem(m0); + /* XXX reset card, start again? */ + return; +} + +/* Transmit one packet. */ +static int +rl2_transmit(sc, m0, len, pad) + struct rl2_softc * sc; + struct mbuf * m0; + int len; + int pad; +{ + struct mbuf * m; + int zfirst = !(mtod(m0, u_int8_t*)[0]&1); + int actlen; + int tlen = len + pad; + struct rl2_msg_tx_state state; + static u_int8_t zeroes[60]; + struct rl2_mm_sendpacket cmd = { + RL2_MM_SENDPACKET, + RL2_MM_SENDPACKET_MODE_BIT7 | + (zfirst ? RL2_MM_SENDPACKET_MODE_ZFIRST : 0) | + (1 ? RL2_MM_SENDPACKET_MODE_QFSK : 0), /* sc->qfsk? */ + 0x70, /* txpower */ + htons(4 + tlen) & 0xff, + (htons(4 + tlen) >> 8) & 0xff, + 0, + 0, + sc->sc_txseq++, + 0 + }; + +#ifdef DIAGNOSTIC + if (sizeof cmd != 12) + panic("rl2_transmit"); +#endif + + dprintf(" T[%d+%d", len, pad); + + if (rl2_msg_tx_start(sc, &cmd, sizeof cmd + tlen, &state)) + goto error; + +#ifdef RL2DUMP + printf("%s: send %c%d seq %d data ", sc->sc_dev.dv_xname, + cmd.mm_cmd.cmd_letter, cmd.mm_cmd.cmd_fn, cmd.mm_cmd.cmd_seq); + RL2DUMPHEX(&cmd, sizeof cmd); + printf(":"); +#endif + rl2_msg_tx_data(sc, &cmd, sizeof cmd, &state); + + actlen = 0; + for (m = m0; m; m = m->m_next) { + if (m->m_len) { +#ifdef RL2DUMP + RL2DUMPHEX(mtod(m, void *), m->m_len); + printf("|"); +#endif + rl2_msg_tx_data(sc, mtod(m, void *), m->m_len, &state); + } + actlen += m->m_len; + } +#ifdef DIAGNOSTIC + if (actlen != len) + panic("rl2_transmit: len %d != %d", actlen, len); + if (pad > sizeof zeroes) + panic("rl2_transmit: pad %d > %d", pad, sizeof zeroes); +#endif + if (pad) { +#ifdef RL2DUMP + RL2DUMPHEX(zeroes, pad); +#endif + rl2_msg_tx_data(sc, zeroes, pad, &state); + } + +#ifdef RL2DUMP + printf("\n"); +#endif + if (rl2_msg_tx_end(sc, &state)) + goto error; + return (0); + + error: + dprintf(" error]"); + return (-1); +} + +/* (Supposedly) called when interrupts are suspiciously absent. */ +static void +rl2watchdog(ifp) + struct ifnet * ifp; +{ + struct rl2_softc * sc = (struct rl2_softc *)ifp->if_softc; + + log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname); + ++sc->sc_arpcom.ac_if.if_oerrors; + rl2_reset(sc); + rl2_enable(sc, 1); +} + +/* Handle single card interrupt. */ +int +rl2intr(arg) + void * arg; +{ + struct rl2_softc * sc = (struct rl2_softc *)arg; + extern int cold; + + dprintf("!"); + + /* Tell card not to interrupt any more. */ + rl2_enable(sc, 0); + + if (cold) + /* During autoconfig - must handle interrupts now. */ + rl2softintr(sc); + else + /* Handle later. */ + timeout(rl2softintr, sc, 1); + + return (1); +} + +/* Process earlier card interrupt at splnetintr. */ +static void +rl2softintr(arg) + void * arg; +{ + struct rl2_softc *sc = (struct rl2_softc *)arg; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int len; + u_int8_t w; + struct rl2_mm_cmd hdr; + + dprintf(" si("); + + again: + /* Save wakeup state. */ + w = rl2_wakeup(sc, RL2_WAKEUP_SET); + + if ((len = rl2_rx_request(sc, 300)) < 0) { + /* Error in transfer. */ + /* XXX need reset? */ + rl2_rx_end(sc); + } else if (len < sizeof hdr) { + /* Short message. */ + rl2_rx_end(sc); + printf("%s: short msg (%d)\n", sc->sc_dev.dv_xname, len); + ifp->if_ierrors++; + } else { + /* Valid message: read header and process. */ + rl2_rx_data(sc, &hdr, sizeof hdr); + rl2read(sc, &hdr, len); + } + + /* Ensure that wakeup state is unchanged if transmitting. */ + if (ifp->if_flags & IFF_OACTIVE) + w |= RL2_WAKEUP_NOCHANGE; + rl2_wakeup(sc, w); + + /* Check for more interrupts. */ + if (rl2_status_rx_ready(sc)) + goto again; + + /* Some cards need this? */ + rl2_eoi(sc); + + /* Re-enable card. */ + rl2_enable(sc, 1); + + dprintf(")"); +} + +/* Read and process a message from the card. */ +void +rl2read(sc, hdr, len) + struct rl2_softc *sc; + struct rl2_mm_cmd *hdr; + int len; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct mbuf *m; + struct ether_header *eh; + u_int8_t data[1538]; + u_int8_t *buf; + size_t buflen; + struct rl2_pdata pd = RL2_PDATA_INIT; + struct rl2_mm_synchronised * syncp = (struct rl2_mm_synchronised *)data; + + dprintf(" [read]"); + + /* Were we waiting for this message? */ + if (rl2_mbox_lock(sc, hdr->cmd_seq, (void **)&buf, &buflen) == 0) { +#ifdef DIAGNOSTIC + if (buflen < sizeof *hdr) + panic("rl2read buflen"); +#endif + bcopy(hdr, buf, sizeof *hdr); + buf += sizeof *hdr; + len -= sizeof *hdr; + buflen -= sizeof *hdr; + if (len) { + if (len == buflen) /* Expected size */ + rl2_rx_pdata(sc, buf, len, &pd); + else if (len < buflen) { /* Underfill */ +#ifdef DIAGNOSTIC + printf("%s: underfill %d<%d, cmd %c%d\n", + sc->sc_dev.dv_xname, + len, buflen, + hdr->cmd_letter, hdr->cmd_fn); +#endif + rl2_rx_pdata(sc, buf, len, &pd); + } else { /* Overflow */ +#ifdef DIAGNOSTIC + printf("%s: overflow %d>%d, cmd %c%d\n", + sc->sc_dev.dv_xname, + len, buflen, + hdr->cmd_letter, hdr->cmd_fn); +#endif + rl2_rx_pdata(sc, buf, buflen, &pd); + /* Drain the rest somewhere. */ + rl2_rx_pdata(sc, data, len - buflen, &pd); + } + } + rl2_rx_end(sc); + /* This message can now be handled by the waiter. */ + rl2_mbox_unlock(sc, hdr->cmd_seq, len + sizeof *hdr); + return; + } + + /* Otherwise, handle the message right here, right now. */ + + /* Check if we can cope with the size of this message. */ + if (len > sizeof data) { + printf("%s: big msg (%d)\n", sc->sc_dev.dv_xname, len); + ifp->if_ierrors++; + rl2_rx_end(sc); + /* XXX may need reset */ + return; + } + + /* Check for error results. */ + if (hdr->cmd_error & 0x80) { + printf("%s: command error 0x%02x command %c%d len=%d\n", + sc->sc_dev.dv_xname, + hdr->cmd_error & 0x7f, + hdr->cmd_letter, hdr->cmd_fn, + len); + ifp->if_ierrors++; + rl2_rx_end(sc); + /* XXX may need reset */ + return; + } + + /* + * "b1": Receiving a packet is a special case. + * We wish to read the data with pio straight into an + * mbuf to avoid a memory-memory copy. + */ + if (hdr->cmd_letter == 'b' && hdr->cmd_fn == 1) { + m = rl2get(sc, hdr, len); + rl2_rx_end(sc); + if (m == NULL) + return; + ifp->if_ipackets++; +#ifdef DIAGNOSTIC + if (bcmp(mtod(m, u_int8_t *), "prox", 4) == 0) { + printf("%s: proxim special packet received\n", + sc->sc_dev.dv_xname); + } +#endif + eh = mtod(m, struct ether_header *); +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + m_adj(m, sizeof (struct ether_header)); + ether_input(ifp, eh, m); + return; + } + + + /* Otherwise we read the packet into a buffer on the stack. */ + bcopy(hdr, data, sizeof *hdr); + if (len > sizeof *hdr) + rl2_rx_pdata(sc, data + sizeof *hdr, len - sizeof *hdr, &pd); + rl2_rx_end(sc); + +#ifdef RL2DUMP + printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname, + hdr->cmd_letter, hdr->cmd_fn, hdr->cmd_seq); + RL2DUMPHEX(hdr, sizeof hdr); + printf(":"); + RL2DUMPHEX(data + sizeof hdr, len - sizeof hdr); + printf("\n"); +#endif + + switch (RL2_MM_CMD(hdr->cmd_letter, hdr->cmd_fn)) { + case RL2_MM_CMD('b', 0): /* b0: Transmit done. */ +#ifdef DIAGNOSTIC + if (len != 4) + printf("%s: 'b0' len %d != 4\n", + sc->sc_dev.dv_xname, len); +#endif + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_opackets++; + rl2start(ifp); + break; + + case RL2_MM_CMD('a', 20): /* a20: Card fault. */ + printf("%s: hardware fault\n", sc->sc_dev.dv_xname); + /* Bring it down. */ + rl2stop(sc); + ifp->if_oerrors++; + if_down(ifp); /* The next if_up() will cause a hard reset. */ + break; + + case RL2_MM_CMD('a', 4): /* a4: Sync'd. */ + if (bcmp(syncp->enaddr, sc->sc_arpcom.ac_enaddr, + ETHER_ADDR_LEN) == 0) { + /* Sync'd to own enaddr. */ + printf("%s: nothing to sync to; now master ", + sc->sc_dev.dv_xname); + } + else + printf("%s: synchronised to ", sc->sc_dev.dv_xname); + printf("%.11s (%s) channel %d/%d\n", + syncp->mastername, + ether_sprintf(syncp->enaddr), + syncp->channel, + syncp->subchannel); + + /* Record the new circumstances. */ + sc->sc_param.rp_channel = syncp->channel; + sc->sc_param.rp_subchannel = syncp->subchannel; + sc->sc_state |= RL2_STATE_SYNC; + + /* Resume sending. */ + rl2start(ifp); + break; + + case RL2_MM_CMD('a', 5): /* a4: Lost sync. */ + printf("%s: lost sync\n", sc->sc_dev.dv_xname); + sc->sc_state &= ~RL2_STATE_SYNC; + break; + + case RL2_MM_CMD('a', 18): /* a18: Roaming. */ + printf("%s: roaming\n", sc->sc_dev.dv_xname); + break; + default: +#ifdef DIAGNOSTIC + printf("%s: msg `%c%d' seq %d data {", + sc->sc_dev.dv_xname, + hdr->cmd_letter, hdr->cmd_fn, hdr->cmd_seq); + RL2DUMPHEX(hdr, sizeof hdr); + printf(":"); + RL2DUMPHEX(data, len); + printf("}\n"); +#endif + break; + } + +} + +/* Extract a received network packet from the card. */ +static struct mbuf * +rl2get(sc, hdr, totlen) + struct rl2_softc *sc; + struct rl2_mm_cmd *hdr; + int totlen; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int len; + struct mbuf *m, **mp, *top; + struct rl2_pdata pd = RL2_PDATA_INIT; + /* u_int8_t hwhdr[ETHER_ADDR_LEN * 2]; */ + + dprintf(" [get]"); + +#ifdef RL2DUMP + printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname, + hdr->cmd_letter, hdr->cmd_fn, hdr->cmd_seq); + RL2DUMPHEX(hdr, sizeof hdr); + printf(":"); +#endif + + totlen -= sizeof *hdr; +#ifdef DIAGNOSTIC + if (totlen <= 0) { + printf("%s: empty packet", sc->sc_dev.dv_xname); + goto drop; + } +#endif + +#if 0 + totlen -= sizeof hwhdr; + /* Skip the hardware header. */ + rl2_rx_pdata(sc, hwhdr, sizeof hwhdr, &pd); +#ifdef RL2DUMP + RL2DUMPHEX(hwhdr, sizeof hwhdr); + printf("/"); +#endif +#endif + /* (Most of the following code fleeced from elink3.c.) */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto drop; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + len = MHLEN; + top = 0; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) { + m_freem(top); + goto drop; + } + len = MLEN; + } + if (top && totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + len = MCLBYTES; + } + len = min(totlen, len); + rl2_rx_pdata(sc, mtod(m, u_int8_t *), len, &pd); +#ifdef RL2DUMP + RL2DUMPHEX(mtod(m, u_int8_t *), len); + if (totlen != len) + printf("|"); +#endif + m->m_len = len; + totlen -= len; + *mp = m; + mp = &m->m_next; + } +#ifdef RL2DUMP + printf("\n"); +#endif + return m; + +drop: +#ifdef RL2DUMP + printf(": drop\n"); +#endif + ifp->if_iqdrops++; + return NULL; +} + +/* Interface control. */ +static int +rl2ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct rl2_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + s = splnet(); + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + splx(s); + return error; + } + + switch (cmd) { + case SIOCSIFADDR: + /* Set address. */ + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + rl2init(sc); + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif + default: + rl2init(sc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* Was running, want down: stop. */ + rl2stop(sc); + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* Was not running, want up: start. */ + rl2init(sc); + } + + if ((ifp->if_flags & IFF_RUNNING) != 0 && + ((ifp->if_flags & IFF_PROMISC) != sc->sc_promisc)) { + /* Promiscuity changed. */ + sc->sc_promisc = ifp->if_flags & IFF_PROMISC; + rl2_setparam(sc); + } + + /* XXX Deal with other flag changes? */ + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + return (EOPNOTSUPP); + + /* XXX There should be a way to change the channel & secid here. */ + + default: + error = EINVAL; + break; + } + + splx(s); + return (error); +} + +/* Stop output from the card. */ +static void +rl2stop(sc) + struct rl2_softc *sc; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + + dprintf(" [stop]"); + /* XXX Should kill interrupts? */ + /* rl2_enable(sc, 0); */ + ifp->if_flags &= ~IFF_RUNNING; +} + +/* Test for existence of card. */ +static int +rl2_probe(sc) + struct rl2_softc *sc; +{ + + dprintf(" [probe]"); + /* If we can reset it, it's there. */ + return(rl2_reset(sc)); +} + +/* Get MAC address from card. */ +static int +rl2_getenaddr(sc, enaddr) + struct rl2_softc *sc; + u_int8_t * enaddr; +{ + struct rl2_mm_cmd query = RL2_MM_GETENADDR; + struct rl2_mm_gotenaddr response = { RL2_MM_GETENADDR }; + + if (rl2_msg_txrx(sc, &query, sizeof query, + &response, sizeof response)) + return (-1); + bcopy(response.enaddr, enaddr, sizeof response.enaddr); + return (0); +}; + +/* Get firmware version string from card. */ +static int +rl2_getpromvers(sc, ver, verlen) + struct rl2_softc *sc; + char *ver; + int verlen; +{ + struct rl2_mm_cmd query = RL2_MM_GETPROMVERSION; + struct rl2_mm_gotpromversion response = { RL2_MM_GOTPROMVERSION }; + int i; + +#ifdef DIAGNOSTIC + if (verlen != sizeof response.version) + panic("rl2_getpromvers"); +#endif + + if (rl2_msg_txrx(sc, &query, sizeof query, + &response, sizeof response)) + return (-1); + bcopy(response.version, ver, verlen); + /* Nul trailing spaces. */ + for (i = verlen - 1; i >= 0 && ver[i] <= ' '; i--) + ver[i] = '\0'; + return (0); +}; + +/* Set default operational parameters on card. */ +static int +rl2_setparam(sc) + struct rl2_softc *sc; +{ + struct rl2_mm_setparam param = { RL2_MM_SETPARAM }; + struct rl2_mm_paramset presponse; +#if 0 + struct rl2_mm_setmagic magic = { RL2_MM_SETMAGIC }; + struct rl2_mm_disablehopping hop = { RL2_MM_DISABLEHOPPING }; + struct rl2_mm_cmd response; +#endif + + bzero((char*)¶m + sizeof param.mm_cmd, + sizeof param - sizeof param.mm_cmd); + + dprintf(" [setting parameters]"); + param.opmode = (sc->sc_promisc ? RL2_MM_SETPARAM_OPMODE_PROMISC : + RL2_MM_SETPARAM_OPMODE_NORMAL); + param.stationtype = sc->sc_param.rp_stationtype; + + /* Spread-spectrum frequency hopping. */ + param.hop_period = 1; + param.bfreq = 2; + param.sfreq = 7; + + /* Choose channel. */ + param.channel = sc->sc_param.rp_channel; + param.subchannel = sc->sc_param.rp_subchannel; + param.domain = sc->sc_param.rp_domain; + + /* Name of this station when acting as master. */ + bcopy(sc->sc_param.rp_master, param.mastername, sizeof param.mastername); + + /* Security params. */ + param.sec1 = (sc->sc_param.rp_security & 0x0000ff) >> 0; + param.sec2 = (sc->sc_param.rp_security & 0x00ff00) >> 8; + param.sec3 = (sc->sc_param.rp_security & 0xff0000) >> 16; + + param.sync_to = 1; + bzero(param.syncname, sizeof param.syncname); + + if (rl2_msg_txrx(sc, ¶m, sizeof param, + &presponse, sizeof presponse)) + return (-1); +#if 0 + dprintf(" [setting magic]"); + magic.fairness_slot = 3; /* lite: 1, norm: 3, off: -1 */ + magic.deferral_slot = 3; /* lite: 0, norm: 3, off: -1 */ + magic.regular_mac_retry = 7; + magic.frag_mac_retry = 10; + magic.regular_mac_qfsk = 2; + magic.frag_mac_qfsk = 5; + magic.xxx1 = 0xff; + magic.xxx2 = 0xff; + magic.xxx3 = 0xff; + magic.xxx4 = 0x00; + if (rl2_msg_txrx(sc, &magic, sizeof magic, + &response, sizeof response)) + return (-1); + + dprintf(" [disabling freq hopping]"); + hop.hopflag = RL2_MM_DISABLEHOPPING_HOPFLAG_DISABLE; + if (rl2_msg_txrx(sc, &hop, sizeof hop, + &response, sizeof response)) + return (-1); + +#endif + return (0); +} + +#if 0 +/* Configure the way the card leaves a basestation. */ +static int +rl2_roamconfig(sc) + struct rl2_softc *sc; +{ + struct rl2_mm_setroaming roam = { RL2_MM_SETROAMING }; + struct rl2_mm_cmd response; + static int retry[3] = { 6, 6, 4 }; + static int rssi[3] = { 5, 15, 5 }; + + dprintf(" [roamconfig]"); +#ifdef DIAGNOSTIC + if (sc->sc_param.rp_roamconfig > 2) + panic("roamconfig"); +#endif + roam.sync_alarm = 0; + roam.retry_thresh = retry[sc->sc_param.rp_roamconfig]; + roam.rssi_threshold = rssi[sc->sc_param.rp_roamconfig]; + roam.xxx1 = 0x5a; + roam.sync_rssi_threshold = 0; + roam.xxx2 = 0x5a; + roam.missed_sync = 0x4; + if (rl2_msg_txrx(sc, &roam, sizeof roam, + &response, sizeof response)) + return (-1); + + return (0); +} + +/* Enable roaming. */ +static int +rl2_roam(sc) + struct rl2_softc *sc; +{ + struct rl2_mm_cmd roam = RL2_MM_ROAM; + struct rl2_mm_cmd response; + + return (rl2_msg_txrx(sc, &roam, sizeof roam, + &response, sizeof response)); +} + +/* Enable multicast capability. */ +static int +rl2_multicast(sc, enable) + struct rl2_softc *sc; + int enable; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct rl2_mm_multicast mcast = { RL2_MM_MULTICAST }; + struct rl2_mm_cmd response; + int ret; + + mcast.enable = enable; + + ret = rl2_msg_txrx(sc, &mcast, sizeof mcast, + &response, sizeof response); + if (ret == 0) { + if (enable) + ifp->if_flags |= IFF_MULTICAST; + else + ifp->if_flags &= ~IFF_MULTICAST; + } + return (ret); +} + +/* Search for and sync with any master. */ +static int +rl2_searchsync(sc) + struct rl2_softc *sc; +{ + struct rl2_mm_search search = { RL2_MM_SEARCH }; + struct rl2_mm_searching response; + + bzero(search.xxx1, sizeof search.xxx1); + search.domain = sc->sc_param.rp_domain; + search.roaming = 1; + search.xxx3 = 0; + search.xxx4 = 1; + search.xxx5 = 0; + bzero(search.xxx6, sizeof search.xxx6); + + return (rl2_msg_txrx(sc, &search, sizeof search, + &response, sizeof response)); +} +#endif + +#if notyet /* unused */ +/* Set values from an external parameter block. */ +static int +rl2_iosetparam(sc, param) + struct rl2_softc *sc; + struct rl2_param *param; +{ + int error = 0; + + if (param->rp_roamconfig > 2) + error = EINVAL; + if (param->rp_security > 0x00ffffff) + error = EINVAL; + if (param->rp_stationtype > 2) + error = EINVAL; + if (param->rp_channel > 15) + error = EINVAL; + if (param->rp_subchannel > 15) + error = EINVAL; + if (error == 0) { + /* Apply immediately. */ + bcopy(param, &sc->sc_param, sizeof *param); + if (rl2_setparam(sc)) + error = EIO; + } + return (error); +} + +/* Protect the eeprom from storing a security ID(?) */ +static int +rl2_lockprom(sc) + struct rl2_softc *sc; +{ + struct rl2_mm_cmd lock = RL2_MM_EEPROM_PROTECT; + struct rl2_mm_cmd response; + + /* XXX Always yields an error? */ + return (rl2_msg_txrx(sc, &lock, sizeof lock, + &response, sizeof response)); +} + +/* Set the h/w Inactivity Time Out timer on the card. */ +static int +rl2_ito(sc) + struct rl2_softc * sc; +{ + struct rl2_mm_setito ito = { RL2_MM_MULTICAST }; + struct rl2_mm_cmd response; + + ito.xxx = 3; + ito.timeout = LLDInactivityTimeOut /* enabler, 0 or 1 */; + ito.bd_wakeup = LLDBDWakeup /* 0 */; + ito.pm_sync = LLDPMSync /* 0 */; + ito.sniff_time = ito.timeout ? LLDSniffTime /* 0 */ : 0; + + if (rl2_msg_txrx(sc, &ito, sizeof ito, + &response, sizeof response)) + return (-1); +} + +/* Put the card into standby mode. */ +static int +rl2_standby(sc) + struct rl2_softc * sc; +{ + struct rl2_mm_standby standby = { RL2_MM_STANDBY }; + + standby.xxx = 0; + if (rl2_msg_txrx(sc, &ito, sizeof ito, NULL, 0)) + return (-1); +} +#endif diff --git a/sys/dev/ic/rl2.h b/sys/dev/ic/rl2.h new file mode 100644 index 00000000000..98259a8fc64 --- /dev/null +++ b/sys/dev/ic/rl2.h @@ -0,0 +1,31 @@ +/* $OpenBSD: rl2.h,v 1.1 1999/06/21 23:21:46 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public domain. + * + * Proxim RangeLAN2 parameters. + * + * Eventually, there should be a way of getting and setting these + * from user space. Perhaps through ioctl(). + */ + +struct rl2_param { + u_int8_t rp_roamconfig; /* roam speed */ +#define RL2_ROAM_SLOW 0 +#define RL2_ROAM_NORMAL 1 +#define RL2_ROAM_FAST 2 + u_int32_t rp_security; /* security id */ +#define RL2_SECURITY_DEFAULT 0x0010203 + u_int8_t rp_stationtype; +#define RL2_STATIONTYPE_SLAVE 0 +#define RL2_STATIONTYPE_ALTMASTER 1 +#define RL2_STATIONTYPE_MASTER 2 + u_int8_t rp_domain; + u_int8_t rp_channel; + u_int8_t rp_subchannel; + char rp_master[11]; /* valid only when st.type is master */ +}; + +/* XXX possible ioctls to use */ +#define RL2IOSPARAM _IOW('2', 1, struct rl2_param) /* set params */ +#define RL2IOGPARAM _IOR('2', 2, struct rl2_param) /* get params */ + diff --git a/sys/dev/ic/rl2cmd.h b/sys/dev/ic/rl2cmd.h new file mode 100644 index 00000000000..9c32dd63b24 --- /dev/null +++ b/sys/dev/ic/rl2cmd.h @@ -0,0 +1,269 @@ +/* $OpenBSD: rl2cmd.h,v 1.1 1999/06/21 23:21:46 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public Domain. + * + * RangeLAN2 host-to-card message protocol. + */ + +/* + * Micro-message commands + */ + +struct rl2_mm_cmd { + u_int8_t cmd_letter; /* command letter */ + u_int8_t cmd_seq; /* must increment, <0x80 */ + u_int8_t cmd_fn; /* function number */ + u_int8_t cmd_error; /* reserved */ +}; + +#define RL2_MM_CMD(l,n) ((((unsigned int)l)<<8) | ((unsigned int)n)) +#define RL2_MM_CMD_LETTER(cmd) ((unsigned char)(((cmd) & 0xff00)>>8)) +#define RL2_MM_CMD_FUNCTION(cmd) ((unsigned char)((cmd) & 0xff)) + +#if 0 + /* A: Initialisation commands */ + RL2_MM_INITIALIZE = 0, /* A0 a0 */ /* [3]>0 */ + RL2_MM_SEARCH_AND_SYNC, /* A1 a1- */ + RL2_MM_SEARCH_CONTINUE, /* A2 a2. */ + RL2_MM_ABORT_SEARCH, /* A3 a3. */ + RL2_MM_SYNCHRONISED, /* a4 */ /*HandleInSync*/ + RL2_MM_UNSYNCHRONISED, /* A5 */ + RL2_MM_GOTO_STANDBY, /* A6 */ /* stop() */ + RL2_MM_ITO, /* A7 a7. */ + RL2_MM_KEEPALIVE, /* A8 a8. */ + RL2_MM_MULTICAST, /* A9 a9. */ + RL2_MM_RFNC_STATS, /* A11 a11.*/ + RL2_MM_SECURITY, /* A12 a12.*/ + RL2_MM_ROM_VERSION, /* A13 a13 */ + RL2_MM_GLOBAL_ADDR, /* A14 a14 */ + RL2_MM_CONFIGMAC, /* A16 a16.*/ + RL2_MM_ROAMPARAM, /* A17 a17.*/ + RL2_MM_ROAMING, /* a18 */ /* LLDRoam */ + RL2_MM_ROAM, /* A19 a19 */ + RL2_MM_FAULT, /* a20 */ /* beep,reset */ + RL2_MM_SNIFF_MODE, /* A22 */ + RL2_MM_DISABLE_EEPROM_WRITE, /* A23 a23.*/ + RL2_MM_ENABLE_EEPROMP_WRITE, /* A24 a24.*/ + RL2_MM_HOPSTATS, /* A35 a35 */ + /* B: Data commands */ + /* C: Diagnostic commands */ + RL2_MM_DISABLE_HOPPING, /* C9 */ +#endif + +#define RL2_CMDCODE(letter, num) ((((letter) & 0xff) << 8) | ((num) & 0xff)) + +#define u_int4_t u_int8_t + +struct rl2_mm_setparam { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SETPARAM { 'A', 0, 0, 0 } +#define RL2_MM_SETPARAM_CODE RL2_CMDCODE('A',0) + u_int8_t enaddr[6]; + u_int8_t opmode; +#define RL2_MM_SETPARAM_OPMODE_NORMAL 0 +#define RL2_MM_SETPARAM_OPMODE_PROMISC 1 +#define RL2_MM_SETPARAM_OPMODE_PROTOCOL 2 + u_int8_t stationtype; +#define RL2_MM_SETPARAM_STATIONTYPE_SLAVE RL2_STATIONTYPE_SLAVE +#define RL2_MM_SETPARAM_STATIONTYPE_ALTMASTER RL2_STATIONTYPE_ALTMASTER +#define RL2_MM_SETPARAM_STATIONTYPE_MASTER RL2_STATIONTYPE_MASTER + u_int8_t hop_period; + u_int8_t bfreq; + u_int8_t sfreq; + u_int4_t channel : 4; /* lower bits */ + u_int4_t subchannel : 4; /* upper bits */ + char mastername[11]; + u_int4_t sec1 : 4; /* default 3 */ + u_int4_t domain : 4; /* default 0 */ + u_int8_t sec2; /* default 2 */ + u_int8_t sec3; /* default 1 */ + u_int8_t sync_to; /* 1 if roaming */ + u_int8_t xxx_pad; /* zero */ + char syncname[11]; +}; + +struct rl2_mm_paramset { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_PARAMSET { 'a', 0, 0, 0 } +#define RL2_MM_PARAMSET_CODE RL2_CMDCODE('a',0) + u_int8_t xxx; +}; + +struct rl2_mm_search { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SEARCH { 'A', 0, 1, 0 } +#define RL2_MM_SEARCH_CODE RL2_CMDCODE('A',1) + u_int8_t xxx1[23]; + u_int4_t xxx2 : 4; + u_int4_t domain : 4; + u_int8_t roaming; + u_int8_t xxx3; /* default 0 */ + u_int8_t xxx4; /* default 1 */ + u_int8_t xxx5; /* default 0 */ + u_int8_t xxx6[11]; +}; + +struct rl2_mm_searching { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SEARCHING { 'a', 0, 1, 0 } +#define RL2_MM_SEARCHING_CODE RL2_CMDCODE('a',1) + u_int8_t xxx; +}; + +#define RL2_MM_ABORTSEARCH { 'A', 0, 3, 0 } +#define RL2_MM_ABORTSEARCH_CODE RL2_CMDCODE('A',3) + +struct rl2_mm_synchronised { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SYNCHRONISED { 'a', 0, 4, 0 } +#define RL2_MM_SYNCHRONISED_CODE RL2_CMDCODE('a',4) + u_int4_t channel : 4; /* lower bits */ + u_int4_t subchannel : 4; /* upper bits */ + char mastername[11]; + u_int8_t enaddr[6]; +}; + +#define RL2_MM_UNSYNCHRONISED { 'a', 0, 5, 0 } +#define RL2_MM_UNSYNCHRONISED_CODE RL2_CMDCODE('a',5) + +struct rl2_mm_standby { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_STANDBY { 'A', 0, 6, 0 } + u_int8_t xxx; /* default 0 */ +}; + +struct rl2_mm_setito { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SETITO { 'A', 0, 7, 0 } + u_int8_t xxx; /* default 3 */ + u_int8_t timeout; + unsigned int bd_wakeup : 1; + unsigned int pm_sync : 7; + u_int8_t sniff_time; +}; + +#define RL2_MM_GOTITO { 'a', 0, 7, 0 } + +#define RL2_MM_SENDKEEPALIVE { 'A', 0, 8, 0 } + +struct rl2_mm_multicast { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_MULTICAST { 'A', 0, 9, 0 } + u_int8_t enable; +}; + +#define RL2_MM_MULTICASTING { 'a', 0, 9, 0 } + +#define RL2_MM_GETSTATS { 'A', 0, 11, 0 } +#define RL2_MM_GOTSTATS { 'a', 0, 11, 0 } + +struct rl2_mm_setsecurity { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SETSECURITY { 'A', 0, 12, 0 } + u_int8_t sec1; + u_int8_t sec2; + u_int8_t sec3; +}; + +#define RL2_MM_GOTSECURITY { 'a', 0, 12, 0 } + +#define RL2_MM_GETPROMVERSION { 'A', 0, 13, 0 } + +struct rl2_mm_gotpromversion { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_GOTPROMVERSION { 'a', 0, 13, 0 } + u_int8_t xxx; + char version[7]; +}; + +#define RL2_MM_GETENADDR { 'A', 0, 14, 0 } + +struct rl2_mm_gotenaddr { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_GOTENADDR { 'a', 0, 14, 0 } + u_int8_t xxx; + u_int8_t enaddr[6]; +}; + +struct rl2_mm_setmagic { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SETMAGIC { 'A', 0, 16, 0 } + u_char fairness_slot : 3; + u_char deferral_slot : 5; + u_int8_t regular_mac_retry; /* default 0x07 */ + u_int8_t frag_mac_retry; /* default 0x0a */ + u_int8_t regular_mac_qfsk; /* default 0x02 */ + u_int8_t frag_mac_qfsk; /* default 0x05 */ + u_int8_t xxx1; /* default 0xff */ + u_int8_t xxx2; /* default 0xff */ + u_int8_t xxx3; /* default 0xff */ + u_int8_t xxx4; /* zero */ +}; + +#define RL2_MM_GOTMAGIC { 'a', 0, 16, 0 } + +struct rl2_mm_setroaming { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SETROAMING { 'A', 0, 17, 0 } + u_int8_t sync_alarm; + u_int8_t retry_thresh; + u_int8_t rssi_threshold; + u_int8_t xxx1; /* default 0x5a */ + u_int8_t sync_rssi_threshold; + u_int8_t xxx2; /* default 0xa5 */ + u_int8_t missed_sync; +}; + +#define RL2_MM_GOTROAMING { 'a', 0, 17, 0 } + +#define RL2_MM_ROAMING { 'a', 0, 18, 0 } +#define RL2_MM_ROAM { 'A', 0, 19, 0 } +#define RL2_MM_FAULT { 'a', 0, 20, 0 } +#define RL2_MM_FAULT_CODE RL2_CMDCODE('a',20) +#define RL2_MM_EEPROM_PROTECT { 'A', 0, 23, 0 } +#define RL2_MM_EEPROM_PROTECTED { 'a', 0, 23, 0 } +#define RL2_MM_EEPROM_UNPROTECT { 'A', 0, 24, 0 } +#define RL2_MM_EEPROM_UNPROTECTED { 'a', 0, 24, 0 } +#define RL2_MM_HOP_STATISTICS { 'a', 0, 35, 0 } + +struct rl2_mm_sendpacket { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_SENDPACKET { 'B', 0, 0, 0 } + u_int8_t mode; +#define RL2_MM_SENDPACKET_MODE_BIT7 0x80 +#define RL2_MM_SENDPACKET_MODE_ZFIRST 0x20 +#define RL2_MM_SENDPACKET_MODE_QFSK 0x03 + u_int8_t power; /* default 0x70 */ + u_int8_t length_lo; + u_int8_t length_hi; + u_int8_t xxx1; /* default 0 */ + u_int8_t xxx2; /* default 0 */ + u_int8_t sequence; /* must increment */ + u_int8_t xxx3; /* default 0 */ +}; + +#define RL2_MM_SENTPACKET { 'b', 0, 0, 0 } + +struct rl2_mm_recvpacket { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_RECVPACKET { 'b', 0, 1, 0 } + u_int8_t xxx[8]; +}; + +struct rl2_mm_disablehopping { + struct rl2_mm_cmd mm_cmd; +#define RL2_MM_DISABLEHOPPING { 'C', 0, 9, 0 } + u_int8_t hopflag; +#define RL2_MM_DISABLEHOPPING_HOPFLAG_DISABLE 0x52 +}; + +/* queue */ +struct rl2_rx { + SIMPLEQ_ENTRY(rl2_rx) rx_entry; + size_t rx_size; + struct rl2_mm_cmd rx_hdr; + u_int8_t rx_data[1]; +}; + +#define RL2_MAX_RX_QUEUE_LEN 16 + diff --git a/sys/dev/ic/rl2reg.h b/sys/dev/ic/rl2reg.h new file mode 100644 index 00000000000..a03f68aa9c2 --- /dev/null +++ b/sys/dev/ic/rl2reg.h @@ -0,0 +1,266 @@ +/* $OpenBSD: rl2reg.h,v 1.1 1999/06/21 23:21:47 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public Domain. + * + * RangeLAN2 registers + */ + +/* + * The RangeLAN2 cards provide four control registers for transferring + * messaged between the host and the card using programmed i/o. + * + * A transfer protocol is followed when sending asynchronous messages to, + * and receiving messages from, the card. + * + * DATA + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | data | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * STATUS + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |WAKEUP | tx message state |CLRNAK | rx message state | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * CONTROL + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | | | 16BIT | RESET | | | TXINT | RXINT | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * INTSEL + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | | | |ENABLE | interrupt line | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ + +/* Register offsets */ + +#define RL2_REG_DATA 0 +#define RL2_REG_STATUS 2 +#define RL2_REG_CONTROL 4 +#define RL2_REG_EOI 5 +#define RL2_REG_INTSEL 6 +#define RL2_NPORTS 8 + +/* + * A short delay is needed (16ms?) after register writes. + * XXX This is done by performing an innocent and harmless bus read. (i386) + * This is what Proxim's driver does, anyway. + */ +#define _rl2_regacc_delay() \ + bus_space_read_1(I386_BUS_SPACE_IO, 0, 0x61) + +static void _rl2_register_write_1 __P((struct rl2_softc *, u_int8_t, + u_int8_t)); +static u_int8_t _rl2_register_read_1 __P((struct rl2_softc *, u_int8_t)); +static int rl2_status_rx_ready __P((struct rl2_softc *)); + +/* Register access */ + +/* Write to a register */ +static inline void +_rl2_register_write_1(sc, regoff, value) + struct rl2_softc *sc; + u_int8_t regoff; + u_int8_t value; +{ + +#ifdef RL2DEBUG_REG + printf(" %c<%02x", "DDS3CEI7"[regoff], value); +#endif + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (regoff), (value)); + _rl2_regacc_delay(); +} + +/* Read from a register */ +static inline u_int8_t +_rl2_register_read_1(sc, regoff) + struct rl2_softc *sc; + u_int8_t regoff; +{ + u_int8_t ret; + + ret = bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (regoff)); +#ifdef RL2DEBUG_REG + if (ret != (sc)->dbg_oreg[regoff]) { + /* avoid spewing out too much debug info */ + printf(" %c>%02x", "DDS3CEI7"[regoff], ret); + (sc)->dbg_oreg[regoff] = ret; + } +#endif + return (ret); +} + +/* Data register */ + +/* 8-bit data access */ +#define rl2_data_write_1(sc, value) \ + _rl2_register_write_1(sc, RL2_REG_DATA, (value)) +#define rl2_data_read_1(sc) \ + _rl2_register_read_1(sc, RL2_REG_DATA) +#define rl2_data_write_multi_1(sc, buf, len) \ + bus_space_write_multi_1((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA, (buf), (len)) +#define rl2_data_read_multi_1(sc, buf, len) \ + bus_space_read_multi_1((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA, (buf), (len)) + +/* 16-bit data access */ +#define rl2_data_write_2(sc, value) \ + bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA, (value)) +#define rl2_data_read_2(sc) \ + bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA) +#define rl2_data_write_multi_2(sc, buf, len) \ + bus_space_write_multi_2((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA, (buf), (len)) +#define rl2_data_read_multi_2(sc, buf, len) \ + bus_space_read_multi_2((sc)->sc_iot, (sc)->sc_ioh, \ + RL2_REG_DATA, (buf), (len)) + +/* Status register */ + +#define RL2_STATUS_CLRNAK 0x08 +#define RL2_STATUS_WAKEUP 0x80 + +/* Status codes */ +#define RL2_STATUS_TX_IDLE 0x00 +#define RL2_STATUS_TX_HILEN_AVAIL 0x01 +#define RL2_STATUS_TX_HILEN_ACCEPT 0x02 +#define RL2_STATUS_TX_XFR_COMPLETE 0x03 +#define RL2_STATUS_TX_XFR 0x04 +#define RL2_STATUS_TX_ERROR 0x05 +#define RL2_STATUS_TX_LOLEN_AVAIL 0x06 +#define RL2_STATUS_TX_LOLEN_ACCEPT 0x07 +#define RL2_STATUS_TX_MASK 0x0f + +#define RL2_STATUS_RX_IDLE 0x00 +#define RL2_STATUS_RX_HILEN_AVAIL 0x10 +#define RL2_STATUS_RX_HILEN_ACCEPT 0x20 +#define RL2_STATUS_RX_XFR_COMPLETE 0x30 +#define RL2_STATUS_RX_XFR 0x40 +#define RL2_STATUS_RX_ERROR 0x50 +#define RL2_STATUS_RX_LOLEN_AVAIL 0x60 +#define RL2_STATUS_RX_LOLEN_ACCEPT 0x70 +#define RL2_STATUS_RX_MASK 0x70 + +#define rl2_status_write(sc, value) \ + _rl2_register_write_1(sc, RL2_REG_STATUS, (value)) +#define rl2_status_set(sc, bits) \ + rl2_status_write(sc, (sc)->sc_status |= (bits)) +#define rl2_status_clear(sc, bits) \ + rl2_status_write(sc, (sc)->sc_status &= ~(bits)) +#define _rl2_status_setmask(sc, mask, bits) \ +do { \ + int _s; \ + \ + _s = splhigh(); \ + (sc)->sc_status = ((sc)->sc_status & (mask)) | (bits); \ + rl2_status_write(sc, (sc)->sc_status); \ + splx(_s); \ +} while (0); +#define rl2_status_rx_write(sc, state) \ + _rl2_status_setmask((sc), ~RL2_STATUS_RX_MASK, state) +#define rl2_status_tx_write(sc, state) \ + _rl2_status_setmask((sc), ~RL2_STATUS_TX_MASK, state) +#define rl2_status_read(sc) \ + _rl2_register_read_1(sc, RL2_REG_STATUS) +#define rl2_status_rx_read(sc) \ + (rl2_status_read(sc) & ~RL2_STATUS_TX_MASK) +#define rl2_status_tx_read(sc) \ + (rl2_status_read(sc) & ~RL2_STATUS_RX_MASK) + +static inline int +rl2_status_rx_ready(sc) + struct rl2_softc *sc; +{ + u_int8_t status; + + status = rl2_status_rx_read(sc); + return (status == 0x60 || status == 0x10 || status == 0x50); +} + +#define rl2_status_tx_int(sc) do { \ + int _s = splhigh(); \ + \ + rl2_control_clear(sc, RL2_CONTROL_TXINT); \ + rl2_control_set(sc, RL2_CONTROL_TXINT); \ + splx(_s); \ +} while (0) +#define rl2_status_rx_int(sc) do { \ + int _s = splhigh(); \ + \ + rl2_control_clear(sc, RL2_CONTROL_RXINT); \ + rl2_control_set(sc, RL2_CONTROL_RXINT); \ + splx(_s); \ +} while (0) + +/* Control register */ + +#define RL2_CONTROL_RXINT 0x01 +#define RL2_CONTROL_TXINT 0x02 +#define RL2_CONTROL_BIT2 0x04 +#define RL2_CONTROL_BIT3 0x08 +#define RL2_CONTROL_RESET 0x10 +#define RL2_CONTROL_16BIT 0x20 +#define RL2_CONTROL_MASK 0x3f + +#define rl2_control_write(sc, value) \ + _rl2_register_write_1(sc, RL2_REG_CONTROL, \ + (sc)->sc_control = (value)) +#define rl2_control_read(sc) \ + _rl2_register_read_1(sc, RL2_REG_CONTROL) +#define rl2_control_set(sc, bits) \ + rl2_control_write(sc, (sc)->sc_control | (bits)) +#define rl2_control_clear(sc, bits) \ + rl2_control_write(sc, (sc)->sc_control & ~(bits)) +#define rl2_control_outofstandby(sc) do { \ + rl2_control_write(sc, (sc)->sc_control | RL2_CONTROL_RESET);\ + DELAY(30000); \ + rl2_control_write(sc, (sc)->sc_control); \ +} while (0) + +/* IntSel register */ + +#define RL2_INTSEL_IRQMASK 0x07 +#define RL2_INTSEL_ENABLE 0x10 +#define RL2_INTSEL_BIT7 0x80 + +#define rl2_intsel_disable(sc) do { \ + int _s; \ + \ + _s = splhigh(); \ + _rl2_register_write_1(sc, RL2_REG_INTSEL, \ + (sc)->sc_intsel &= ~RL2_INTSEL_ENABLE); \ + splx(_s); \ +} while (0) +#define rl2_intsel_enable(sc) do { \ + int _s; \ + \ + _s = splhigh(); \ + _rl2_register_write_1(sc, RL2_REG_INTSEL, \ + (sc)->sc_intsel |= RL2_INTSEL_ENABLE); \ + splx(_s); \ +} while (0) + +#define rl2_intsel_write(sc, value) \ + _rl2_register_write_1(sc, RL2_REG_INTSEL, \ + (sc)->sc_intsel |= (value)) + +/* End of interrupt signal (used on some newer cards?) */ + +#define rl2_eoi(sc) \ + (void) _rl2_register_read_1(sc, RL2_REG_EOI) + +/* Strings useful for debugging with printf("%b") */ + +#ifdef RL2DEBUG +#define RL2_BSTR_STATUS "\20\4BIT3\10BIT7" +#define RL2_BSTR_CONTROL "\20\1RXINT\2TXINT\3BIT2\4BIT3\5RESET\6BUS16" +#define RL2_BSTR_INTSEL "\20\5ENABLE" +#endif diff --git a/sys/dev/ic/rl2subr.c b/sys/dev/ic/rl2subr.c new file mode 100644 index 00000000000..1c13098cf4b --- /dev/null +++ b/sys/dev/ic/rl2subr.c @@ -0,0 +1,912 @@ +/* $OpenBSD: rl2subr.c,v 1.1 1999/06/21 23:21:47 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public Domain. + * + * Low level card protocol access to the Proxim RangeLAN2 wireless + * network adaptor. + * + * Information and ideas gleaned from + * - disassembly of Dave Koberstein's <davek@komacke.com> Linux driver + * (which is built with Proxim source), + * - Yoichi Shinoda's <shinoda@cs.washington.edu> BSDI driver, and + * - Geoff Voelker's <voelker@cs.washington.edu> Linux port of the same. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/device.h> +#include <sys/queue.h> +#include <sys/proc.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/rl2.h> +#include <dev/ic/rl2var.h> +#include <dev/ic/rl2reg.h> +#include <dev/ic/rl2cmd.h> + +static int rl2_tx_request __P((struct rl2_softc *, u_int16_t)); +static int rl2_tx_end __P((struct rl2_softc *)); + +/* + * Disables or enables interrupts from the card. Returns the old + * interrupt-enable state. + */ +int +rl2_enable(sc, enable) + struct rl2_softc * sc; + int enable; +{ + int s; + int was_enabled; + + s = splhigh(); + was_enabled = (sc->sc_intsel & RL2_INTSEL_ENABLE) ? 1 : 0; + if (enable != was_enabled) { + if (enable) + sc->sc_intsel |= 0x10; + else + sc->sc_intsel &=~0x10; + _rl2_register_write_1(sc, RL2_REG_INTSEL, sc->sc_intsel); + } + splx(s); + return (was_enabled); +} + +/* + * Perform a hard reset of the card. Determines bus width (8 or + * 16 bit), if sc->sc_width is unset. Returns 0 on success. + * Note: takes about 200ms at splhigh, meaning this is an expensive call, + * but normal (error-free) operation of the card will not need more than + * two resets - one at probe time, and the other when the interface is + * brought up. + */ +int +rl2_reset(sc) + struct rl2_softc * sc; +{ + int s; + int i; + int status; + u_int8_t op = 0x00; + + s = splhigh(); + dprintf(" R["); + if (sc->sc_cardtype & (RL2_CTYPE_UISA | RL2_CTYPE_ONE_PIECE)) + op = 0x04; + if (rl2_status_read(sc) & RL2_STATUS_WAKEUP) { + rl2_control_write(sc, op); + rl2_control_write(sc, op | RL2_CONTROL_RESET); + dprintf(" 7ms"); + DELAY(7000); + rl2_control_write(sc, op); + dprintf(" 7ms"); + DELAY(7000); + } + rl2_control_write(sc, op); + rl2_control_write(sc, op); + rl2_control_write(sc, op | RL2_CONTROL_BIT3); + dprintf(" 67ms"); + DELAY(67000); + rl2_status_write(sc, 0x00); + if (sc->sc_cardtype & (RL2_CTYPE_UISA | RL2_CTYPE_ONE_PIECE)) + rl2_control_write(sc, 0x38); + /* RL2_CONTROL_BIT3 | RL2_CONTROL_RESET | RL2_CONTROL_16BIT */ + else + rl2_control_write(sc, 0x2c); + /* RL2_CONTROL_BIT3 | RL2_CONTROL_BIT2 | RL2_CONTROL_16BIT */ + dprintf(" 67ms"); + DELAY(67000); + rl2_data_write_2(sc, 0xaa55); + rl2_status_write(sc, 0x5a); + splx(s); + for (i = 0; i < 2000; i++) { /* Proxim says 200 not 2000. */ + if ((status = rl2_status_read(sc)) == 0x5a) + break; + DELAY(1000); + } + dprintf(" (%dms)", i); + s = splhigh(); + if (status != 0x5a) { + splx(s); + printf("%s: reset timeout\n", sc->sc_dev.dv_xname); + dprintf("]=-1"); + return (-1); + } + if (sc->sc_width == 8) { + if (sc->sc_cardtype & (RL2_CTYPE_UISA | RL2_CTYPE_ONE_PIECE)) + rl2_control_write(sc, RL2_CONTROL_BIT3); + else + rl2_control_write(sc, RL2_CONTROL_BIT3 | + RL2_CONTROL_BIT2); + rl2_data_write_1(sc, 0x20); + } else if (sc->sc_width == 16) { + rl2_data_write_2(sc, 0x0000); + } else { + if (rl2_data_read_2(sc) == 0x55aa) { + rl2_data_write_2(sc, 0x0000); + sc->sc_width = 16; + } else { + if (sc->sc_cardtype & (RL2_CTYPE_UISA | + RL2_CTYPE_ONE_PIECE)) + rl2_control_write(sc, RL2_CONTROL_BIT3); + else + rl2_control_write(sc, RL2_CONTROL_BIT3 | + RL2_CONTROL_BIT2); + rl2_data_write_1(sc, 0x20); + sc->sc_width = 8; + } + printf("%s: %d bit bus\n", sc->sc_dev.dv_xname, sc->sc_width); + } + rl2_status_write(sc, 0x00); + sc->sc_intsel = 0; + rl2_intsel_write(sc, sc->sc_irq); + splx(s); + dprintf("]"); + return (0); +} + +/* + * Sets the new 'wakeup' state. Returns the old wakeup state. + * The special state value RL2_WAKEUP_SET should be used to wake the + * card up. The card can be partially put to sleep (presumably to save + * power) by sending it the 'Standby' command. + */ +u_int8_t +rl2_wakeup(sc, wnew) + struct rl2_softc * sc; + u_int8_t wnew; +{ + u_int8_t wold, s; + int i; + + /* Save what the last-written values were. */ + wold = (sc->sc_status & 0x80) | (sc->sc_control & 0x10); + + if (wnew == RL2_WAKEUP_SET) { + /* SetWakeupBit() */ + dprintf(" Ws["); + rl2_status_set(sc, 0x80); + if (0/*LLDInactivityTimeOut && + (sc->sc_cardtype & RL2_CTYPE_OEM)*/) { + dprintf (" 167ms"); + DELAY(167000); + } else { + dprintf (" .1ms"); + DELAY(100); + } + s = rl2_status_read(sc); + rl2_control_set(sc, 0x10); + if ((s & 0x80) != 0) + for (i = 0; i < 9; i++) { + dprintf(" 2ms"); + DELAY(2000); + rl2_status_set(sc, 0x80); + } + dprintf("]"); + } else { + /* ClearWakeupBit() */ + dprintf(" Wc["); + if ((wnew & 0x80) == 0) + rl2_status_clear(sc, 0x80); + if ((wnew & 0x10) == 0) + rl2_control_clear(sc, 0x10); + dprintf("]"); + } + return (wold); +} + +/* + * Performs the first (request) stage of transmitting a command message + * to the card. 'len' is the expected length of the message is needed. + * Returns: 0 on success + * 1 on timeout + * 2 on NAK (card busy, and will need a rl2_clear_nak() after 100ms) + */ +static int +rl2_tx_request(sc, len) + struct rl2_softc * sc; + u_int16_t len; +{ + /* TxRequest() */ + int s; + int i; + u_int8_t status; + + /* u_int8_t w; */ + /* w = rl2_wakeup(sc, RL2_WAKEUP_SET); */ + + dprintf(" Tr["); + if (sc->sc_width == 16) { + rl2_status_tx_write(sc, 0x01); + rl2_data_write_2(sc, len); + rl2_status_tx_int(sc); + + s = spl0(); + for (i = 0; i < 600; i++) { + status = rl2_status_tx_read(sc); + if (status == 0x02 || status == 0x05) + break; + DELAY(1000); + } + splx(s); + dprintf(" %dms", i); + if (status == 0x02) + goto success; + if (status == 0x05) + goto error; + } else if (sc->sc_width == 8) { + rl2_status_tx_write(sc, 0x06); + rl2_data_write_1(sc, len & 0x00ff); + rl2_status_tx_int(sc); + s = spl0(); + for (i = 0; i < 6800; i++) { + status = rl2_status_tx_read(sc); + if (status == 0x07) + break; + DELAY(1000); + } + splx(s); + dprintf(" %dms", i); + if (status == 0x07) { + rl2_data_write_1(sc, (len & 0xff00) >> 8); + rl2_status_tx_write(sc, 0x01); + s = spl0(); + for (i = 0; i < 600; i++) { + status = rl2_status_tx_read(sc); + if (status == 0x02 || status == 0x05) + break; + DELAY(1000); + } + splx(s); + dprintf(" %dms", i); + if (status == 0x02) + goto success; + if (status == 0x05) + goto error; + } + } +#ifdef DIAGNOSTIC + else + panic("rln: bus width"); +#endif + + printf("%s: tx_request timed out, status 0x%02x", + sc->sc_dev.dv_xname, status); + dprintf("]=(1)"); + return (1); + +error: + /* XXX Will need to clear nak within 100 ms. */ + dprintf("]=2"); +#ifdef DIAGNOSTIC + printf("%s: tx protocol fault (nak)\n", sc->sc_dev.dv_xname); +#endif + return (2); + +success: + /* rl2_wakeup(sc, w); */ + dprintf("]=0"); + return (0); +} + +/* + * Performs the third (and final) stage of transmitting a command + * message to the card. + * Returns: 0 on command success. + * non-zero on failure (card will need reset) + */ +static int +rl2_tx_end(sc) + struct rl2_softc * sc; +{ + /* EndOfTx() */ + int i; + int s; + u_int8_t status; + + dprintf(" Te["); + s = spl0(); + for (i = 0; i < 600; i++) { + status = rl2_status_tx_read(sc); + if (status == 0x03) + break; + DELAY(1000); + } + splx(s); + if (status == 0x03) { + rl2_status_tx_write(sc, 0x00); + dprintf("]=0"); + return (0); + } else { + printf("%s: tx cmd failed (%02x)\n", sc->sc_dev.dv_xname, + status); + /* XXX Needs reset? */ + dprintf("]=-1"); + return (-1); + } +} + +/* + * Performs first (request) stage of receiving a message from the card. + * Returns: 0 on failure, + * n>0 on success, where 'n' is the length of the message + */ + +int +rl2_rx_request(sc, timeo) + struct rl2_softc * sc; + int timeo; /* milliseconds */ +{ + /* RxRequest */ + int s; + int len = 0; + int i; + u_int8_t status; + u_int8_t hi, lo; + + dprintf(" Rr["); + status = rl2_status_rx_read(sc); + + /* Short wait for states 1|5|6. */ + s = spl0(); + for (i = 0; i < timeo; i++) { + if (status == 0x60 || status == 0x10 || status == 0x50) + break; + DELAY(1000); + status = rl2_status_rx_read(sc); + } + splx(s); + dprintf(" (%dms)",i); + + if (sc->sc_width == 16) { + if (status != 0x10) + goto badstatus_quiet; + /* Read 2 bytes. */ + len = rl2_data_read_2(sc); + } else if (sc->sc_width == 8) { + if (status != 0x60) + goto badstatus_quiet; + /* Read low byte. */ + lo = rl2_data_read_1(sc); + rl2_status_rx_write(sc, 0x70); + rl2_status_rx_int(sc); + s = spl0(); + for (i = 0; i < 600; i++) { + status = rl2_status_rx_read(sc); + if (status == 0x10) + break; + DELAY(1000); + } + splx(s); + if (status != 0x10) + goto badstatus; + /* Read high byte. */ + hi = rl2_data_read_1(sc); + len = lo | (hi << 8); + } +#ifdef DIAGNOSTIC + else + panic("rl2: bus width %d", sc->sc_width); +#endif + + dprintf(" len=%d]", len); + return (len); + +badstatus: + printf("%s: rx_request tiemd out, status %02x\n", + sc->sc_dev.dv_xname, status); +badstatus_quiet: + if (status == 0x50) + printf("%s: rx protocol error (nak)\n", sc->sc_dev.dv_xname); + dprintf("]"); + return (-1); +} + +/* Performs part of the second (transfer) stage of receiving a data message. */ +void +rl2_rx_pdata(sc, buf, len, pd) + struct rl2_softc * sc; + void * buf; + int len; + struct rl2_pdata * pd; +{ + char * data = (char *)buf; + + if (pd->p_nremain) { + *data++ = pd->p_data; + if (--len == 0) + return; + } + + pd->p_nremain = 0; + + if (sc->sc_width == 16) { + /* Round down to the closest even multiple. */ + rl2_data_read_multi_2(sc, data, len / 2); +#ifdef RL2DEBUG_REG + dprintf(" D>"); + dprinthex(data, len); +#endif + if (len & 1) { + /* Read the last byte plus a bit extra. */ + union { + u_int16_t w; + u_int8_t b[2]; + } u; + + u.w = rl2_data_read_2(sc); + data[len - 1] = u.b[0]; + pd->p_data = u.b[1]; + pd->p_nremain = 1; +#ifdef RL2DEBUG_REG + dprintf(" D>{%02x%02x}", u.b[0], u.b[1]); +#endif + } + } else if (sc->sc_width == 8) { + rl2_data_read_multi_1(sc, data, len); +#ifdef RL2DEBUG_REG + dprintf(" D>"); + dprinthex(data, len); +#endif + if (len & 1) { + /* Must read multiples of two. */ + pd->p_data = rl2_data_read_1(sc); + pd->p_nremain = 1; +#ifdef RL2DEBUG_REG + dprintf(" D>{%02x}", pd->p_data); +#endif + } + } + +} + +int +rl2_rx_data(sc, buf, len) + struct rl2_softc * sc; + void * buf; + int len; +{ + /* RxData() */ + struct rl2_pdata pd = { 0, 0 }; + int s; + int i; + u_int8_t status; + + dprintf(" Rd["); + rl2_status_rx_write(sc, 0x20); + rl2_status_rx_int(sc); + s = spl0(); + for (i = 0; i < 600; i++) { + status = rl2_status_rx_read(sc); + if (status == 0x40) + break; + DELAY(1000); + } + splx(s); + if (status != 0x40) { + dprintf("]=-1"); + return (-1); + } + + rl2_rx_pdata(sc, buf, len, &pd); +#ifdef DIAGNOSTIC + /* We should have nothing left over. */ + if (pd.p_nremain || len & 1) + panic("rl2_rx_data: leftover"); +#endif + + dprintf("]=0"); + return (0); +} + +void +rl2_rx_end(sc) + struct rl2_softc * sc; +{ + /* EndOfRx() */ + + dprintf(" Re["); + rl2_status_rx_write(sc, 0x30); + rl2_status_rx_int(sc); + /* rl2_wakeup(sc, 0); */ + dprintf("]"); +} + +/* Clear a transmission NAK from the card. */ +void +rl2_clear_nak(sc) + struct rl2_softc * sc; +{ + /* ClearNAK() */ + + rl2_status_tx_write(sc, 0x08); + rl2_status_tx_int(sc); +} + +/* + * Send a command message to the card. Returns; + * 2: NAK + * -1: failure + * 0: success + */ +int +rl2_msg_tx_start(sc, buf, pktlen, state) + struct rl2_softc * sc; + void * buf; + int pktlen; + struct rl2_msg_tx_state * state; +{ + struct rl2_mm_cmd * cmd = (struct rl2_mm_cmd *)buf; + int ret; + + state->ien = rl2_enable(sc, 0); + state->pd.p_nremain = 0; + + if (!(cmd->cmd_letter == 'A' && cmd->cmd_fn == 6)) /* Standby. */ + state->w = rl2_wakeup(sc, RL2_WAKEUP_SET); + else + state->w = RL2_WAKEUP_NOCHANGE; + + ret = rl2_tx_request(sc, pktlen); + if (ret == 2) { + rl2_clear_nak(sc); + if (sc->sc_cardtype & RL2_CTYPE_OEM) { + /* XXX Needs reset? */ + } + ret = 2; + } + else if (ret == 1) { + /* Timeout. */ + rl2_status_tx_write(sc, 0x04); + ret = -1; + } + return (ret); +} + +void +rl2_msg_tx_data(sc, buf, len, state) + struct rl2_softc * sc; + void * buf; + u_int16_t len; + struct rl2_msg_tx_state * state; +{ + char * data = (char *)buf; + + if (sc->sc_width == 16 && state->pd.p_nremain) { + /* XXX htons() needed? */ + union { + u_int8_t b[2]; + u_int16_t w; + } u; + + u.b[0] = state->pd.p_data; + if (len) { + u.b[1] = *data++; + len--; + } else + u.b[1] = '\0'; +#ifdef RL2DEBUG_REG + dprintf(" D<%02x%02x", u.b[0], u.b[1]); +#endif + rl2_data_write_2(sc, u.w); + state->pd.p_nremain = 0; + } + + if (len) { + if (sc->sc_width == 16) { + if (len >= 2) + rl2_data_write_multi_2(sc, buf, len / 2); + if (len & 1) { + state->pd.p_nremain = 1; + state->pd.p_data = data[len - 1]; + } + } else if (sc->sc_width == 8) + rl2_data_write_multi_1(sc, buf, len); +#ifdef DIAGNOSTIC + else + panic("rl2_msg_tx_data width %d", sc->sc_width); +#endif +#ifdef RL2DEBUG_REG + dprintf(" D<"); + dprinthex(data, len); +#endif + } +} + + +int +rl2_msg_tx_end(sc, state) + struct rl2_softc * sc; + struct rl2_msg_tx_state * state; +{ + int ret; + + /* Flush the tx buffer. */ + if (state->pd.p_nremain) + rl2_msg_tx_data(sc, NULL, 0, state); + +#ifdef DIAGNOSTIC + if (state->pd.p_nremain) + panic("rl2_msg_tx_end remain %d", state->pd.p_nremain); +#endif + ret = rl2_tx_end(sc); + if (sc->sc_arpcom.ac_if.if_flags & IFF_OACTIVE) { + state->w |= 0x10 | 0x80; + } + rl2_wakeup(sc, state->w); + rl2_enable(sc, state->ien); + return (ret); +} + +/* + * Transmit a command message to, and (optionally) receive a response + * message from the card. Each transmitted message has a sequence + * number, and corresponding reply messages have the same sequence + * number. We use the sequence numbers to index the mailboxes so + * that rl2softintr() can signal this routine when it has serviced + * and correctly received a response. + */ + +int +rl2_msg_txrx(sc, tx, txlen, rx, rxlen) + struct rl2_softc * sc; + void * tx; + int txlen; + void * rx; + int rxlen; +{ + struct rl2_mm_cmd * txc = (struct rl2_mm_cmd *)tx; + struct rl2_mm_cmd * rxc = (struct rl2_mm_cmd *)rx; + struct rl2_msg_tx_state state; + int ien; + int ret; + int s; + +#ifdef DIAGNOSTIC + if (rx != NULL && rxlen < sizeof *rxc) + panic("rl2_msg_txrx"); +#endif + + /* Each message has a unique sequence number. */ + s = splhigh(); + txc->cmd_seq = sc->sc_pktseq++; + if (sc->sc_pktseq > 0x7c) + sc->sc_pktseq = 0; + splx(s); + +#ifdef RL2DUMP + printf("%s: send %c%d seq %d data ", sc->sc_dev.dv_xname, + txc->cmd_letter, txc->cmd_fn, txc->cmd_seq); + RL2DUMPHEX(txc, sizeof *txc); + printf(":"); + RL2DUMPHEX((char *)tx + sizeof *txc, txlen - sizeof *txc); + printf("\n"); +#endif + + if (rx != NULL) + if (rl2_mbox_create(sc, txc->cmd_seq, rx, rxlen) < 0) + /* Mailbox collision. */ + return (-1); + + /* Start the transfer. */ + if ((ret = rl2_msg_tx_start(sc, tx, txlen, &state))) { + if (rx != NULL) + rl2_mbox_wait(sc, txc->cmd_seq, -1); + return (ret); + } + + /* Always send an even number of bytes. */ + rl2_msg_tx_data(sc, tx, (txlen + 1) & ~1, &state); + + /* End the transmission. */ + if ((ret = rl2_msg_tx_end(sc, &state))) { + /* Destroy mailbox. */ + if (rx != NULL) + rl2_mbox_wait(sc, txc->cmd_seq, -1); + return (ret); + } + + /* Don't wait for reply if there is nowhere to put it. */ + if (rx == NULL) + return (0); + + /* Enable interrupts if not already. */ + ien = rl2_enable(sc, 1); + + /* Wait for the reply message. */ + if (rl2_mbox_wait(sc, txc->cmd_seq, 2000) <= 0) { + printf("%s: lost message %c%d seq %d\n", sc->sc_dev.dv_xname, + txc->cmd_letter, txc->cmd_fn, txc->cmd_seq); + rl2_enable(sc, ien); + return (-1); + } + rl2_enable(sc, ien); + +#ifdef RL2DUMP + printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname, + rxc->cmd_letter, rxc->cmd_fn, rxc->cmd_seq); + RL2DUMPHEX(rxc, sizeof *rxc); + printf(":"); + RL2DUMPHEX(((char *)rx) + sizeof *rxc, rxlen - sizeof *rxc); + printf("\n"); +#endif + + /* Check for errors in the received message. */ + if (rxc->cmd_error & 0x80) { + printf("%s: command error 0x%02x command %c%d\n", + sc->sc_dev.dv_xname, + rxc->cmd_error & 0x7f, + rxc->cmd_letter, rxc->cmd_fn); + return (-1); + } + + return (0); +} + +/* + * Mailboxes provide a simple way to tell the interrupt + * service routine that someone is expecting a reply message. + * Mailboxes are identified by the message sequence number + * and also hold a pointer to storage supplied by the waiter. + */ + +/* Create a mailbox for filling. */ +int +rl2_mbox_create(sc, seq, buf, len) + struct rl2_softc * sc; + u_int8_t seq; + void * buf; + size_t len; +{ + int s; + struct rl2_mbox * mb = &sc->sc_mbox[seq]; + + dprintf(" <create %d", seq); + +#ifdef DIAGNOSTIC + if (seq > RL2_NMBOX) + panic("mbox create"); +#endif + + s = splhigh(); + if (mb->mb_state != RL2MBOX_VOID) { +#ifdef DIAGNOSTIC + printf("mbox collision"); +#endif + splx(s); + return (-1); + } + mb->mb_buf = buf; + mb->mb_len = len; + mb->mb_actlen = 0; + mb->mb_state = RL2MBOX_EMPTY; + dprintf(" empty>"); + splx(s); + return (0); +} + + +/* Wait for a mailbox to be filled. */ +int +rl2_mbox_wait(sc, seq, timeo) + struct rl2_softc * sc; + u_int8_t seq; + int timeo; +{ + int i; + int s; + int ret; + volatile struct rl2_mbox * mb = &sc->sc_mbox[seq]; + extern int cold; + + dprintf(" <wait %d", seq); + +#ifdef DIAGNOSTIC + if (seq > RL2_NMBOX) + panic("mbox wait"); +#endif + if (cold) { + /* Autoconfiguration - spin at spl0. */ + s = spl0(); + i = 0; + while (mb->mb_state == RL2MBOX_EMPTY && i < timeo) { + DELAY(hz); /* 1 tick. */ + i++; + } + if (i) + dprintf(" %dms", i); + while (mb->mb_state == RL2MBOX_FILLING) + ; + splx(s); + } else { + tsleep((void *)mb, PRIBIO, "rl2mbox", timeo); + if (mb->mb_state == RL2MBOX_FILLING) + /* XXX Could race. */ + tsleep((void *)mb, PRIBIO, "rl2mbox", 0); + } + + s = splhigh(); +#ifdef DIAGNOSTIC + if (mb->mb_state != RL2MBOX_EMPTY && mb->mb_state != RL2MBOX_FILLED) + panic("mbox wait %d", mb->mb_state); +#endif + ret = mb->mb_actlen; + mb->mb_state = RL2MBOX_VOID; + dprintf(" void>=%d", ret); + splx(s); + return (ret); +} + +/* Lock a mailbox for filling. */ +int +rl2_mbox_lock(sc, seq, bufp, lenp) + struct rl2_softc * sc; + u_int8_t seq; + void ** bufp; + size_t * lenp; +{ + int s; + struct rl2_mbox * mb = &sc->sc_mbox[seq]; + + dprintf(" <lock %d", seq); + + s = splhigh(); +#ifdef DIAGNOSTIC + if (seq > RL2_NMBOX) + panic("mbox lock"); +#endif + if (mb->mb_state != RL2MBOX_EMPTY) { + splx(s); + dprintf(" ?>"); + return (-1); + } + + mb->mb_state = RL2MBOX_FILLING; + dprintf(" filling>"); + *bufp = mb->mb_buf; + *lenp = mb->mb_len; + + splx(s); + return (0); +} + +/* Unlock a mailbox and inform the waiter of the actual number of bytes. */ +void +rl2_mbox_unlock(sc, seq, actlen) + struct rl2_softc * sc; + u_int8_t seq; + size_t actlen; +{ + int s; + struct rl2_mbox * mb = &sc->sc_mbox[seq]; + + dprintf(" <unlock %d", seq); + + s = splhigh(); +#ifdef DIAGNOSTIC + if (seq > RL2_NMBOX) + panic("mbox unlock seq"); + if (mb->mb_state != RL2MBOX_FILLING) + panic("mbox unlock"); +#endif + mb->mb_state = RL2MBOX_FILLED; + dprintf(" filled>"); + mb->mb_actlen = actlen; + wakeup(mb); + splx(s); +} + diff --git a/sys/dev/ic/rl2var.h b/sys/dev/ic/rl2var.h new file mode 100644 index 00000000000..92143c1af9c --- /dev/null +++ b/sys/dev/ic/rl2var.h @@ -0,0 +1,135 @@ +/* $OpenBSD: rl2var.h,v 1.1 1999/06/21 23:21:47 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public domain. + * + * Proxim RangeLAN2 soft state copy. + */ + +/* + * Mailboxes are used to communicate card-initiated messages + * from the interrupt handler to other kernel threads. + */ +struct rl2_mbox { + void * mb_buf; /* caller's buffer */ + size_t mb_len; /* buffer size */ + size_t mb_actlen; /* actual message size */ + volatile u_int8_t mb_state; /* mailbox state */ +#define RL2MBOX_VOID 0 +#define RL2MBOX_EMPTY 1 +#define RL2MBOX_FILLING 2 +#define RL2MBOX_FILLED 3 +}; + +#define RL2_NMBOX 0x7c /* = maximum sequence number */ + +/* Soft state */ +struct rl2_softc { + struct device sc_dev; + void *sc_ih; /* interrupt handler */ + struct arpcom sc_arpcom; /* Ethernet common part */ + bus_space_tag_t sc_iot; /* bus cookie */ + bus_space_handle_t sc_ioh; /* bus i/o handle */ + + u_int8_t sc_width; /* bus transfer width */ + u_int8_t sc_irq; /* irq for card */ + + u_int16_t sc_cardtype; /* set from the 'flags' directive */ +#define RL2_CTYPE_OEM 0x01 +#define RL2_CTYPE_UISA 0x02 +#define RL2_CTYPE_ONE_PIECE 0x04 + + u_int8_t sc_intsel; /* copy of INTSEL */ + u_int8_t sc_status; /* copy of STATUS */ + u_int8_t sc_control; /* copy of CONTROL */ +#ifdef RL2DEBUG_REG + u_int8_t dbg_oreg[8]; /* last value written to registers */ +#endif + + u_int8_t sc_pktseq; /* card message seq no */ + u_int8_t sc_txseq; /* tx packet seq no */ + + u_int16_t sc_state; +#define RL2_STATE_SYNC 0x0001 /* card is synchronised */ + + struct rl2_mbox sc_mbox[0x80]; /* per-message mailboxes */ + struct rl2_param sc_param; /* user-configurable parameters */ + u_int8_t sc_promisc; /* receive all packets */ +}; + +/* Structure used to hold partial read state for rl2_rx_pdata() */ +struct rl2_pdata { + u_int8_t p_data; /* extra bytes read but not consumed */ + int p_nremain; /* number of bytes not consumed */ +}; +#define RL2_PDATA_INIT {0,0} + +/* Structure used to hold partial transmit state for rl2_msg_tx_*() */ +struct rl2_msg_tx_state { + int ien; /* saved interrupt state */ + u_int8_t w; /* saved wakup state */ + struct rl2_pdata pd; /* saved partial write state */ +}; + +struct rl2_mm_cmd; /* fwd decl */ + +#define RL2_WAKEUP_SET 0xff +#define RL2_WAKEUP_NOCHANGE (0x80|0x10) + +void rl2config __P((struct rl2_softc *)); +int rl2intr __P((void *)); +void rl2read __P((struct rl2_softc *, struct rl2_mm_cmd *, int)); +int rl2_enable __P((struct rl2_softc *, int)); +int rl2_reset __P((struct rl2_softc *)); +u_int8_t rl2_wakeup __P((struct rl2_softc *, u_int8_t)); +int rl2_rx_request __P((struct rl2_softc *, int)); +int rl2_rx_data __P((struct rl2_softc *, void *, int)); +void rl2_rx_pdata __P((struct rl2_softc *, void *, int, + struct rl2_pdata *)); +void rl2_rx_end __P((struct rl2_softc *)); +void rl2_clear_nak __P((struct rl2_softc *)); +void rl2_msg_tx_data __P((struct rl2_softc *, void *, u_int16_t, + struct rl2_msg_tx_state *)); +int rl2_msg_tx_start __P((struct rl2_softc *, void *, int, + struct rl2_msg_tx_state *)); +int rl2_msg_tx_end __P((struct rl2_softc *, + struct rl2_msg_tx_state *)); +int rl2_msg_txrx __P((struct rl2_softc *, void *, int, + void *, int)); + +int rl2_mbox_create __P((struct rl2_softc *, u_int8_t, void *, + size_t)); +int rl2_mbox_wait __P((struct rl2_softc *, u_int8_t, int)); +int rl2_mbox_lock __P((struct rl2_softc *, u_int8_t, void **, + size_t*)); +void rl2_mbox_unlock __P((struct rl2_softc *, u_int8_t, size_t)); + +/* debug all card operations */ +#ifdef RL2DEBUG +#define dprintf(fmt, args...) printf(fmt , ## args) + /* log(LOG_DEBUG, fmt , ## args) */ +#define dprinthex(buf, len) do { \ + unsigned char *_b = (unsigned char*)(buf); \ + int _i, _l=(len); \ + printf("{"); \ + for(_i = 0; _i < _l; _i++) { \ + printf("%02x", _b[_i]); \ + if (_i % 4 == 3 && _i != _l - 1) \ + printf(","); \ + } \ + printf("}"); \ +} while (0) +#else +#define dprintf(fmt, args...) /* nothing */ +#define dprinthex(buf, len) /* nothing */ +#endif + +/* debug messages to/from card. prints 4-byte groups separated by commas */ +#define RL2DUMP +#define RL2DUMPHEX(buf, buflen) do { \ + int _i; \ + for (_i = 0; _i < (buflen); _i++) { \ + printf("%02x", ((unsigned char *)(buf))[_i]); \ + if (_i != (buflen) - 1 && _i % 4 == 3) \ + printf(","); \ + } \ +} while (0) diff --git a/sys/dev/pcmcia/files.pcmcia b/sys/dev/pcmcia/files.pcmcia index 2e0040fb2e4..5d75d427de6 100644 --- a/sys/dev/pcmcia/files.pcmcia +++ b/sys/dev/pcmcia/files.pcmcia @@ -1,4 +1,4 @@ -# $OpenBSD: files.pcmcia,v 1.16 1999/05/18 19:24:05 niklas Exp $ +# $OpenBSD: files.pcmcia,v 1.17 1999/06/21 23:21:45 d Exp $ # $NetBSD: files.pcmcia,v 1.9 1998/06/21 18:45:41 christos Exp $ # # Config.new file and device description for machine-independent PCMCIA code. @@ -56,6 +56,11 @@ file dev/pcmcia/if_xe.c xe_pcmcia #attach fdc at pcmcia with fdc_pcmcia #file dev/pcmcia/fdc_pcmcia.c fdc_pcmcia +# Proxim RangeLAN2 PC-Card +define rln +attach rln at pcmcia with rln_pcmcia : rln +file dev/pcmcia/if_rl2_pcmcia.c rln_pcmcia + # PCMCIA multi-port serial cards #device pcmcom {[slave = -1]} #attach pcmcom at pcmcia diff --git a/sys/dev/pcmcia/if_rl2_pcmcia.c b/sys/dev/pcmcia/if_rl2_pcmcia.c new file mode 100644 index 00000000000..0950e304cea --- /dev/null +++ b/sys/dev/pcmcia/if_rl2_pcmcia.c @@ -0,0 +1,288 @@ +/* $OpenBSD: if_rl2_pcmcia.c,v 1.1 1999/06/21 23:21:45 d Exp $ */ +/* + * David Leonard <d@openbsd.org>, 1999. Public domain. + * + * Proxim RangeLAN2 PC-Card and compatibles + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/device.h> +#include <sys/queue.h> + +#include <net/if.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/rl2.h> +#include <dev/ic/rl2var.h> +#include <dev/ic/rl2reg.h> + +#include <dev/pcmcia/pcmciareg.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciadevs.h> + +struct rl2_pcmcia_softc { + struct rl2_softc sc_rl2; /* real "rl2" softc */ + + struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o information */ + int sc_io_window; /* i/o window for the card */ + struct pcmcia_function *sc_pf; /* our PCMCIA function */ + void *sc_ih; /* our interrupt handle */ +}; + +static int rl2_pcmcia_match __P((struct device *, void *, void *)); +static struct rl2_pcmcia_product * rl2_pcmcia_product_lookup __P(( + struct pcmcia_attach_args *)); +static void rl2_pcmcia_attach __P((struct device *, struct device *, void *)); +static int rl2intr_pcmcia __P((void *arg)); + +#ifdef notyet +static int rl2_pcmcia_enable __P((struct rl2_softc *)); +static void rl2_pcmcia_disable __P((struct rl2_softc *)); +#endif + +struct cfattach rln_pcmcia_ca = { + sizeof(struct rl2_pcmcia_softc), rl2_pcmcia_match, rl2_pcmcia_attach +}; + +/* Apparently this is the "7200 PC card"? */ +#define PCMCIA_CIS_RANGELAN2 { "PROXIM", "LAN CARD", "RANGELAN2", NULL } + +static struct rl2_pcmcia_product { + u_int32_t manufacturer; + u_int32_t product; + const char *name; + u_int8_t flags; +} rl2_pcmcia_products[] = { + { 0x0126, /* Digital */ + 0x1058, /* RoamAbout 2400 FH */ + "Digital RoamAbout 2400 FH", + 0 }, + { 0x8a01, /* AMP */ + 0x0066, /* Wireless */ + "AMP Wireless", + 0 }, + { 0, + 0, + "unknown RangeLAN2 wireless network card", + 0 }, +}; + +/* Match the product and manufacturer codes with known card types */ +static struct rl2_pcmcia_product * +rl2_pcmcia_product_lookup(pa) + struct pcmcia_attach_args *pa; +{ + struct rl2_pcmcia_product *rpp; + + for (rpp = rl2_pcmcia_products; rpp->manufacturer && rpp->product; + rpp++) + if (pa->manufacturer == rpp->manufacturer && + pa->product == rpp->product) + break; + return (rpp); +} + +/* Match card CIS info string with RangeLAN2 cards */ +static int +rl2_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct pcmcia_attach_args *pa = aux; + const char *cis1_info[4] = PCMCIA_CIS_RANGELAN2; + int i; + + for (i = 0; i < 4; i++) + if (cis1_info[i] && + strcmp(cis1_info[i], pa->card->cis1_info[i]) != 0) + return (0); + + return (1); +} + +/* Attach and configure */ +static void +rl2_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct rl2_pcmcia_softc *psc = (void *) self; + struct rl2_softc *sc = &psc->sc_rl2; + struct pcmcia_attach_args *pa = aux; + struct pcmcia_config_entry *cfe; + struct rl2_pcmcia_product *rpp; + +#ifdef RL2DEBUG + /* Allowed i/o base addresses from the RoamAbout owner's manual */ + int i; + static bus_addr_t iobases[] = { + 0x270, /* useful in user-space debugging */ + 0x100, 0x120, 0x140, 0x218, 0x270, 0x280, 0x290, 0x298, + 0x2a0, 0x2a8, 0x2e0, 0x300, 0x310, 0x358, 0x360, 0x368, + 0 + }; +#endif + + psc->sc_pf = pa->pf; + cfe = psc->sc_pf->cfe_head.sqh_first; + + /* Guess the transfer width we will be using */ + if (cfe->flags & PCMCIA_CFE_IO16) + sc->sc_width = 16; + else if (cfe->flags & PCMCIA_CFE_IO8) + sc->sc_width = 8; + else + sc->sc_width = 0; + +#ifdef DIAGNOSTIC + /* We only expect one i/o region and no memory region */ + if (cfe->num_memspace != 0) + printf(": unexpected number of memory spaces (%d)\n", + cfe->num_memspace); + if (cfe->num_iospace != 1) + printf(": unexpected number of i/o spaces (%d)\n", + cfe->num_iospace); + else if (cfe->iospace[0].length != RL2_NPORTS) + printf(": unexpected size of i/o space (0x%x)\n", + cfe->iospace[0].length); + if (sc->sc_width == 0) + printf(": unknown bus width\n"); +#endif /* DIAGNOSTIC */ + + pcmcia_function_init(psc->sc_pf, cfe); + + /* Allocate i/o space */ +#ifdef RL2DEBUG + /* Try only those ports from the manual */ + for (i=0; iobases[i] != 0; i++) + if (pcmcia_io_alloc(psc->sc_pf, iobases[i], RL2_NPORTS, + RL2_NPORTS, &psc->sc_pcioh) == 0) + break; + if (iobases[i] == 0) { +#else + if (pcmcia_io_alloc(psc->sc_pf, 0, RL2_NPORTS, + RL2_NPORTS, &psc->sc_pcioh)) { +#endif + printf(": can't alloc i/o space\n"); + return; + } + + sc->sc_iot = psc->sc_pcioh.iot; + sc->sc_ioh = psc->sc_pcioh.ioh; + + /* Map i/o space */ + if (pcmcia_io_map(psc->sc_pf, ((sc->sc_width == 8) ? PCMCIA_WIDTH_IO8 : + (sc->sc_width == 16) ? PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_AUTO), + 0, RL2_NPORTS, &psc->sc_pcioh, &psc->sc_io_window)) { + printf(": can't map i/o space\n"); + return; + } + + /* Enable the card */ + if (pcmcia_function_enable(psc->sc_pf)) { + printf(": function enable failed\n"); + return; + } + +#ifdef notyet + sc->enable = rl2_pcmcia_enable; + sc->disable = rl2_pcmcia_disable; +#endif + + rpp = rl2_pcmcia_product_lookup(pa); + + /* Check if the device has a separate antenna module */ + sc->sc_cardtype = 0; + switch (psc->sc_pf->ccr_base) { + case 0x0100: + sc->sc_cardtype |= RL2_CTYPE_ONE_PIECE; + break; + case 0x0800: + sc->sc_cardtype &= ~RL2_CTYPE_ONE_PIECE; + break; +#ifdef DIAGNOSTIC + default: + printf("\n%s: cannot tell if one or two piece (ccr addr %x)\n", + sc->sc_dev.dv_xname, psc->sc_pf->ccr_base); +#endif + } + + /* The PC-card needs to be told to use 'irq' 15 */ + sc->sc_irq = 15; + + /* + * We need to get an interrupt before configuring, since + * polling registers (the alternative) to reading card + * responses, causes hard lock-ups. + */ + printf("\n"); + sc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, + rl2intr_pcmcia, sc); + if (sc->sc_ih == NULL) + printf("%s: couldn't establish interrupt\n", + sc->sc_dev.dv_xname); + + printf("%s: %s", sc->sc_dev.dv_xname, rpp->name); + rl2config(sc); + printf("\n"); +} + +/* Interrupt handler */ +static int +rl2intr_pcmcia(arg) + void *arg; +{ + struct rl2_softc *sc = (struct rl2_softc *)arg; + struct rl2_pcmcia_softc *psc = (struct rl2_pcmcia_softc *)sc; + int opt; + int ret; + + /* Need to immediately read/write the option register for PC-card */ + opt = pcmcia_ccr_read(psc->sc_pf, PCMCIA_CCR_OPTION); + pcmcia_ccr_write(psc->sc_pf, PCMCIA_CCR_OPTION, opt); + + /* Call actual interrupt handler */ + ret = rl2intr(arg); + + return (ret); +} + +#ifdef notyet +static int +rl2_pcmcia_enable(sc) + struct rl2_softc *sc; +{ + struct rl2_pcmcia_softc *psc = (struct rl2_pcmcia_softc *) sc; + struct pcmcia_function *pf = psc->sc_pf; + + /* Establish the interrupt */ + sc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, + rl2intr_pcmcia, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt\n", + sc->sc_dev.dv_xname); + return (1); + } + + return (pcmcia_function_enable(pf)); +} + +static void +rl2_pcmcia_disable(sc) + struct rl2_softc *sc; +{ + struct rl2_pcmcia_softc *psc = (struct rl2_pcmcia_softc *) sc; + + pcmcia_function_disable(psc->sc_pf); + pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih); +} +#endif |