File: [local] / sys / dev / ic / rln.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:10:54 2008 UTC (16 years, 6 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: rln.c,v 1.18 2006/03/25 22:41:43 djm 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>
#endif
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ic/rln.h>
#include <dev/ic/rlnvar.h>
#include <dev/ic/rlnreg.h>
#include <dev/ic/rlncmd.h>
/* Autoconfig definition of driver back-end. */
struct cfdriver rln_cd = {
NULL, "rln", DV_IFNET
};
void rlninit(struct rln_softc *);
void rlnstart(struct ifnet*);
void rlnwatchdog(struct ifnet*);
int rlnioctl(struct ifnet *, u_long, caddr_t);
void rlnstop(struct rln_softc *);
/* Interrupt handler. */
void rlnsoftintr(void *);
/* Packet I/O. */
int rln_transmit(struct rln_softc *, struct mbuf *,
int, int);
struct mbuf * rlnget(struct rln_softc *, struct rln_mm_cmd *,
int);
/* Card protocol-level functions. */
int rln_getenaddr(struct rln_softc *, u_int8_t *);
int rln_getpromvers(struct rln_softc *, char *, int);
int rln_sendinit(struct rln_softc *);
#if notyet
int rln_roamconfig(struct rln_softc *);
int rln_roam(struct rln_softc *);
int rln_multicast(struct rln_softc *, int);
int rln_searchsync(struct rln_softc *);
int rln_iosetparam(struct rln_softc *, struct rln_param *);
int rln_lockprom(struct rln_softc *);
int rln_ito(struct rln_softc *);
int rln_standby(struct rln_softc *);
#endif
/* Back-end attach and configure. Assumes card has been reset. */
void
rlnconfig(sc)
struct rln_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; /* rln_newseq() */
sc->sc_txseq = 0;
sc->sc_state = 0;
/* Initialise user-configurable params. */
sc->sc_param.rp_roam_config = RLN_ROAM_NORMAL;
sc->sc_param.rp_security = RLN_SECURITY_DEFAULT;
sc->sc_param.rp_station_type = RLN_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);
/* Initialise the message mailboxes. */
for (i = 0; i < RLN_NMBOX; i++)
sc->sc_mbox[i].mb_state = RLNMBOX_VOID;
/* Probe for some properties. */
printf(", %s-piece",
(sc->sc_cardtype & RLN_CTYPE_ONE_PIECE) ? "one" : "two");
if (sc->sc_cardtype & RLN_CTYPE_OEM)
printf(" oem");
if (sc->sc_cardtype & RLN_CTYPE_UISA)
printf(" micro-isa");
/* Read the card's PROM revision. */
if (rln_getpromvers(sc, promvers, sizeof promvers)) {
printf(": could not read PROM version\n");
return;
}
printf(", fw %.7s", promvers);
/* Fetch the card's MAC address. */
if (rln_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));
timeout_set(&sc->sc_timeout, rlnsoftintr, sc);
/* Attach as a network interface. */
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_start = rlnstart;
ifp->if_ioctl = rlnioctl;
ifp->if_watchdog = rlnwatchdog;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
IFQ_SET_READY(&ifp->if_snd);
if_attach(ifp);
ether_ifattach(ifp);
}
/* Bring device up. */
void
rlninit(sc)
struct rln_softc * sc;
{
/* LLDInit() */
struct ifnet * ifp = &sc->sc_arpcom.ac_if;
int s;
s = splnet();
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 (rln_reset(sc)) {
printf("%s: could not reset card\n", sc->sc_dev.dv_xname);
goto fail;
}
sc->sc_state = 0; /* Also clears RLN_STATE_NEEDINIT. */
/* Use this host's name as a master name. */
if (!cold && sc->sc_param.rp_master[0] == '\0') {
bcopy(hostname, sc->sc_param.rp_master,
min(hostnamelen, sizeof sc->sc_param.rp_master));
}
rln_enable(sc, 1);
/* Initialise operational params. */
if (rln_sendinit(sc)) {
printf("%s: could not set card parameters\n",
sc->sc_dev.dv_xname);
goto fail;
}
#if 0
rln_roamconfig(sc);
/* rln_lockprom(sc); */
/* SendSetITO() */
rln_multicast(sc, 1);
rln_roam(sc);
/* Synchronise with something. */
rln_searchsync(sc);
#endif
ifp->if_flags |= IFF_RUNNING;
rlnstart(ifp);
splx(s);
return;
fail:
ifp->if_flags &= ~IFF_UP;
splx(s);
return;
}
/* Start outputting on interface. This is always called at splnet(). */
void
rlnstart(ifp)
struct ifnet * ifp;
{
struct rln_softc * sc = (struct rln_softc *)ifp->if_softc;
struct mbuf * m0;
int len, pad, ret, s;
dprintf(" start[");
if (sc->sc_state & RLN_STATE_NEEDINIT)
rlninit(sc);
/* 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 & RLN_STATE_SYNC) == 0) {
dprintf(" nosync]");
return;
}
rln_enable(sc, 1);
startagain:
s = splnet();
IFQ_DEQUEUE(&ifp->if_snd, m0);
splx(s);
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, BPF_DIRECTION_OUT);
#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 octets. */
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 = rln_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);
rln_need_reset(sc);
return;
}
/* Transmit one packet. */
int
rln_transmit(sc, m0, len, pad)
struct rln_softc * sc;
struct mbuf * m0;
int len;
int pad;
{
struct mbuf * m;
int zfirst;
int actlen;
int tlen = len + pad;
struct rln_msg_tx_state state;
static u_int8_t zeroes[60];
struct rln_mm_sendpacket cmd = { RLN_MM_SENDPACKET };
/* Does the packet start with a zero bit? */
zfirst = ((*mtod(m0, u_int8_t *) & 1) == 0);
cmd.mode =
RLN_MM_SENDPACKET_MODE_BIT7 |
(zfirst ? RLN_MM_SENDPACKET_MODE_ZFIRST : 0) |
(0 ? RLN_MM_SENDPACKET_MODE_QFSK : 0), /* sc->qfsk? */
cmd.power = 0x70; /* 0x70 or 0xf0 */
cmd.length_lo = htons(4 + tlen) & 0xff;
cmd.length_hi = (htons(4 + tlen) >> 8) & 0xff;
cmd.xxx1 = 0;
cmd.xxx2 = 0;
cmd.xxx3 = 0;
/* A unique packet-level sequence number, independent of sc_seq. */
cmd.sequence = sc->sc_txseq;
sc->sc_txseq++;
if (sc->sc_txseq > RLN_MAXSEQ)
sc->sc_txseq = 0;
dprintf(" T[%d+%d", len, pad);
if (rln_msg_tx_start(sc, &cmd, sizeof cmd + tlen, &state))
goto error;
cmd.mm_cmd.cmd_seq = rln_newseq(sc);
/* Send the SENDPACKET command header */
#ifdef RLNDUMP
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);
RLNDUMPHEX(&cmd, sizeof cmd);
printf(":");
#endif
rln_msg_tx_data(sc, &cmd, sizeof cmd, &state);
/* XXX do we need to insert a hardware header here??? */
/* Follow the header immediately with the packet payload */
actlen = 0;
for (m = m0; m; m = m->m_next) {
if (m->m_len) {
#ifdef RLNDUMP
RLNDUMPHEX(mtod(m, void *), m->m_len);
#endif
rln_msg_tx_data(sc, mtod(m, void *), m->m_len, &state);
}
if (m->m_next)
printf("|");
actlen += m->m_len;
}
#ifdef DIAGNOSTIC
if (actlen != len)
panic("rln_transmit: len %d != %d", actlen, len);
if (pad > sizeof zeroes)
panic("rln_transmit: pad %d > %d", pad, sizeof zeroes);
#endif
if (pad) {
#ifdef RLNDUMP
printf(":");
RLNDUMPHEX(zeroes, pad);
#endif
rln_msg_tx_data(sc, zeroes, pad, &state);
}
#ifdef RLNDUMP
printf("\n");
#endif
if (rln_msg_tx_end(sc, &state))
goto error;
return (0);
error:
dprintf(" error]");
return (-1);
}
/* (Supposedly) called when interrupts are suspiciously absent. */
void
rlnwatchdog(ifp)
struct ifnet * ifp;
{
struct rln_softc * sc = (struct rln_softc *)ifp->if_softc;
log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
++sc->sc_arpcom.ac_if.if_oerrors;
rlninit(sc);
rln_enable(sc, 1);
}
/* Handle single card interrupt. */
int
rlnintr(arg)
void * arg;
{
struct rln_softc * sc = (struct rln_softc *)arg;
dprintf("!");
/* Tell card not to interrupt any more. */
rln_enable(sc, 0);
if (cold)
/* During autoconfig - must handle interrupts now. */
rlnsoftintr(sc);
else
/* Handle later. */
timeout_add(&sc->sc_timeout, 1);
return (1);
}
/* Process earlier card interrupt at splsoftnet. */
void
rlnsoftintr(arg)
void * arg;
{
struct rln_softc *sc = (struct rln_softc *)arg;
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
int len;
u_int8_t w;
struct rln_mm_cmd hdr;
int s;
s = splsoftnet();
dprintf(" si(");
again:
/* Save wakeup state. */
w = rln_wakeup(sc, RLN_WAKEUP_SET);
if ((len = rln_rx_request(sc, 300)) < 0) {
/* Error in transfer. */
rln_need_reset(sc);
rln_rx_end(sc);
} else if (len < sizeof hdr) {
/* Short message. */
rln_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. */
rln_rx_data(sc, &hdr, sizeof hdr);
rlnread(sc, &hdr, len);
}
/* Ensure that wakeup state is unchanged if transmitting. */
if (ifp->if_flags & IFF_OACTIVE)
w |= RLN_WAKEUP_NOCHANGE;
rln_wakeup(sc, w);
/* Check for more interrupts. */
if ((sc->sc_state & RLN_STATE_NEEDINIT) == 0 &&
rln_status_rx_ready(sc)) {
if (rln_status_rx_read(sc) == RLN_STATUS_RX_ERROR) {
#ifdef DIAGNOSTIC
printf("%s: protocol error\n", sc->sc_dev.dv_xname);
#endif
DELAY(100 * 1000); /* Woah, baby. */
rln_clear_nak(sc);
} else {
#ifdef DIAGNOSTIC
printf("%s: intr piggyback\n", sc->sc_dev.dv_xname);
#endif
goto again;
}
}
rln_eoi(sc);
rln_enable(sc, 1);
dprintf(")");
splx(s);
}
/* Read and process a message from the card. */
void
rlnread(sc, hdr, len)
struct rln_softc *sc;
struct rln_mm_cmd *hdr;
int len;
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct mbuf *m;
u_int8_t data[1538];
u_int8_t *buf;
size_t buflen;
struct rln_pdata pd = RLN_PDATA_INIT;
struct rln_mm_synchronised * syncp = (struct rln_mm_synchronised *)data;
int s;
dprintf(" [read]");
/* Were we waiting for this message? */
if (rln_mbox_lock(sc, hdr->cmd_seq, (void **)&buf, &buflen) == 0) {
#ifdef DIAGNOSTIC
if (buflen < sizeof *hdr)
panic("rlnread buflen");
#endif
bcopy(hdr, buf, sizeof *hdr);
buf += sizeof *hdr;
len -= sizeof *hdr;
buflen -= sizeof *hdr;
if (len) {
if (len == buflen) /* Expected size */
rln_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
rln_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
rln_rx_pdata(sc, buf, buflen, &pd);
/* Drain the rest somewhere. */
rln_rx_pdata(sc, data, len - buflen, &pd);
}
}
rln_rx_end(sc);
/* This message can now be handled by the waiter. */
rln_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: msg too big (%d)\n", sc->sc_dev.dv_xname, len);
ifp->if_ierrors++;
rln_rx_end(sc);
/* rln_need_reset(sc); */
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 & ~0x80,
hdr->cmd_letter, hdr->cmd_fn,
len);
ifp->if_ierrors++;
rln_rx_end(sc);
rln_need_reset(sc);
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 = rlnget(sc, hdr, len);
rln_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
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
ether_input_mbuf(ifp, m);
return;
}
/* Otherwise we read the packet into a buffer on the stack. */
bcopy(hdr, data, sizeof *hdr);
if (len > sizeof *hdr)
rln_rx_pdata(sc, data + sizeof *hdr, len - sizeof *hdr, &pd);
rln_rx_end(sc);
#ifdef RLNDUMP
printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname,
hdr->cmd_letter, hdr->cmd_fn, hdr->cmd_seq);
RLNDUMPHEX(hdr, sizeof hdr);
printf(":");
RLNDUMPHEX(data + sizeof hdr, len - sizeof hdr);
printf("\n");
#endif
switch (RLN_MM_CMD(hdr->cmd_letter, hdr->cmd_fn)) {
case RLN_MM_CMD('b', 0): /* b0: Transmit done. */
#ifdef DIAGNOSTIC
if (len != 7)
printf("%s: 'b0' len %d != 7\n",
sc->sc_dev.dv_xname, len);
#endif
ifp->if_flags &= ~IFF_OACTIVE;
ifp->if_opackets++;
s = splnet();
rlnstart(ifp);
splx(s);
break;
case RLN_MM_CMD('a', 20): /* a20: Card fault. */
printf("%s: hardware fault\n", sc->sc_dev.dv_xname);
break;
case RLN_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. */
/*
* From http://www.proxim.com/support/faq/7400.shtml
* 3. RLNSETUP reports that I'm synchronized to my own MAC address. What
* does that mean?
* You are the acting Master for this network. Either you are
* configured as the Master or as an Alternate Master. If you are an
* Alternate Master, you may be out of range or on a different Domain
* and Security ID from the true Master.
*/
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 |= RLN_STATE_SYNC;
/* Resume sending. */
s = splnet();
rlnstart(ifp);
splx(s);
break;
case RLN_MM_CMD('a', 5): /* a4: Lost sync. */
printf("%s: lost sync\n", sc->sc_dev.dv_xname);
sc->sc_state &= ~RLN_STATE_SYNC;
break;
case RLN_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);
RLNDUMPHEX(hdr, sizeof hdr);
printf(":");
RLNDUMPHEX(data, len);
printf("}\n");
#endif
break;
}
}
/* Extract a received network packet from the card. */
struct mbuf *
rlnget(sc, hdr, totlen)
struct rln_softc *sc;
struct rln_mm_cmd *hdr;
int totlen;
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
int len;
int pad;
struct mbuf *m, **mp, *top;
struct rln_pdata pd = RLN_PDATA_INIT;
struct {
u_int8_t rssi;
u_int8_t xxx1; /* always 00? */
u_int16_t len; /* payload length */
u_int8_t xxx2; /* always 00? */
u_int8_t xxx3; /* always c0? */
u_int8_t seq;
u_int8_t xxx4;
struct ether_addr to; /* destination station addr */
struct ether_addr from; /* sending station addr */
} hwhdr;
dprintf(" [get]");
#ifdef RLNDUMP
/* Decode the command header: */
printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname,
hdr->cmd_letter, hdr->cmd_fn, hdr->cmd_seq);
RLNDUMPHEX(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
/* Decode the hardware header: */
rln_rx_pdata(sc, &hwhdr, sizeof hwhdr, &pd);
totlen -= sizeof hwhdr;
#ifdef RLNDUMP
RLNDUMPHEX(&hwhdr, sizeof hwhdr);
printf("/");
#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;
/*
* Insert some leading padding in the mbuf, so that payload data is
* aligned.
*/
pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);
m->m_data += pad;
len = MHLEN - pad;
top = 0;
mp = ⊤
while (totlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
m_freem(top);
goto drop;
}
len = MLEN;
}
if (totlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT) {
len = MCLBYTES;
if (!top) {
m->m_data += pad;
len -= pad;
}
}
}
len = min(totlen, len);
rln_rx_pdata(sc, mtod(m, u_int8_t *), len, &pd);
#ifdef RLNDUMP
RLNDUMPHEX(mtod(m, u_int8_t *), len);
if (totlen != len)
printf("|");
#endif
m->m_len = len;
totlen -= len;
*mp = m;
mp = &m->m_next;
}
#ifdef RLNDUMP
printf("\n");
#endif
return top;
drop:
#ifdef RLNDUMP
printf(": drop\n");
#endif
ifp->if_iqdrops++;
return NULL;
}
/* Interface control. */
int
rlnioctl(ifp, cmd, data)
struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
struct rln_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
int s, error;
int need_init;
printf("%s: ioctl cmd[%c/%d] data=%x\n", sc->sc_dev.dv_xname,
IOCGROUP(cmd), IOCBASECMD(cmd), data);
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:
rlninit(sc);
arp_ifinit(&sc->sc_arpcom, ifa);
break;
#endif
default:
rlninit(sc);
break;
}
break;
case SIOCSIFFLAGS:
need_init = 0;
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
/* Was running, want down: stop. */
rlnstop(sc);
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/* Was not running, want up: start. */
need_init = 1;
}
if (ifp->if_flags & IFF_RUNNING) {
if ((ifp->if_flags & IFF_PROMISC) &&
(sc->sc_state & RLN_STATE_PROMISC) == 0) {
sc->sc_state |= RLN_STATE_PROMISC;
need_init = 1;
}
else if ((ifp->if_flags & IFF_PROMISC) == 0 &&
(sc->sc_state & RLN_STATE_PROMISC)) {
sc->sc_state &= ~RLN_STATE_PROMISC;
need_init = 1;
}
}
if (need_init)
rlninit(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = EOPNOTSUPP;
break;
#if notyet
case RLNIOSPARAM:
error = rln_iosetparam(sc, (struct rln_param *)&data);
break;
case RLNIOGPARAM:
bcopy(&sc->sc_param, (struct rln_param *)&data,
sizeof sc->sc_param);
break;
#endif
default:
error = EINVAL;
break;
}
splx(s);
return (error);
}
/* Stop output from the card. */
void
rlnstop(sc)
struct rln_softc *sc;
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
dprintf(" [stop]");
ifp->if_flags &= ~IFF_RUNNING;
rln_enable(sc, 0);
}
/* Get MAC address from card. */
int
rln_getenaddr(sc, enaddr)
struct rln_softc *sc;
u_int8_t * enaddr;
{
struct rln_mm_cmd query = RLN_MM_GETENADDR;
struct rln_mm_gotenaddr response = { RLN_MM_GETENADDR };
if (rln_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. */
int
rln_getpromvers(sc, ver, verlen)
struct rln_softc *sc;
char *ver;
int verlen;
{
struct rln_mm_cmd query = RLN_MM_GETPROMVERSION;
struct rln_mm_gotpromversion response = { RLN_MM_GOTPROMVERSION };
int i;
#ifdef DIAGNOSTIC
if (verlen != sizeof response.version)
panic("rln_getpromvers");
#endif
if (rln_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. */
int
rln_sendinit(sc)
struct rln_softc *sc;
{
struct rln_mm_init init = { RLN_MM_INIT };
struct rln_mm_initted iresponse;
#if 0
struct rln_mm_setmagic magic = { RLN_MM_SETMAGIC };
struct rln_mm_disablehopping hop = { RLN_MM_DISABLEHOPPING };
struct rln_mm_cmd response;
#endif
bzero((char *)&init + sizeof init.mm_cmd,
sizeof init - sizeof init.mm_cmd);
dprintf(" [setting parameters]");
init.opmode = (sc->sc_state & RLN_STATE_PROMISC ?
RLN_MM_INIT_OPMODE_PROMISC : RLN_MM_INIT_OPMODE_NORMAL);
init.stationtype = sc->sc_param.rp_station_type;
/* Spread-spectrum frequency hopping. */
init.hop_period = 1;
init.bfreq = 2;
init.sfreq = 7;
/* Choose channel. */
init.channel = sc->sc_param.rp_channel;
init.subchannel = sc->sc_param.rp_subchannel;
init.domain = sc->sc_param.rp_domain;
/* Name of this station when acting as master. */
bcopy(sc->sc_param.rp_master, init.mastername, sizeof init.mastername);
/* Security params. */
init.sec1 = (sc->sc_param.rp_security & 0x0000ff) >> 0;
init.sec2 = (sc->sc_param.rp_security & 0x00ff00) >> 8;
init.sec3 = (sc->sc_param.rp_security & 0xff0000) >> 16;
init.sync_to = 1;
bzero(init.syncname, sizeof init.syncname);
if (rln_msg_txrx(sc, &init, sizeof init,
&iresponse, sizeof iresponse))
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 (rln_msg_txrx(sc, &magic, sizeof magic,
&response, sizeof response))
return (-1);
dprintf(" [disabling freq hopping]");
hop.hopflag = RLN_MM_DISABLEHOPPING_HOPFLAG_DISABLE;
if (rln_msg_txrx(sc, &hop, sizeof hop,
&response, sizeof response))
return (-1);
#endif
return (0);
}
#if notyet
/* Configure the way the card leaves a basestation. */
int
rln_roamconfig(sc)
struct rln_softc *sc;
{
struct rln_mm_setroaming roam = { RLN_MM_SETROAMING };
struct rln_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_roam_config > 2)
panic("roamconfig");
#endif
roam.sync_alarm = 0;
roam.retry_thresh = retry[sc->sc_param.rp_roam_config];
roam.rssi_threshold = rssi[sc->sc_param.rp_roam_config];
roam.xxx1 = 0x5a;
roam.sync_rssi_threshold = 0;
roam.xxx2 = 0x5a;
roam.missed_sync = 0x4;
if (rln_msg_txrx(sc, &roam, sizeof roam,
&response, sizeof response))
return (-1);
return (0);
}
/* Enable roaming. */
int
rln_roam(sc)
struct rln_softc *sc;
{
struct rln_mm_cmd roam = RLN_MM_ROAM;
struct rln_mm_cmd response;
return (rln_msg_txrx(sc, &roam, sizeof roam,
&response, sizeof response));
}
/* Enable multicast capability. */
int
rln_multicast(sc, enable)
struct rln_softc *sc;
int enable;
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct rln_mm_multicast mcast = { RLN_MM_MULTICAST };
struct rln_mm_cmd response;
int ret;
mcast.enable = enable;
ret = rln_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. */
int
rln_searchsync(sc)
struct rln_softc *sc;
{
struct rln_mm_search search = { RLN_MM_SEARCH };
struct rln_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 (rln_msg_txrx(sc, &search, sizeof search,
&response, sizeof response));
}
/* Set values from an external parameter block. */
int
rln_iosetparam(sc, param)
struct rln_softc *sc;
struct rln_param *param;
{
int error = 0;
if (param->rp_roam_config > 2)
error = EINVAL;
if (param->rp_security > 0x00ffffff)
error = EINVAL;
if (param->rp_station_type > 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 (rln_sendinit(sc))
error = EIO;
}
return (error);
}
/* Protect the eeprom from storing a security ID(?) */
int
rln_lockprom(sc)
struct rln_softc *sc;
{
struct rln_mm_cmd lock = RLN_MM_EEPROM_PROTECT;
struct rln_mm_cmd response;
/* XXX Always yields an error? */
return (rln_msg_txrx(sc, &lock, sizeof lock,
&response, sizeof response));
}
/* Set the h/w Inactivity Time Out timer on the card. */
int
rln_ito(sc)
struct rln_softc * sc;
{
struct rln_mm_setito ito = { RLN_MM_MULTICAST };
struct rln_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 (rln_msg_txrx(sc, &ito, sizeof ito,
&response, sizeof response))
return (-1);
}
/* Put the card into standby mode. */
int
rln_standby(sc)
struct rln_softc * sc;
{
struct rln_mm_standby standby = { RLN_MM_STANDBY };
standby.xxx = 0;
if (rln_msg_txrx(sc, &ito, sizeof ito, NULL, 0))
return (-1);
}
void
rln_crypt(userkey, cardkey)
char *userkey; /* User's string (max 20 chars). */
u_int8_t *cardkey; /* 20 bits (3 bytes) */
{
/*
* From <http://www.proxim.com/learn/whiteppr/rl2security.shtml>
* "RangeLAN2 Security Features":
*
* The Security ID is a unique, 20 character alphanumeric
* string defined and configured by the user. It must be
* identically configured in every radio intended to
* communicate with others in the same network. Once
* configured, the Security ID is reduced to 20 bits by a
* proprietary algorithm confidential to Proxim. It is
* merged with the radio MAC address (a 12 character field
* unique to every radio), scrambled and stored using another
* proprietary, confidential algorithm.
*/
int32_t key;
int8_t ret;
int i;
int len;
int32_t multiplicand = 0x80000181;
int64_t res;
/*
* This algorithm is `compatible' with Proxim's first
* `proprietary confidential algorithm': i.e., it appears
* to be functionally identical.
*/
len = strlen(s);
key = 0x030201;
for (i = 0; i < len; i++) {
key *= userkey[i];
res = (int64_t)multiplicand * key;
key = key - 0xfffffd *
(((key + (int32_t)(res >> 32)) >> 23) - (key >> 31));
}
cardkey[0] = (key >> 16) & 0xff;
cardkey[1] = (key >> 8) & 0xff;
cardkey[2] = key & 0xff;
cardkey[0] |= 0x03; /* Restrict key space by 2 bits. */
}
#endif