David van Moolenbroek bcc1b08c61 orinoco: use new libnetdriver
Change-Id: I003590539dd76f4be8e067f986bb1f17b151490d
2014-12-04 12:10:51 +00:00

1287 lines
39 KiB
C

/*
* orinoco.c
*
* This file contains a wireless device driver for Prism based wireless
* cards.
*
* Created by Stevens Le Blond <slblond@few.vu.nl>
* and Michael Valkering <mjvalker@cs.vu.nl>
*
*/
#include <minix/drivers.h>
#include <minix/netdriver.h>
#include <machine/pci.h>
#include <machine/vmparam.h>
#include <sys/mman.h>
#define VERBOSE 1 /* display message during init */
#include "assert.h"
#include "hermes.h"
#include "hermes_rid.h"
#include "orinoco.h"
#define OR_M_ENABLED 1
#define OR_M_DISABLED 0
#define OR_F_EMPTY 0
#define OR_F_MULTI 1
#define OR_F_BROAD (1<<1)
#define OR_F_ENABLED (1<<2)
#define OR_F_PROMISC (1<<3)
#define OR_F_READING (1<<4)
#define OR_F_SEND_AVAIL (1<<5)
#define OR_F_PACK_SENT (1<<6)
#define OR_F_PACK_RECV (1<<7)
#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC |\
HERMES_EV_WTERR | HERMES_EV_TXEXC|\
HERMES_EV_INFO | HERMES_EV_INFDROP|\
HERMES_EV_TX)
#define NO_FID (-1)
#define ETH_ALEN 6
#define USER_BAP 0
#define IRQ_BAP 1
#define ETH_HLEN 14
static t_or or_state;
struct ethhdr {
u8_t h_dest[ETH_ALEN];
u8_t h_src[ETH_ALEN];
u16_t h_proto;
};
struct header_struct {
/* 802.3 */
u8_t dest[ETH_ALEN];
u8_t src[ETH_ALEN];
u16_t len;
/* 802.2 */
u8_t dsap;
u8_t ssap;
u8_t ctrl;
/* SNAP */
u8_t oui[3];
u16_t ethertype;
};
#define RUP_EVEN(x) (((x) + 1) & (~1))
u8_t encaps_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
#define ENCAPS_OVERHEAD (sizeof (encaps_hdr) + 2)
/********************************************************************
* Data tables *
********************************************************************/
/* The frequency of each channel in MHz */
static const long channel_frequency[] = {
2412, 2417, 2422, 2427, 2432, 2437, 2442,
2447, 2452, 2457, 2462, 2467, 2472, 2484
};
#define NUM_CHANNELS (sizeof(channel_frequency) / sizeof(channel_frequency[0]))
/* This tables gives the actual meanings of the bitrate IDs returned by the
* firmware. Not used yet */
struct {
int bitrate; /* in 100s of kilobits */
int automatic;
u16_t txratectrl;
} bitrate_table[] =
{
{110, 1, 15}, /* Entry 0 is the default */
{10, 0, 1},
{10, 1, 1},
{20, 0, 2},
{20, 1, 3},
{55, 0, 4},
{55, 1, 7},
{110, 0, 8},};
#define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
static void or_other(const message *m_ptr, int ipc_status);
static void or_stop(void);
static int or_probe(t_or *, unsigned int skip);
static void or_ev_info(t_or *);
static int or_init(unsigned int instance, ether_addr_t *addr);
static void or_init_struct(t_or *, unsigned int);
static void or_init_hw(t_or *, ether_addr_t *);
static void or_check_ints(t_or *);
static void or_writerids(hermes_t *, t_or *);
static void or_readrids(hermes_t *, t_or *, ether_addr_t *);
static u32_t or_get_bar(int devind, t_or * orp);
static void or_stat(eth_stat_t *stat);
static void print_linkstatus(t_or * orp, u16_t status);
static ssize_t or_recv(struct netdriver_data *data, size_t max);
static int or_get_recvd_packet(t_or *orp, u16_t rxfid, u8_t *databuf);
static void or_reset(void);
static void or_alarm(clock_t stamp);
static int or_send(struct netdriver_data *data, size_t size);
static void setup_wepkey(t_or *orp, char *wepkey0);
static void or_intr(unsigned int mask);
static void or_handler(t_or *orp);
static void or_dump(void);
static const struct netdriver or_table = {
.ndr_init = or_init,
.ndr_stop = or_stop,
.ndr_recv = or_recv,
.ndr_send = or_send,
.ndr_stat = or_stat,
.ndr_intr = or_intr,
.ndr_alarm = or_alarm,
.ndr_other = or_other
};
/*****************************************************************************
* main *
* *
* *
* The main function of the driver, receiving and processing messages *
*****************************************************************************/
int main(int argc, char *argv[])
{
env_setargs(argc, argv);
netdriver_task(&or_table);
return 0;
}
/*****************************************************************************
* or_other *
* *
* *
* Process miscellaneous messages *
*****************************************************************************/
static void or_other(const message *m_ptr, int ipc_status)
{
if (is_ipc_notify(ipc_status) && m_ptr->m_source == TTY_PROC_NR)
or_dump();
}
/*****************************************************************************
* or_stop *
* *
* Stop the card *
*****************************************************************************/
static void or_stop(void)
{
t_or *orp;
orp = &or_state;
/* TODO: send a signal to the card to shut it down */
}
/*****************************************************************************
* or_intr *
* *
* Process the interrupts which the card generated *
*****************************************************************************/
static void or_intr(unsigned int __unused mask)
{
t_or *orp;
int s;
orp = &or_state;
/* Run interrupt handler at driver level. */
or_handler(orp);
/* Reenable interrupts for this hook. */
if ((s=sys_irqenable(&orp->or_hook_id)) != OK)
printf("orinoco: error, couldn't enable interrupts: %d\n", s);
/* Perform tasks based on the flagged conditions. */
or_check_ints(orp);
}
/*****************************************************************************
* or_reset *
* *
* Sometime the card gets screwed, behaving erratically. Solution: reset the *
* card. This is actually largely redoing the initialization *
*****************************************************************************/
static void or_reset(void)
{
static clock_t last_reset, now;
t_or *orp;
int i, r;
if (OK != (r = getticks(&now)))
panic("orinoco: getuptime() failed: %d", r);
if(now - last_reset < sys_hz() * 10) {
printf("Resetting card too often. Going to reset driver\n");
exit(1);
}
last_reset = now;
orp = &or_state;
orp->or_need_reset = FALSE;
or_init_hw(orp, NULL);
orp->rx_last = orp->rx_first = 0;
for(i = 0; i < NR_RX_BUFS; i++) {
orp->rx_length[0] = 0;
}
if (orp->or_tx.ret_busy)
orp->or_tx_busy--;
orp->or_tx.ret_busy = FALSE;
orp->or_send_int = TRUE;
}
/*****************************************************************************
* or_dump *
* *
* Dump interesting information about the card on F-key pressed. *
* Not implemented yet *
*****************************************************************************/
static void or_dump(void)
{
t_or *orp;
int sfkeys;
orp = &or_state;
if(OK != fkey_events(NULL, &sfkeys)) {
printf("Contacting the TTY failed\n");
}
if(bit_isset(sfkeys, 11)) {
print_linkstatus(orp, orp->last_linkstatus);
}
}
/*****************************************************************************
* or_init *
* *
* The main initialization function, called at startup. *
*****************************************************************************/
static int or_init(unsigned int instance, ether_addr_t *addr)
{
int fkeys, sfkeys, r;
t_or *orp;
orp = &or_state;
/* Initialize the orp structure */
or_init_struct(orp, instance);
/* Try to find out where the card is in the pci bus */
if (!or_probe(orp, instance))
return ENXIO;
/* initialize card, hardware/firmware */
or_init_hw(orp, addr);
/* Use a synchronous alarm instead of a watchdog timer. */
sys_setalarm(sys_hz(), 0);
/* Observe some function key for debug dumps. */
fkeys = sfkeys = 0; bit_set(sfkeys, 11);
if ((r=fkey_map(&fkeys, &sfkeys)) != OK)
printf("Warning: orinoco couldn't observe F-key(s): %d\n",r);
return OK;
}
/*****************************************************************************
* or_probe *
* *
* Try to find the card based on information provided by pci and get irq and *
* bar *
*****************************************************************************/
static int or_probe(t_or * orp, unsigned int skip)
{
u8_t ilr;
u32_t bar;
char *dname;
u16_t vid, did;
int r, devind;
pci_init();
/* Start looking from the beginning */
r = pci_first_dev(&devind, &vid, &did);
if (r == 0)
return FALSE;
/* Skip as many instances as requested */
while (skip--) {
r = pci_next_dev(&devind, &vid, &did);
if (!r)
return FALSE;
}
/* Get the name as advertised by pci */
dname = pci_dev_name(vid, did);
if (!dname)
dname = "unknown device";
printf("%s: %s (%04x/%04x) at %s\n",
orp->or_name, dname, vid, did, pci_slot_name(devind));
pci_reserve(devind);
/* Get the irq */
ilr = pci_attr_r8(devind, PCI_ILR);
orp->or_irq = ilr;
/* Map registers into memory */
bar = or_get_bar(devind, orp);
orp->hw.locmem = vm_map_phys(SELF, (void *)bar, PAGE_SIZE);
if (orp->hw.locmem == MAP_FAILED)
panic("or_probe: vm_map_phys failed");
return TRUE;
}
/*****************************************************************************
* or_get_bar *
* *
* Get the base address from pci (from Base Address Register) and find out *
* whether the card is memory mapped or in I/O space. Currently, only *
* memmory mapped is supported. *
*****************************************************************************/
static u32_t or_get_bar(int devind, t_or * orp)
{
u32_t bar;
int is_iospace;
hermes_t *hw = &(orp->hw);
/* bit 1 off the PCI_BAR register indicates whether the cards registers
* are mapped in io-space or shared memory */
is_iospace = pci_attr_r32(devind, PCI_BAR) & 1;
if (is_iospace) {
/* read where the base address is in I/O space */
bar = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
if ((bar & 0x3ff) >= 0x100 - 32 || bar < 0x400)
panic("base address isn't properly configured");
/* In I/O space registers are 2 bytes wide, without any spacing
* in between */
hermes_struct_init (hw, bar, is_iospace,
HERMES_16BIT_REGSPACING);
#if VERBOSE
printf ("%s: using I/O space address 0x%x, IRQ %d\n",
orp->or_name, bar, orp->or_irq);
#endif
panic("Not implemented yet");
/* Although we are able to find the desired bar and irq for an
* I/O spaced card, we haven't implemented the right register
* accessing functions. This wouldn't be difficult, but we were
* not able to test them. Therefore, give an alert here */
return bar;
} else {
/* read where the base address is in shared memory */
bar = pci_attr_r32(devind, PCI_BAR) & 0xfffffff0;
/* maybe some checking whether the address is legal... */
/* Memory mapped registers are 2 bytes wide, aligned on 4
* bytes */
hermes_struct_init (hw, bar, is_iospace,
HERMES_32BIT_REGSPACING);
#if VERBOSE
printf ("%s: using shared memory address", orp->or_name);
printf (" 0x%x, IRQ %d\n", bar, orp->or_irq);
#endif
return bar;
}
}
/*****************************************************************************
* or_init_struct *
* *
* Set the orinoco structure to default values *
*****************************************************************************/
static void or_init_struct(t_or * orp, unsigned int instance)
{
int i;
memset(orp, 0, sizeof(*orp));
strlcpy(orp->or_name, OR_NAME, sizeof(orp->or_name));
orp->or_name[sizeof(OR_NAME) - 2] = instance + '0';
orp->or_link_up = -1;
orp->or_tx.or_txfid = NO_FID;
for(i = 0; i < NR_RX_BUFS; i++) {
orp->rxfid[i] = NO_FID;
orp->rx_length[i] = 0;
}
/* Keep an administration in the driver whether the internal
buffer is in use. That's what ret_busy is for */
orp->or_tx.ret_busy = FALSE;
orp->or_tx_busy = 0;
orp->or_nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
}
/*****************************************************************************
* or_init_hw *
* *
* Initialize hardware and prepare for intercepting the interrupts. At the *
* end, the card is up and running. May be called multiple times. *
*****************************************************************************/
static void or_init_hw(t_or * orp, ether_addr_t * addr)
{
#if VERBOSE
int i;
#endif
int err, s;
hermes_t *hw = &(orp->hw);
static int first_time = TRUE;
/* first step in starting the card */
if (hermes_cor_reset(hw) != 0) {
printf ("%s: Failed to start the card\n", orp->or_name);
}
/* here begins the real things, yeah! ;) */
if ((err = hermes_init (hw)) != 0) {
printf ("error value of hermes_init(): %d\n", err);
}
if (first_time) {
/* Get the MAC address (which is a data item in the card)*/
or_readrids(hw, orp, addr);
}
/* Write a few rids to the card, e.g. WEP key*/
or_writerids (hw, orp);
#if VERBOSE
printf ("%s: Ethernet address ", orp->or_name);
for (i = 0; i < 6; i++)
printf("%x%c", addr->ea_addr[i], i < 5 ? ':' : '\n');
#endif
/* Prepare internal TX buffer in the card */
err = hermes_allocate (hw,
orp->or_nicbuf_size,
&(orp->or_tx.or_txfid));
if (err)
printf ("%s:Error %d allocating Tx buffer\n",
orp->or_name, err);
/* Establish event handle */
if(first_time) {
orp->or_hook_id = orp->or_irq;
if ((s=sys_irqsetpolicy(orp->or_irq, 0,
&orp->or_hook_id)) != OK)
printf("orinoco: couldn't set IRQ policy: %d\n", s);
if ((s=sys_irqenable(&orp->or_hook_id)) != OK)
printf("orinoco: couldn't enable interrupts: %d\n", s);
first_time = FALSE;
}
/* Tell the card which events should raise an interrupt to the OS */
hermes_set_irqmask (hw, ORINOCO_INTEN);
/* Enable operation */
err = hermes_docmd_wait (hw, HERMES_CMD_ENABLE, 0, NULL);
if (err)
printf("%s: Error %d enabling MAC port\n", orp->or_name, err);
}
/*****************************************************************************
* or_readrids *
* *
* Read some default rids from the card. A rid (resource identifier) *
* is a data item in the firmware, some configuration variable. *
* In our case, we are mostly interested in the MAC address for now *
*****************************************************************************/
static void or_readrids(hermes_t * hw, t_or * orp, ether_addr_t * addr)
{
int err;
assert(addr != NULL);
/* Read the MAC address */
err = hermes_read_ltv (hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
ETH_ALEN, NULL, addr);
if (err)
printf("%s: failed to read MAC address!\n", orp->or_name);
}
/*****************************************************************************
* or_writerids *
* *
* Write some default rids to the card. A rid (resource identifier) *
* is a data item in the firmware, some configuration variable, e.g. WEP key *
*****************************************************************************/
static void or_writerids(hermes_t * hw, t_or * orp)
{
int err;
struct hermes_idstring idbuf;
static char essid[IW_ESSID_MAX_SIZE + 1];
static char wepkey0[LARGE_KEY_LENGTH + 1];
/* Set the MAC port */
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, 1);
if (err) {
printf ("%s: Error %d setting port type\n", orp->or_name, err);
return;
}
if (OK != env_get_param("essid", essid, sizeof(essid))) {
essid[0] = 0;
}
if(strlen(essid) == 0) {
printf("%s: no essid provided in boot monitor!\n",
orp->or_name);
printf("Hope you'll connect to the right network... \n");
}
/* Set the desired ESSID */
idbuf.len = strlen (essid);
memcpy (&idbuf.val, essid, sizeof (idbuf.val));
err = hermes_write_ltv (hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
HERMES_BYTES_TO_RECLEN (strlen (essid) + 2),
&idbuf);
if (err) {
printf ("%s: Error %d setting DESIREDSSID\n",
orp->or_name, err);
return;
}
if (OK != env_get_param("wep", wepkey0, sizeof(wepkey0))) {
wepkey0[0] = 0;
}
switch(strlen(wepkey0)) {
case 0:
/* No key found in monitor, using no encryption */
break;
case LARGE_KEY_LENGTH:
setup_wepkey(orp, wepkey0);
break;
default:
printf("Invalid key provided. Has to be 13 chars\n");
break;
}
}
/*****************************************************************************
* setup_wepkey *
* *
* If a wepkey is provided in the boot monitor, set the necessary rids so *
* that the card will decrypt received data and encrypt data to send by *
* by default with this key. *
* It appears that there is a severe bug in setting up WEP. If the driver *
* doesnt function properly, please turn WEP off. *
*****************************************************************************/
static void setup_wepkey(t_or *orp, char *wepkey0)
{
int default_key = 0, err = 0;
hermes_t *hw = &(orp->hw);
err = hermes_write_wordrec (hw, USER_BAP,
HERMES_RID_CNFWEPDEFAULTKEYID,
default_key);
if (err)
printf ("%s: Error %d setting the default WEP-key entry\n",
orp->or_name, err);
err = hermes_write_ltv (hw, USER_BAP,
HERMES_RID_CNFDEFAULTKEY0,
HERMES_BYTES_TO_RECLEN(LARGE_KEY_LENGTH),
wepkey0);
if (err)
printf ("%s: Error %d setting the WEP-key0\n",
orp->or_name, err);
err = hermes_write_wordrec (hw, USER_BAP,
HERMES_RID_CNFAUTHENTICATION,
HERMES_AUTH_OPEN);
if (err)
printf ("%s: Error %d setting the authentication flag\n",
orp->or_name, err);
err = hermes_write_wordrec (hw, USER_BAP,
HERMES_RID_CNFWEPFLAGS_INTERSIL,
HERMES_WEP_PRIVACY_INVOKED);
if (err)
printf ("%s: Error %d setting the master wep setting flag\n",
orp->or_name, err);
}
/*****************************************************************************
* or_handler *
* *
* The handler which is called when the card generated an interrupt. Events *
* like EV_INFO and EV_RX have to be handled before an acknowledgement for *
* the event is returned to the card. See also the documentation *
*****************************************************************************/
static void or_handler(t_or *orp)
{
int length;
u16_t evstat, events, fid;
hermes_t *hw = &(orp->hw);
beginning:
/* Retrieve which kind of event happened */
evstat = hermes_read_reg (hw, HERMES_EVSTAT);
events = evstat;
/* There are plenty of events possible. The more interesting events
are actually implemented. Whether the following events actually
raise an interrupt depends on the value of ORINOCO_INTEN. For more
information about the events, see the specification in pdf */
/* Occurs at each tick of the auxiliary time */
if (events & HERMES_EV_TICK) {
events &= ~HERMES_EV_TICK;
}
/* Occurs when a wait time-out error is detected */
if (events & HERMES_EV_WTERR) {
events &= ~HERMES_EV_WTERR;
}
/* Occurs when an info frame is dropped because there is not enough
buffer space available */
if (events & HERMES_EV_INFDROP) {
events &= ~(HERMES_EV_INFDROP);
}
/* This AP-only event will be asserted at the beacon interval prior to
the DTIM interval */
if (events & HERMES_EV_DTIM) {
events &= ~(HERMES_EV_DTIM);
}
/* Occurs when a command execution is completed */
if (events & HERMES_EV_CMD) {
events &= ~(HERMES_EV_CMD);
}
/* Occurs when the asynchronous transmission process is unsuccessfully
completed */
if (events & HERMES_EV_TXEXC) {
/* What buffer generated the event? Represented by an fid */
fid = hermes_read_reg(hw, HERMES_TXCOMPLFID);
if(fid == 0xFFFF) {
/* Illegal fid found */
printf("unexpected txexc_fid interrupted\n");
}
if (orp->or_tx.ret_busy)
orp->or_tx_busy--;
orp->or_tx.ret_busy = FALSE;
orp->or_send_int = TRUE;
orp->or_got_int = TRUE;
/* To detect illegal fids */
hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF);
events &= ~(HERMES_EV_TXEXC);
/* We don't do anything else yet.
* Could be used for statistics */
}
/* Occurs when the asynchronous transmission process is successfully
completed */
if (events & HERMES_EV_TX) {
events &= ~(HERMES_EV_TX);
/* Which buffer was sent, represented by an fid */
fid = hermes_read_reg (hw, HERMES_TXCOMPLFID);
if(fid == 0xFFFF) {
/* Illegal fid found */
printf("unexpected tx_fid interrupted\n");
}
if (orp->or_tx.ret_busy)
orp->or_tx_busy--;
orp->or_tx.ret_busy = FALSE;
orp->or_send_int = TRUE;
orp->or_got_int = TRUE;
orp->or_tx_alive = TRUE;
/* To detect illegal fids */
hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF);
/* We don't do anything else when such event happens */
}
/* Occurs when an info frame is available in the card */
if (events & HERMES_EV_INFO) {
events &= ~(HERMES_EV_INFO);
/* Process the information, inside the handler (!) */
or_ev_info(orp);
}
/* Occurs when a TX buffer is available again for usage */
if (events & HERMES_EV_ALLOC) {
/* Which frame is now marked as free? */
fid = hermes_read_reg (hw, HERMES_ALLOCFID);
if (fid == 0xFFFF){
/* An illegal frame identifier is found. Ignore */
printf("Allocate event on unexpected fid\n");
goto next;
}
/* To be able to detect illegal fids */
hermes_write_reg(hw, HERMES_ALLOCFID, 0xFFFF);
events &= ~(HERMES_EV_ALLOC);
}
/* Occurs when a frame is received */
if (events & HERMES_EV_RX) {
events &= ~(HERMES_EV_RX);
/* If the last buffer is still filled with data, then we don't
* have any buffers available to store the data */
if(orp->rx_length[orp->rx_last] != 0) {
/* indeed, we are going to overwrite information
* in a buffer
*/
}
/* Which buffer is storing the data (represented by a fid) */
orp->rxfid[orp->rx_last]
= hermes_read_reg (hw, HERMES_RXFID);
/* Get the packet from the card and store it in
* orp->rx_buf[orp->rx_last]. The length is returned by this
* function
*/
length = or_get_recvd_packet(orp, orp->rxfid[orp->rx_last],
(orp->rx_buf[orp->rx_last]));
if(length < 0) {
/* Error happened. */
printf("length < 0\n");
goto next;
} else {
orp->rx_length[orp->rx_last] = length;
}
/* The next buffer will be used the next time, circularly */
orp->rx_last++;
orp->rx_last %= NR_RX_BUFS;
orp->or_got_int = TRUE;
}
next:
if (events) {
printf("Unknown event: 0x%x\n", events);
}
/* Acknowledge to the card that the events have been processed. After
* this the card will assume we have processed any buffer which were in
* use for this event. */
hermes_write_reg (hw, HERMES_EVACK, evstat);
evstat = hermes_read_reg (hw, HERMES_EVSTAT);
if(evstat != 0 && !(evstat & HERMES_EV_TICK)) {
goto beginning;
}
}
/*****************************************************************************
* or_alarm *
* *
* Will be called regularly to see whether the driver has crashed. If that *
* condition is detected, reset the driver and card *
*****************************************************************************/
static void or_alarm(clock_t __unused stamp)
{
t_or *orp;
/* Use a synchronous alarm instead of a watchdog timer. */
sys_setalarm(sys_hz(), 0);
orp = &or_state;
if (orp->or_tx_busy == 0) {
/* Assume that an idle system is alive */
orp->or_tx_alive= TRUE;
return;
}
if (orp->connected == 0) {
orp->or_tx_alive= TRUE;
return;
}
if (orp->or_tx_alive) {
orp->or_tx_alive= FALSE;
return;
}
printf("or_alarm: resetting card\n");
orp->or_need_reset= TRUE;
orp->or_got_int= TRUE;
or_check_ints(orp);
}
/*****************************************************************************
* or_send *
* *
* Send a packet, if possible *
*****************************************************************************/
static int or_send(struct netdriver_data *data, size_t size)
{
size_t p, data_len, data_off;
int err;
struct ethhdr *eh;
t_or *orp;
hermes_t *hw;
struct hermes_tx_descriptor desc;
struct header_struct hdr;
u16_t txfid;
/* We need space for the max packet size itself, plus an ethernet
* header, plus 2 bytes so we can align the IP header on a
* 32bit boundary, plus 1 byte so we can read in odd length
* packets from the card, which has an IO granularity of 16
* bits */
static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1];
orp = &or_state;
hw = &(orp->hw);
/* Switch off interrupts. The card is accessable via 2 BAPs, one for
* reading and one for writing. In theory these BAPs should be
* independent, but in practice, the are not. By switching off the
* interrupts of the card, the chances of one interfering with the
* other should be less
*/
/* ..except that this is not happening at all here. */
txfid = orp->or_tx.or_txfid;
if (orp->or_tx.ret_busy || orp->connected == 0) {
/* there is no buffer in the card available */
return SUSPEND;
}
/* Copy the data to be sent from the vector to the databuf */
netdriver_copyin(data, 0, databuf, size);
/* Zero out the rest of the buffer */
memset(&databuf[size], 0, sizeof(databuf) - size);
memset(&desc, 0, sizeof(desc));
/* Reclaim the tx buffer once the data is sent (OK), or it is clear
* that transmission failed (EX). Reclaiming means that we can reuse
* the buffer again for transmission
*/
desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
/* Actually, this reclaim bit is the only thing which needs to be set
* in the descriptor
*/
err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
if (err) {
printf("hermes_bap_pwrite() descriptor error:resetting card\n");
/* When this happens, the card is quite confused: it will not
* recover. Reset it
*/
or_reset();
return OK; /* pretend the packet was sent anyway.. */
}
eh = (struct ethhdr *) databuf;
/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > 1500) {
/* Ethernet-II frame */
data_len = size - ETH_HLEN;
data_off = HERMES_802_3_OFFSET + sizeof (hdr);
/* 802.3 header */
memcpy(hdr.dest, eh->h_dest, ETH_ALEN);
memcpy(hdr.src, eh->h_src, ETH_ALEN);
hdr.len = htons(data_len + ENCAPS_OVERHEAD);
/* 802.2 header */
memcpy(&hdr.dsap, &encaps_hdr, sizeof (encaps_hdr));
hdr.ethertype = eh->h_proto;
err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
txfid, HERMES_802_3_OFFSET);
if (err) {
printf("%s: Error %d writing packet header to BAP\n",
orp->or_name, err);
return OK; /* pretend the packet was sent anyway.. */
}
p = ETH_HLEN;
} else {
/* IEEE 802.3 frame */
data_len = size + ETH_HLEN;
data_off = HERMES_802_3_OFFSET;
p = 0;
}
/* Round up for odd length packets */
err = hermes_bap_pwrite(hw, USER_BAP, (void *) &(databuf[p]),
RUP_EVEN(data_len), txfid, data_off);
if (err) {
printf("hermes_bap_pwrite(data): error %d\n", err);
return OK; /* pretend the packet was sent anyway.. */
}
/* this should be before the docmd_wait. Cause otherwise the bit can
* be cleared in the handler (if irq's not off) before it is set
* and then 1 reset (ret_busy=false) is lost
*/
orp->or_tx.ret_busy = TRUE;
orp->or_tx_busy++;
/* Send the packet which was constructed in txfid */
err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL,
txfid, NULL);
if (err) {
printf("hermes_docmd_wait(TX|RECL): error %d\n", err);
/* Mark the buffer as available again */
orp->or_tx.ret_busy = FALSE;
orp->or_tx_busy--;
return OK; /* pretend the packet was sent anyway.. */
}
return OK;
}
/*****************************************************************************
* or_ev_info *
* *
* Process information which comes in from the card *
*****************************************************************************/
static void or_ev_info(t_or * orp)
{
struct hermes_tallies_frame tallies;
struct hermes_linkstatus linkstatus;
u16_t newstatus;
u16_t infofid;
int err, len, type;
hermes_t *hw = &orp->hw;
struct {
u16_t len;
u16_t type;
} info;
infofid = hermes_read_reg (hw, HERMES_INFOFID);
err = hermes_bap_pread (hw, IRQ_BAP, &info, sizeof (info), infofid,
0);
if (err) {
printf ("%s: error %d reading info frame.\n", orp->or_name,
err);
return;
}
len = HERMES_RECLEN_TO_BYTES (info.len);
type = info.type;
switch (type) {
case HERMES_INQ_TALLIES:
if (len > sizeof(tallies)) {
printf("%s: Tallies frame too long ", orp->or_name);
printf("(%d bytes)\n", len);
len = sizeof (tallies);
}
hermes_read_words(hw, HERMES_DATA1, (void *)&tallies, len / 2);
/* TODO: do something with the tallies structure */
break;
case HERMES_INQ_LINKSTATUS:
if (len != sizeof(linkstatus)) {
printf("%s: Unexpected size for linkstatus ",
orp->or_name);
printf("frame (%d bytes)\n", len);
}
hermes_read_words(hw, HERMES_DATA1, (void *)&linkstatus,
len / 2);
newstatus = linkstatus.linkstatus;
if ((newstatus == HERMES_LINKSTATUS_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE)) {
orp->connected = 1;
orp->or_send_int = TRUE;
orp->or_got_int = TRUE;
}
else if ((newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
|| (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED)) {
orp->connected = 0;
}
if (newstatus != orp->last_linkstatus)
print_linkstatus(orp, newstatus);
orp->last_linkstatus = newstatus;
break;
default:
printf("%s:Unknown information frame received (type %04x).\n",
orp->or_name, type);
break;
}
}
/*****************************************************************************
* or_print_linkstatus *
* *
* Process information which comes in from the card *
*****************************************************************************/
static void print_linkstatus(t_or * orp, u16_t status)
{
int err;
u16_t d;
char *s;
hermes_t *hw = &(orp->hw);
switch (status) {
case HERMES_LINKSTATUS_NOT_CONNECTED:
s = "Not Connected";
break;
case HERMES_LINKSTATUS_CONNECTED:
s = "Connected";
break;
case HERMES_LINKSTATUS_DISCONNECTED:
s = "Disconnected";
break;
case HERMES_LINKSTATUS_AP_CHANGE:
s = "AP Changed";
break;
case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
s = "AP Out of Range";
break;
case HERMES_LINKSTATUS_AP_IN_RANGE:
s = "AP In Range";
break;
case HERMES_LINKSTATUS_ASSOC_FAILED:
s = "Association Failed";
break;
default:
s = "UNKNOWN";
}
printf("%s: link status: %s, ", orp->or_name, s);
err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &d);
if (err) {
printf("error %d\n", err);
return;
}
printf("channel: %d, freq: %ld MHz\n", d, (channel_frequency[d-1]));
}
/*****************************************************************************
* or_check_ints *
* *
* Process events which have been postponed in the interrupt handler *
*****************************************************************************/
static void or_check_ints(t_or * orp)
{
if (!orp->or_got_int)
return;
orp->or_got_int = FALSE;
if (orp->or_need_reset)
or_reset();
if (orp->rx_first != orp->rx_last)
netdriver_recv();
if (orp->or_send_int)
netdriver_send();
}
/*****************************************************************************
* is_ethersnap *
* *
* is there an LLC and SNAP header in the ethernet packet? The inet task *
* isn't very interested in it... *
*****************************************************************************/
static int is_ethersnap(struct header_struct *hdr)
{
/* We de-encapsulate all packets which, a) have SNAP headers
* (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
* and where b) the OUI of the SNAP header is 00:00:00 or
* 00:00:f8 - we need both because different APs appear to use
* different OUIs for some reason */
return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0)
&& ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
}
/*****************************************************************************
* or_recv *
* *
* Receive a packet, if one is available *
*****************************************************************************/
static ssize_t or_recv(struct netdriver_data *data, size_t max)
{
t_or *orp;
u8_t *databuf;
size_t length;
orp = &or_state;
if (orp->rx_first == orp->rx_last)
return SUSPEND;
/* store the pointer to this data in databuf */
databuf = &(orp->rx_buf[orp->rx_first][0]);
length = orp->rx_length[orp->rx_first];
orp->rxfid[orp->rx_first] = NO_FID;
orp->rx_length[orp->rx_first] = 0;
/* Next time, the next buffer with data will be retrieved */
orp->rx_first++;
orp->rx_first %= NR_RX_BUFS;
if (length > max)
length = max;
netdriver_copyout(data, 0, databuf, length);
orp->or_stat.ets_packetR++;
return length;
}
/*****************************************************************************
* or_get_recvd_packet *
* *
* The card has received data. Retrieve the data from the card and put it *
* in a buffer in the driver (in the orp structure) *
*****************************************************************************/
static int or_get_recvd_packet(t_or *orp, u16_t rxfid, u8_t *databuf)
{
struct hermes_rx_descriptor desc;
hermes_t *hw;
struct header_struct hdr;
int err, length, offset;
u16_t status;
memset(databuf, 0, IEEE802_11_FRAME_LEN);
hw = &(orp->hw);
/* Read the data from the buffer in the card which holds the data.
* First get the descriptor which will tell us whether the packet is
* healthy*/
err = hermes_bap_pread (hw, IRQ_BAP, &desc, sizeof (desc), rxfid, 0);
if (err) {
printf("Orinoco: error %d reading Rx descriptor. "
"Frame dropped\n", err);
orp->or_stat.ets_recvErr++;
return -1;
}
status = desc.status;
if (status & HERMES_RXSTAT_ERR) {
if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
printf("Error reading Orinoco Rx descriptor.Dropped");
} else {
orp->or_stat.ets_CRCerr++;
printf("Orinoco: Bad CRC on Rx. Frame dropped\n");
}
orp->or_stat.ets_recvErr++;
return -1;
}
/* For now we ignore the 802.11 header completely, assuming
that the card's firmware has handled anything vital. The only
thing we want to know is the length of the received data */
err = hermes_bap_pread (hw, IRQ_BAP, &hdr, sizeof (hdr),
rxfid, HERMES_802_3_OFFSET);
if (err) {
printf("Orinoco: error %d reading frame header. "
"Frame dropped\n", err);
orp->or_stat.ets_recvErr++;
return -1;
}
length = ntohs (hdr.len);
/* Sanity checks */
if (length < 3) {
/* No for even an 802.2 LLC header */
printf("Orinoco: error in frame length: length = %d\n",
length);
/* orp->or_stat.ets_recvErr++; */
return -1;
}
if (length > IEEE802_11_DATA_LEN) {
printf("Orinoco: Oversized frame received (%d bytes)\n",
length);
orp->or_stat.ets_recvErr++;
return -1;
}
length += sizeof (struct ethhdr);
offset = HERMES_802_3_OFFSET;
/* Read the interesting parts of the data to the drivers memory. This
* would be everything from the 802.3 layer and up */
err = hermes_bap_pread (hw,
IRQ_BAP, (void *) databuf, RUP_EVEN (length),
rxfid, offset);
if (err) {
printf("Orinoco: error doing hermes_bap_pread()\n");
orp->or_stat.ets_recvErr++;
return -1;
}
/* Some types of firmware give us the SNAP and OUI headers. Remove
* these.
*/
if (is_ethersnap(&hdr)) {
length -= 8;
memcpy(databuf + ETH_ALEN * 2,
databuf + sizeof(struct header_struct) - 2,
length - ETH_ALEN * 2);
}
if(length<60) length=60;
return length;
}
/*****************************************************************************
* or_stat *
* *
* Return the statistics structure. The statistics aren't updated until now, *
* so this won't return much interesting yet. *
*****************************************************************************/
static void or_stat(eth_stat_t *stat)
{
memcpy(stat, &or_state.or_stat, sizeof(*stat));
}