summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Leonard <d@cvs.openbsd.org>1999-06-21 23:21:48 +0000
committerDavid Leonard <d@cvs.openbsd.org>1999-06-21 23:21:48 +0000
commit88db842a291c43de6f4c89d019c1379faa1b9913 (patch)
treed78266526ddb764ace7aa9c946371ee1a220bb4f
parenta42c0e1f01ffd4afe90afadcbfd92ea5b6d079bf (diff)
RangeLAN2 wireless network card
-rw-r--r--sys/conf/files7
-rw-r--r--sys/dev/ic/rl2.c1115
-rw-r--r--sys/dev/ic/rl2.h31
-rw-r--r--sys/dev/ic/rl2cmd.h269
-rw-r--r--sys/dev/ic/rl2reg.h266
-rw-r--r--sys/dev/ic/rl2subr.c912
-rw-r--r--sys/dev/ic/rl2var.h135
-rw-r--r--sys/dev/pcmcia/files.pcmcia7
-rw-r--r--sys/dev/pcmcia/if_rl2_pcmcia.c288
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 = &top;
+
+ 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*)&param + 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, &param, 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