File: [local] / sys / dev / ic / rlnsubr.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: rlnsubr.c,v 1.5 2002/03/14 01:26:55 millert 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/rln.h>
#include <dev/ic/rlnvar.h>
#include <dev/ic/rlnreg.h>
#include <dev/ic/rlncmd.h>
static int rln_tx_request(struct rln_softc *, u_int16_t);
static int rln_tx_end(struct rln_softc *);
/*
* Disables or enables interrupts from the card. Returns the old
* interrupt-enable state.
*/
int
rln_enable(sc, enable)
struct rln_softc * sc;
int enable;
{
int s;
int was_enabled;
s = splhigh();
was_enabled = (sc->sc_intsel & RLN_INTSEL_ENABLE) ? 1 : 0;
if (enable != was_enabled) {
if (enable)
sc->sc_intsel |= RLN_INTSEL_ENABLE;
else
sc->sc_intsel &=~RLN_INTSEL_ENABLE;
_rln_register_write_1(sc, RLN_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
rln_reset(sc)
struct rln_softc * sc;
{
int s;
int i;
int status;
u_int8_t op = 0x00;
s = splhigh();
dprintf(" R[");
if (sc->sc_cardtype & (RLN_CTYPE_UISA | RLN_CTYPE_ONE_PIECE))
op = 0x04;
if (rln_status_read(sc) & RLN_STATUS_WAKEUP) {
rln_control_write(sc, op);
rln_control_write(sc, op | RLN_CONTROL_RESET);
dprintf(" 7ms");
DELAY(7000);
rln_control_write(sc, op);
dprintf(" 7ms");
DELAY(7000);
}
rln_control_write(sc, op);
rln_control_write(sc, op);
rln_control_write(sc, op | RLN_CONTROL_BIT3);
dprintf(" 67ms");
DELAY(67000);
rln_status_write(sc, 0x00);
if (sc->sc_cardtype & (RLN_CTYPE_UISA | RLN_CTYPE_ONE_PIECE))
rln_control_write(sc, 0x38);
/* RLN_CONTROL_BIT3 | RLN_CONTROL_RESET | RLN_CONTROL_16BIT */
else
rln_control_write(sc, 0x2c);
/* RLN_CONTROL_BIT3 | RLN_CONTROL_BIT2 | RLN_CONTROL_16BIT */
dprintf(" 67ms");
DELAY(67000);
rln_data_write_2(sc, 0xaa55);
rln_status_write(sc, 0x5a);
splx(s);
for (i = 0; i < 200 * 10; i++) { /* Proxim says 200. */
if ((status = rln_status_read(sc)) == 0x5a)
break;
DELAY(1000);
}
dprintf(" (%dms)", i);
s = splhigh();
if (status != 0x5a) {
splx(s);
/* Only winge if bus width not yet probed */
if (sc->sc_width != 0)
printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
dprintf("]=-1");
return (-1);
}
if (sc->sc_width == 8) {
if (sc->sc_cardtype & (RLN_CTYPE_UISA | RLN_CTYPE_ONE_PIECE))
rln_control_write(sc, RLN_CONTROL_BIT3);
else
rln_control_write(sc, RLN_CONTROL_BIT3 |
RLN_CONTROL_BIT2);
rln_data_write_1(sc, 0x20);
} else if (sc->sc_width == 16) {
rln_data_write_2(sc, 0x0000);
} else {
if (rln_data_read_2(sc) == 0x55aa) {
rln_data_write_2(sc, 0x0000);
sc->sc_width = 16;
} else {
if (sc->sc_cardtype & (RLN_CTYPE_UISA |
RLN_CTYPE_ONE_PIECE))
rln_control_write(sc, RLN_CONTROL_BIT3);
else
rln_control_write(sc, RLN_CONTROL_BIT3 |
RLN_CONTROL_BIT2);
rln_data_write_1(sc, 0x20);
sc->sc_width = 8;
}
/* printf("%s: %d bit bus\n", sc->sc_dev.dv_xname,
sc->sc_width); */
}
rln_status_write(sc, 0x00);
sc->sc_intsel = 0;
rln_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 RLN_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
rln_wakeup(sc, wnew)
struct rln_softc * sc;
u_int8_t wnew;
{
u_int8_t wold, s;
int i;
/* Save what the last-written values were. */
wold = (sc->sc_status & RLN_STATUS_WAKEUP) |
(sc->sc_control & RLN_CONTROL_RESET);
if (wnew == RLN_WAKEUP_SET) {
/* SetWakeupBit() */
dprintf(" Ws[");
rln_status_set(sc, RLN_STATUS_WAKEUP);
if (0/*LLDInactivityTimeOut &&
(sc->sc_cardtype & RLN_CTYPE_OEM)*/) {
dprintf (" 167ms");
DELAY(167000);
} else {
dprintf (" .1ms");
DELAY(100);
}
s = rln_status_read(sc);
rln_control_set(sc, RLN_CONTROL_RESET);
if ((s & RLN_STATUS_WAKEUP) != 0)
for (i = 0; i < 9; i++) {
dprintf(" 2ms");
DELAY(2000);
rln_status_set(sc, RLN_STATUS_WAKEUP);
}
dprintf("]");
} else {
/* ClearWakeupBit() */
dprintf(" Wc[");
if ((wnew & RLN_STATUS_WAKEUP) == 0)
rln_status_clear(sc, RLN_STATUS_WAKEUP);
if ((wnew & RLN_CONTROL_RESET) == 0)
rln_control_clear(sc, RLN_CONTROL_RESET);
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 rln_clear_nak() after 100ms)
*/
static int
rln_tx_request(sc, len)
struct rln_softc * sc;
u_int16_t len;
{
/* TxRequest() */
int s;
int i;
u_int8_t status;
/* u_int8_t w; */
/* w = rln_wakeup(sc, RLN_WAKEUP_SET); */
dprintf(" Tr[");
if (sc->sc_width == 16) {
rln_status_tx_write(sc, RLN_STATUS_TX_HILEN_AVAIL);
rln_data_write_2(sc, len);
rln_status_tx_int(sc);
s = spl0();
for (i = 0; i < 600; i++) {
status = rln_status_tx_read(sc);
if (status == RLN_STATUS_TX_HILEN_ACCEPT ||
status == RLN_STATUS_TX_ERROR)
break;
DELAY(1000);
}
splx(s);
dprintf(" %dms", i);
if (status == RLN_STATUS_TX_HILEN_ACCEPT)
goto success;
if (status == RLN_STATUS_TX_ERROR)
goto error;
} else if (sc->sc_width == 8) {
rln_status_tx_write(sc, RLN_STATUS_TX_LOLEN_AVAIL);
rln_data_write_1(sc, len & 0xff);
rln_status_tx_int(sc);
s = spl0();
for (i = 0; i < 6800; i++) {
status = rln_status_tx_read(sc);
if (status == RLN_STATUS_TX_LOLEN_ACCEPT)
break;
DELAY(1000);
}
splx(s);
dprintf(" %dms", i);
if (status == RLN_STATUS_TX_LOLEN_ACCEPT) {
rln_data_write_1(sc, (len >> 8) & 0xff);
rln_status_tx_write(sc, RLN_STATUS_TX_HILEN_AVAIL);
s = spl0();
for (i = 0; i < 600; i++) {
status = rln_status_tx_read(sc);
if (status == RLN_STATUS_TX_HILEN_ACCEPT ||
status == RLN_STATUS_TX_ERROR)
break;
DELAY(1000);
}
splx(s);
dprintf(" %dms", i);
if (status == RLN_STATUS_TX_HILEN_ACCEPT)
goto success;
if (status == RLN_STATUS_TX_ERROR)
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:
/* 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:
/* rln_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
rln_tx_end(sc)
struct rln_softc * sc;
{
/* EndOfTx() */
int i;
int s;
u_int8_t status;
dprintf(" Te[");
s = spl0();
for (i = 0; i < 600; i++) {
status = rln_status_tx_read(sc);
if (status == RLN_STATUS_TX_XFR_COMPLETE)
break;
DELAY(1000);
}
splx(s);
if (status == RLN_STATUS_TX_XFR_COMPLETE) {
rln_status_tx_write(sc, RLN_STATUS_TX_IDLE);
dprintf("]=0");
return (0);
} else {
printf("%s: tx cmd failed (%02x)\n", sc->sc_dev.dv_xname,
status);
rln_need_reset(sc);
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
rln_rx_request(sc, timeo)
struct rln_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 = rln_status_rx_read(sc);
/* Short wait for states 1|5|6. */
s = spl0();
for (i = 0; i < timeo; i++) {
if (status == RLN_STATUS_RX_LOLEN_AVAIL ||
status == RLN_STATUS_RX_HILEN_AVAIL ||
status == RLN_STATUS_RX_ERROR)
break;
DELAY(1000);
status = rln_status_rx_read(sc);
}
splx(s);
dprintf(" (%dms)",i);
if (sc->sc_width == 16) {
if (status != RLN_STATUS_RX_HILEN_AVAIL)
goto badstatus_quiet;
/* Read 2 octets. */
len = rln_data_read_2(sc);
} else if (sc->sc_width == 8) {
if (status != RLN_STATUS_RX_LOLEN_AVAIL)
goto badstatus_quiet;
/* Read low octet. */
lo = rln_data_read_1(sc);
rln_status_rx_write(sc, RLN_STATUS_RX_LOLEN_ACCEPT);
rln_status_rx_int(sc);
s = spl0();
for (i = 0; i < 600; i++) {
status = rln_status_rx_read(sc);
if (status == RLN_STATUS_RX_HILEN_AVAIL)
break;
DELAY(1000);
}
splx(s);
if (status != RLN_STATUS_RX_HILEN_AVAIL)
goto badstatus;
/* Read high octet. */
hi = rln_data_read_1(sc);
len = lo | (hi << 8);
}
#ifdef DIAGNOSTIC
else
panic("rln: bus width %d", sc->sc_width);
#endif
dprintf(" len=%d]", len);
return (len);
badstatus:
printf("%s: rx_request timed out, status %02x\n",
sc->sc_dev.dv_xname, status);
badstatus_quiet:
if (status == RLN_STATUS_RX_ERROR)
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
rln_rx_pdata(sc, buf, len, pd)
struct rln_softc * sc;
void * buf;
int len;
struct rln_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. */
rln_data_read_multi_2(sc, data, len / 2);
#ifdef RLNDEBUG_REG
dprintf(" D>");
dprinthex(data, len);
#endif
if (len & 1) {
/* Read the last octet plus a bit extra. */
union {
u_int16_t w;
u_int8_t b[2];
} u;
u.w = rln_data_read_2(sc);
data[len - 1] = u.b[0];
pd->p_data = u.b[1];
pd->p_nremain = 1;
#ifdef RLNDEBUG_REG
dprintf(" D>{%02x%02x}", u.b[0], u.b[1]);
#endif
}
} else if (sc->sc_width == 8) {
rln_data_read_multi_1(sc, data, len);
#ifdef RLNDEBUG_REG
dprintf(" D>");
dprinthex(data, len);
#endif
if (len & 1) {
/* Must read multiples of two. */
pd->p_data = rln_data_read_1(sc);
pd->p_nremain = 1;
#ifdef RLNDEBUG_REG
dprintf(" D>{%02x}", pd->p_data);
#endif
}
}
}
int
rln_rx_data(sc, buf, len)
struct rln_softc * sc;
void * buf;
int len;
{
/* RxData() */
struct rln_pdata pd = { 0, 0 };
int s;
int i;
u_int8_t status;
dprintf(" Rd[");
rln_status_rx_write(sc, RLN_STATUS_RX_HILEN_ACCEPT);
rln_status_rx_int(sc);
s = spl0();
for (i = 0; i < 600; i++) {
status = rln_status_rx_read(sc);
if (status == RLN_STATUS_RX_XFR)
break;
DELAY(1000);
}
splx(s);
if (status != RLN_STATUS_RX_XFR) {
dprintf("]=-1");
return (-1);
}
rln_rx_pdata(sc, buf, len, &pd);
#ifdef DIAGNOSTIC
/* We should have nothing left over. */
if (pd.p_nremain || len & 1)
panic("rln_rx_data: leftover");
#endif
dprintf("]=0");
return (0);
}
void
rln_rx_end(sc)
struct rln_softc * sc;
{
/* EndOfRx() */
dprintf(" Re[");
rln_status_rx_write(sc, RLN_STATUS_RX_XFR_COMPLETE);
rln_status_rx_int(sc);
/* rln_wakeup(sc, 0); */
dprintf("]");
}
/* Clear a transmission NAK from the card. */
void
rln_clear_nak(sc)
struct rln_softc * sc;
{
/* ClearNAK() */
rln_status_tx_write(sc, RLN_STATUS_CLRNAK);
rln_status_tx_int(sc);
}
/*
* Send a command message to the card. Returns;
* 2: NAK
* -1: failure
* 0: success
*/
int
rln_msg_tx_start(sc, buf, pktlen, state)
struct rln_softc * sc;
void * buf;
int pktlen;
struct rln_msg_tx_state * state;
{
struct rln_mm_cmd * cmd = (struct rln_mm_cmd *)buf;
int ret;
state->ien = rln_enable(sc, 0);
state->pd.p_nremain = 0;
if (!(cmd->cmd_letter == 'A' && cmd->cmd_fn == 6)) /* Standby. */
state->w = rln_wakeup(sc, RLN_WAKEUP_SET);
else
state->w = RLN_WAKEUP_NOCHANGE;
ret = rln_tx_request(sc, pktlen);
if (ret == 2) {
rln_clear_nak(sc);
if (sc->sc_cardtype & RLN_CTYPE_OEM)
rln_need_reset(sc);
ret = 2;
}
else if (ret == 1) {
/* Timeout. */
rln_status_tx_write(sc, RLN_STATUS_TX_XFR);
ret = -1;
}
return (ret);
}
void
rln_msg_tx_data(sc, buf, len, state)
struct rln_softc * sc;
void * buf;
u_int16_t len;
struct rln_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 RLNDEBUG_REG
dprintf(" D<%02x%02x", u.b[0], u.b[1]);
#endif
rln_data_write_2(sc, u.w);
state->pd.p_nremain = 0;
}
if (len) {
if (sc->sc_width == 16) {
if (len >= 2)
rln_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)
rln_data_write_multi_1(sc, buf, len);
#ifdef DIAGNOSTIC
else
panic("rln_msg_tx_data width %d", sc->sc_width);
#endif
#ifdef RLNDEBUG_REG
dprintf(" D<");
dprinthex(data, len);
#endif
}
}
int
rln_msg_tx_end(sc, state)
struct rln_softc * sc;
struct rln_msg_tx_state * state;
{
int ret;
/* Flush the tx buffer. */
if (state->pd.p_nremain)
rln_msg_tx_data(sc, NULL, 0, state);
#ifdef DIAGNOSTIC
if (state->pd.p_nremain)
panic("rln_msg_tx_end remain %d", state->pd.p_nremain);
#endif
ret = rln_tx_end(sc);
if (sc->sc_arpcom.ac_if.if_flags & IFF_OACTIVE)
state->w = RLN_WAKEUP_NOCHANGE;
rln_wakeup(sc, state->w);
rln_enable(sc, state->ien);
return (ret);
}
/* Return the next unique sequence number to use for a transmitted command */
u_int8_t
rln_newseq(sc)
struct rln_softc * sc;
{
int s;
u_int8_t seq;
s = splhigh();
seq = sc->sc_pktseq++;
if (sc->sc_pktseq > RLN_MAXSEQ)
sc->sc_pktseq = 0;
splx(s);
return (seq);
}
/*
* 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 rlnsoftintr() can signal this routine when it has serviced
* and correctly received a response.
*/
int
rln_msg_txrx(sc, tx, txlen, rx, rxlen)
struct rln_softc * sc;
void * tx;
int txlen;
void * rx;
int rxlen;
{
struct rln_mm_cmd * txc = (struct rln_mm_cmd *)tx;
struct rln_mm_cmd * rxc = (struct rln_mm_cmd *)rx;
struct rln_msg_tx_state state;
int ien;
int ret;
#ifdef DIAGNOSTIC
if (rx != NULL && rxlen < sizeof *rxc)
panic("rln_msg_txrx");
#endif
txc->cmd_seq = rln_newseq(sc);
#ifdef RLNDUMP
printf("%s: send %c%d seq %d data ", sc->sc_dev.dv_xname,
txc->cmd_letter, txc->cmd_fn, txc->cmd_seq);
RLNDUMPHEX(txc, sizeof *txc);
printf(":");
RLNDUMPHEX((char *)tx + sizeof *txc, txlen - sizeof *txc);
printf("\n");
#endif
if (rx != NULL)
if (rln_mbox_create(sc, txc->cmd_seq, rx, rxlen) < 0)
/* Mailbox collision. */
return (-1);
/* Start the transfer. */
if ((ret = rln_msg_tx_start(sc, tx, txlen, &state))) {
if (rx != NULL)
rln_mbox_wait(sc, txc->cmd_seq, -1);
return (ret);
}
/* Always send an even number of octets. */
rln_msg_tx_data(sc, tx, (txlen + 1) & ~1, &state);
/* End the transmission. */
if ((ret = rln_msg_tx_end(sc, &state))) {
/* Destroy mailbox. */
if (rx != NULL)
rln_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 = rln_enable(sc, 1);
/* Wait for the reply message. */
if (rln_mbox_wait(sc, txc->cmd_seq, 4000) <= 0) {
printf("%s: lost message %c%d seq %d\n", sc->sc_dev.dv_xname,
txc->cmd_letter, txc->cmd_fn, txc->cmd_seq);
rln_enable(sc, ien);
return (-1);
}
rln_enable(sc, ien);
#ifdef RLNDUMP
printf("%s: recv %c%d seq %d data ", sc->sc_dev.dv_xname,
rxc->cmd_letter, rxc->cmd_fn, rxc->cmd_seq);
RLNDUMPHEX(rxc, sizeof *rxc);
printf(":");
RLNDUMPHEX(((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 & ~0x80,
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.
* The interrupt service routine signals the mailbox when it
* gets the reply message.
*/
/* Create a mailbox for filling. */
int
rln_mbox_create(sc, seq, buf, len)
struct rln_softc * sc;
u_int8_t seq;
void * buf;
size_t len;
{
int s;
struct rln_mbox * mb = &sc->sc_mbox[seq];
dprintf(" <create %d", seq);
#ifdef DIAGNOSTIC
if (seq > RLN_NMBOX)
panic("mbox create");
#endif
s = splhigh();
if (mb->mb_state != RLNMBOX_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 = RLNMBOX_EMPTY;
dprintf(" empty>");
splx(s);
return (0);
}
/* Wait for a mailbox to be filled. */
int
rln_mbox_wait(sc, seq, timeo)
struct rln_softc * sc;
u_int8_t seq;
int timeo;
{
int i;
int s;
int ret;
volatile struct rln_mbox * mb = &sc->sc_mbox[seq];
dprintf(" <wait %d", seq);
#ifdef DIAGNOSTIC
if (seq > RLN_NMBOX)
panic("mbox wait");
#endif
#if defined(RLN_TSLEEP)
if (!cold) {
tsleep((void *)mb, PRIBIO, "rlnmbox", hz * timeo / 1000);
if (mb->mb_state == RLNMBOX_FILLING) {
/* Must wait until filled. */
s = spl0();
while (mb->mb_state == RLNMBOX_FILLING)
;
splx(s);
}
} else {
/* Autoconfiguration - spin at spl0. */
#endif
s = spl0();
i = 0;
while (mb->mb_state == RLNMBOX_EMPTY && i < timeo) {
DELAY(1000);
i++;
}
if (i)
dprintf(" %dms", i);
while (mb->mb_state == RLNMBOX_FILLING)
;
splx(s);
#if defined(RLN_TSLEEP)
}
#endif
s = splhigh();
#ifdef DIAGNOSTIC
if (mb->mb_state != RLNMBOX_EMPTY && mb->mb_state != RLNMBOX_FILLED)
panic("mbox wait %d", mb->mb_state);
#endif
ret = mb->mb_actlen;
mb->mb_state = RLNMBOX_VOID;
dprintf(" void>=%d", ret);
splx(s);
return (ret);
}
/* Lock a mailbox for filling. */
int
rln_mbox_lock(sc, seq, bufp, lenp)
struct rln_softc * sc;
u_int8_t seq;
void ** bufp;
size_t * lenp;
{
int s;
struct rln_mbox * mb = &sc->sc_mbox[seq];
dprintf(" <lock %d", seq);
s = splhigh();
#ifdef DIAGNOSTIC
if (seq > RLN_NMBOX)
panic("mbox lock");
#endif
if (mb->mb_state != RLNMBOX_EMPTY) {
splx(s);
dprintf(" ?>");
return (-1);
}
mb->mb_state = RLNMBOX_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 octets. */
void
rln_mbox_unlock(sc, seq, actlen)
struct rln_softc * sc;
u_int8_t seq;
size_t actlen;
{
int s;
struct rln_mbox * mb = &sc->sc_mbox[seq];
dprintf(" <unlock %d", seq);
s = splhigh();
#ifdef DIAGNOSTIC
if (seq > RLN_NMBOX)
panic("mbox unlock seq");
if (mb->mb_state != RLNMBOX_FILLING)
panic("mbox unlock");
#endif
mb->mb_state = RLNMBOX_FILLED;
dprintf(" filled>");
mb->mb_actlen = actlen;
#if defined(RLN_TSLEEP)
wakeup(mb);
#endif
splx(s);
}