minix/drivers/orinoco/orinoco.c
Arun Thomas 3b37103fa3 Make the rtl8139 and orinoco drivers handle the system shutdown case
like other drivers. Also, some minor cleanups.
2009-04-22 12:42:37 +00:00

2380 lines
70 KiB
C
Executable file

/*
* 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>
*
* * The valid messages and their parameters are:
*
* m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR DL_GRANT
* |------------+----------+---------+----------+---------+---------+---------|
* | HARDINT | | | | | | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_WRITE | port nr | proc nr | count | mode | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_WRITEV | port nr | proc nr | count | mode | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_WRITEV_S| port nr | proc nr | count | mode | | grant |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_READ | port nr | proc nr | count | | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_READV | port nr | proc nr | count | | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_READV_S | port nr | proc nr | count | | | grant |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_CONF | port nr | proc nr | | mode | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_GETSTAT | port nr | proc nr | | | address | |
* |------------|----------|---------|----------|---------|---------|---------|
* |DL_GETSTAT_S| port nr | proc nr | | | | grant |
* |------------|----------|---------|----------|---------|---------|---------|
* | DL_STOP | port_nr | | | | | |
* |------------|----------|---------|----------|---------|---------|---------|
*
* The messages sent are:
*
* m_type DL_PORT DL_PROC DL_COUNT DL_STAT DL_CLCK
* |------------|----------|---------|----------|---------|---------|
* |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock |
* |------------|----------|---------|----------|---------|---------|
*
* m_type m3_i1 m3_i2 m3_ca1
* |------------|---------|-----------|---------------|
* |DL_CONF_REPL| port nr | last port | ethernet addr |
* |------------|---------|-----------|---------------|
*
* m_type DL_PORT DL_STAT
* |------------|---------|-----------|
* |DL_STAT_REPL| port nr | err |
* |------------|---------|-----------|
*
*/
#include "../drivers.h"
#include <string.h>
#include <stddef.h>
#include <minix/keymap.h>
#include <minix/syslib.h>
#include <minix/type.h>
#include <minix/sysutil.h>
#include <timers.h>
#include <sys/ioc_memory.h>
#include <ibm/pci.h>
#include <minix/ds.h>
#include "../../kernel/const.h"
#include "../../kernel/config.h"
#include "../../kernel/type.h"
#define tmra_ut timer_t
#define tmra_inittimer(tp) tmr_inittimer(tp)
#define VERBOSE 1 /* display message during init */
PRIVATE struct pcitab {
u16_t vid;
u16_t did;
int checkclass;
} pcitab[]=
{
{ 0x1260, 0x3873, 0 },
{ 0x1186, 0x1300, 0 },
{ 0x0000, 0x0000, 0 }
};
static tmra_ut or_watchdog;
#include <stdio.h>
#include <stdlib.h>
#include <minix/com.h>
#include <minix/portio.h>
#include <net/hton.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include <sys/vm_i386.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "assert.h"
#include "hermes.h"
#include "hermes_rid.h"
#include "orinoco.h"
#define ERR -1
#define debug 0
#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 int or_nr_task = ANY;
static t_or or_table[OR_PORT_NR];
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 */
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]))
_PROTOTYPE (static void or_writev, (message * mp, int from_int, int vectored));
_PROTOTYPE (static void or_readv, (message * mp, int from_int, int vectored));
_PROTOTYPE (static void or_writev_s, (message * mp, int from_int));
_PROTOTYPE (static void or_readv_s, (message * mp, int from_int));
_PROTOTYPE (static void reply, (t_or * orp, int err, int may_block));
_PROTOTYPE (static int or_probe, (t_or *));
_PROTOTYPE (static void or_ev_info, (t_or *));
_PROTOTYPE (static void or_init, (message *));
_PROTOTYPE (static void or_pci_conf, (void));
_PROTOTYPE (static void or_init_struct, (t_or *));
_PROTOTYPE (static void map_hw_buffer, (t_or *));
_PROTOTYPE (static void or_init_hw, (t_or *));
_PROTOTYPE (static void or_check_ints, (t_or *));
_PROTOTYPE (static void or_writerids, (hermes_t *, t_or *));
_PROTOTYPE (static void or_readrids, (hermes_t *, t_or *));
_PROTOTYPE (static void or_rec_mode, (t_or *));
_PROTOTYPE (static void mess_reply, (message *, message *));
_PROTOTYPE (static u32_t or_get_bar, (int devind, t_or * orp));
_PROTOTYPE (static void or_getstat, (message * mp));
_PROTOTYPE (static void or_getstat_s, (message * mp));
_PROTOTYPE (static void print_linkstatus, (t_or * orp, u16_t status));
_PROTOTYPE (static int or_get_recvd_packet, (t_or *orp, u16_t rxfid,
u8_t *databuf));
_PROTOTYPE (static void orinoco_stop, (void));
_PROTOTYPE (static void or_reset, (void));
_PROTOTYPE (static void or_watchdog_f, (timer_t *tp) );
_PROTOTYPE (static void setup_wepkey, (t_or *orp, char *wepkey0) );
_PROTOTYPE (static void or_getstat, (message *m));
_PROTOTYPE (static int do_hard_int, (void));
_PROTOTYPE (static void check_int_events, (void));
_PROTOTYPE (static void or_getname, (message *m));
_PROTOTYPE (static int or_handler, (t_or *orp));
_PROTOTYPE (static void or_dump, (message *m));
/* The message used in the main loop is made global, so that rl_watchdog_f()
* can change its message type to fake a HARD_INT message.
*/
PRIVATE message m;
PRIVATE int int_event_check; /* set to TRUE if events arrived */
u32_t system_hz;
static char *progname;
extern int errno;
/*****************************************************************************
* main *
* *
* *
* The main function of the driver, receiving and processing messages *
*****************************************************************************/
int main(int argc, char *argv[]) {
int fkeys, sfkeys, r, i, ret;
u32_t inet_proc_nr;
long v = 0;
t_or *orp;
system_hz = sys_hz();
(progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]);
env_setargs(argc, argv);
/* 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);
/* Try to notify INET that we are present (again). If INET cannot
* be found, assume this is the first time we started and INET is
* not yet alive. */
r = ds_retrieve_u32("inet", &inet_proc_nr);
if (r == OK)
notify(inet_proc_nr);
else if (r != ESRCH)
printf("orinoco: ds_retrieve_u32 failed for 'inet': %d\n", r);
while (TRUE) {
if ((r = receive (ANY, &m)) != OK)
panic(__FILE__, "orinoco: receive failed", NO_NUM);
switch (m.m_type) {
case DEV_PING:
notify(m.m_source);
break;
case DL_WRITEV:
or_writev (&m, FALSE, TRUE);
break;
case DL_WRITEV_S:
or_writev_s (&m, FALSE);
break;
case DL_WRITE:
or_writev (&m, FALSE, FALSE);
break;
case DL_READ:
or_readv (&m, FALSE, FALSE);
break;
case DL_READV:
or_readv (&m, FALSE, TRUE);
break;
case DL_READV_S:
or_readv_s (&m, FALSE);
break;
case DL_CONF:
or_init (&m);
break;
case DL_GETSTAT:
or_getstat (&m);
break;
case DL_GETSTAT_S:
or_getstat_s (&m);
break;
case DL_GETNAME:
or_getname(&m);
break;
case SYN_ALARM:
or_watchdog_f(NULL);
break;
case SYS_SIG:
{
sigset_t sigset = m.NOTIFY_ARG;
if ( sigismember( &sigset, SIGKSTOP ) )
orinoco_stop();
}
break;
case HARD_INT:
do_hard_int();
if (int_event_check)
check_int_events();
break ;
case FKEY_PRESSED:
or_dump(&m);
break;
case PROC_EVENT:
break;
default:
panic(__FILE__,"orinoco: illegal message:", m.m_type);
}
}
}
/*****************************************************************************
* check_int_events *
* *
* If a hard interrupt message came in, call the or_check_ints for the right *
* card *
*****************************************************************************/
static void check_int_events(void) {
int i;
t_or *orp;
/* the HARD_INT message doesn't contain information about the port, try
* to find it */
for (orp = or_table;
orp < or_table + OR_PORT_NR; orp++) {
if (orp->or_mode != OR_M_ENABLED)
continue;
if (!orp->or_got_int)
continue;
orp->or_got_int = 0;
assert (orp->or_flags & OR_F_ENABLED);
or_check_ints (orp);
}
}
/****************************************************************************
* or_getname *
* *
* Gets the drivers name, orinoco *
****************************************************************************/
static void or_getname(message *mp) {
int r;
strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME));
mp->DL_NAME[sizeof(mp->DL_NAME) - 1] = '\0';
mp->m_type = DL_NAME_REPLY;
r = send(mp->m_source, mp);
if(r != OK) {
panic(__FILE__, "or_getname: send failed", r);
}
}
/*****************************************************************************
* do_hard_int *
* *
* Process the interrupts which the card generated *
*****************************************************************************/
static int do_hard_int(void) {
u16_t evstat;
hermes_t *hw;
int i,s;
t_or *orp;
for (i=0; i < OR_PORT_NR; i ++) {
/* Run interrupt handler at driver level. */
or_handler( &or_table[i]);
/* Reenable interrupts for this hook. */
if ((s=sys_irqenable(&or_table[i].or_hook_id)) != OK) {
printf("orinoco: error, couldn't enable");
printf(" interrupts: %d\n", s);
}
}
}
/*****************************************************************************
* orinoco_stop *
* *
* Stops the card. The signal to the card itself is not implemented yet. *
*****************************************************************************/
static void orinoco_stop () {
int i;
t_or *orp;
for (i= 0, orp= &or_table[0]; i<OR_PORT_NR; i++, orp++) {
if (orp->or_mode != OR_M_ENABLED)
continue;
/* TODO: send a signal to the card to shut it down */
}
sys_exit(0);
}
/*****************************************************************************
* or_reset *
* *
* Sometime the card gets screwed, behaving erratically. Solution: reset the *
* card. This is actually largely redoing the initialization *
*****************************************************************************/
static void or_reset() {
static clock_t last_reset, now;
t_or *orp;
int i, j, r;
u16_t irqmask;
hermes_t *hw = &(orp->hw);
if (OK != (r = getuptime(&now)))
panic(__FILE__, "orinoco: getuptime() failed:", r);
if(now - last_reset < system_hz * 10) {
printf("Resetting card too often. Going to reset driver\n");
exit(1);
}
for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) {
if(orp->or_mode == OR_M_DISABLED)
printf("orinoco port %d is disabled\n", i);
if(orp->or_mode != OR_M_ENABLED) {
continue;
}
orp->or_need_reset = 0;
or_init_hw(orp);
orp->rx_last = orp->rx_first = 0;
for(j = 0; j < NR_RX_BUFS; j++) {
orp->rx_length[0] = 0;
}
if(orp->or_flags & OR_F_SEND_AVAIL) {
orp->or_tx.ret_busy = FALSE;
orp->or_send_int = TRUE;
}
}
last_reset = now;
}
/*****************************************************************************
* or_dump *
* *
* Dump interesting information about the card on F-key pressed. *
* Not implemented yet *
*****************************************************************************/
static void or_dump (message *m) {
t_or *orp;
int i, err;
u16_t evstat =0, d;
hermes_t *hw;
for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) {
if(orp->or_mode == OR_M_DISABLED) {
printf("%s is disabled\n", orp->or_name);
}
if(orp->or_mode != OR_M_ENABLED)
continue;
m->m_type = FKEY_CONTROL;
m->FKEY_REQUEST = FKEY_EVENTS;
if(OK!=(sendrec(TTY_PROC_NR,m)) )
printf("Contacting the TTY failed\n");
if(bit_isset(m->FKEY_SFKEYS, 11)) {
print_linkstatus(orp, orp->last_linkstatus);
}
}
}
/*****************************************************************************
* or_init *
* *
* The main initialization function, called when a DL_INIT message comes in. *
*****************************************************************************/
static void or_init (message * mp) {
int port, err, i;
t_or *orp;
message reply;
static int first_time = 1;
hermes_t *hw;
clock_t t0,t1;
if (first_time) {
first_time = 0;
or_pci_conf (); /* Configure PCI devices. */
tmra_inittimer(&or_watchdog);
/* Use a synchronous alarm instead of a watchdog timer. */
sys_setalarm(system_hz, 0);
}
port = mp->DL_PORT;
if (port < 0 || port >= OR_PORT_NR) {
/* illegal port in message */
reply.m_type = DL_CONF_REPLY;
reply.m3_i1 = ENXIO;
mess_reply (mp, &reply);
return;
}
/* the port resolves to the main orinoco structure */
orp = &or_table[port];
/* resolving to the main hardware structure */
hw = &(orp->hw);
if (orp->or_mode == OR_M_DISABLED) {
/* Initialize the orp structure */
or_init_struct (orp);
if (orp->or_mode == OR_M_DISABLED) {
reply.m_type = DL_CONF_REPLY;
reply.m3_i1 = ENXIO;
mess_reply (mp, &reply);
return;
}
if (orp->or_mode == OR_M_ENABLED) {
/* initialize card, hardware/firmware */
orp->or_flags |= OR_F_ENABLED;
or_init_hw (orp);
}
}
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
/* Not supported by the driver yet, but set a couple of options:
* multicasting, promiscuity, broadcasting, depending on the users
* needs */
orp->or_flags &= ~(OR_F_PROMISC | OR_F_MULTI | OR_F_BROAD);
if (mp->DL_MODE & DL_PROMISC_REQ)
orp->or_flags |= OR_F_PROMISC;
if (mp->DL_MODE & DL_MULTI_REQ)
orp->or_flags |= OR_F_MULTI;
if (mp->DL_MODE & DL_BROAD_REQ)
orp->or_flags |= OR_F_BROAD;
orp->or_client = mp->m_source;
or_rec_mode (orp);
/* reply the caller that the configuration succeeded */
reply.m_type = DL_CONF_REPLY;
reply.m3_i1 = mp->DL_PORT;
reply.m3_i2 = OR_PORT_NR;
*(ether_addr_t *) reply.m3_ca1 = orp->or_address;
mess_reply (mp, &reply);
}
/*****************************************************************************
* or_pci_conf *
* *
* Configure the pci related issues of the card, e.g. finding out where the *
* card is in the pci configuration, it's assigned irq, etc. This can be *
* done if the boot monitor is provided with information, or the pci bus *
* can be searched (at the end: or_probe function) *
*****************************************************************************/
static void or_pci_conf () {
long v;
t_or *orp;
int i, h;
static char envfmt[] = "*:d.d.d";
static char envvar[] = OR_ENVVAR "#";
static char val[128];
/* extract information from the boot monitor about the pci
* configuration if provided */
for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++) {
strncpy (orp->or_name, OR_NAME, sizeof(OR_NAME));
orp->or_name[sizeof(OR_NAME) - 2] = i + '0';
orp->or_seen = FALSE;
/* whats this envvar; whats the definition;*/
/* i guess this whole loop could be removed*/
envvar[sizeof (OR_ENVVAR) - 1] = '0' + i;
if (0 == env_get_param(envvar, val, sizeof(val)) &&
! env_prefix(envvar, "pci")) {
env_panic(envvar);
}
v = 0;
(void) env_parse (envvar, envfmt, 1, &v, 0, 255);
orp->or_pci_bus = v;
v = 0;
(void) env_parse (envvar, envfmt, 2, &v, 0, 255);
orp->or_pci_dev = v;
v = 0;
(void) env_parse (envvar, envfmt, 3, &v, 0, 255);
orp->or_pci_func = v;
}
/* Initialize the pci bus, bridges and cards, if not yet done */
pci_init ();
/* Try to find out where the card(s) are in the pci bus */
for (h = 1; h >= 0; h--)
for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++) {
if (((orp->or_pci_bus | orp->or_pci_dev |
orp->or_pci_func) != 0) != h) {
continue;
}
if (or_probe (orp))
orp->or_seen = TRUE;
}
}
/*****************************************************************************
* 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) {
u8_t ilr;
u32_t bar, reg, cpuspace_bar;
char *dname;
u16_t vid, did;
int i, r, devind, just_one;
if ((orp->or_pci_bus | orp->or_pci_dev | orp->or_pci_func) != 0) {
/* The monitor has provided us with clues about where the
* device is. Try to find it at that place */
r = pci_find_dev (orp->or_pci_bus, orp->or_pci_dev,
orp->or_pci_func, &devind);
if (r == 0) {
printf ("%s: no PCI found at %d.%d.%d\n",
orp->or_name, orp->or_pci_bus,
orp->or_pci_dev, orp->or_pci_func);
return (0);
}
/* get the information about the card, vendor id and device
* id */
pci_ids (devind, &vid, &did);
just_one = TRUE;
} else {
/* no clue where the card is. Start looking from the
* beginning */
r = pci_first_dev (&devind, &vid, &did);
if (r == 0)
return (0);
just_one = FALSE;
}
while (TRUE) {
/* loop through the pcitab to find a maching entry. The match
* being between one of the values in pcitab and the
* information provided by the pci bus */
for (i = 0; pcitab[i].vid != 0; i++) {
if (pcitab[i].vid != vid)
continue;
if (pcitab[i].did != did)
continue;
if (pcitab[i].checkclass) {
panic(__FILE__, "or_probe:class check not implmnted",
NO_NUM);
}
/* we have found the card in the pci bus */
break;
}
if (pcitab[i].vid != 0)
break;
if (just_one) {
printf ("%s: wrong PCI device", orp->or_name);
printf (" (%04x/%04x) found at %d.%d.%d\n", vid, did,
orp->or_pci_bus, orp->or_pci_dev,
orp->or_pci_func);
return (0);
}
/* if the pci device which was under consideration was not
* of the desired brand or type, get the next device */
r = pci_next_dev (&devind, &vid, &did);
if (!r)
return (0);
}
/* 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);
orp->devind = devind;
/* Get the irq */
ilr = pci_attr_r8 (devind, PCI_ILR);
orp->or_irq = ilr;
/* Get the base address */
bar = or_get_bar (devind, orp);
orp->or_base_port = bar;
map_hw_buffer(orp);
return TRUE;
}
/*****************************************************************************
* map_hw_buffer *
* *
* Map the memory mapped registers into user space memory *
*****************************************************************************/
static void map_hw_buffer(t_or *orp) {
int r;
size_t o, size, reg_size;
char *buf, *abuf;
hermes_t *hw = &(orp->hw);
/* This way, the buffer will be at least I386_PAGE_SIZE big: see
* calculation with the offset */
size = 2 * I386_PAGE_SIZE;
buf = (char *)malloc(size);
if(buf == NULL)
panic(__FILE__, "map_hw_buffer: cannot malloc size:", size);
/* Let the mapped memory by I386_PAGE_SIZE aligned */
o = I386_PAGE_SIZE - ((vir_bytes)buf % I386_PAGE_SIZE);
abuf = buf + o;
#if 0
r = sys_vm_map(SELF, 1, (vir_bytes)abuf,
1 * I386_PAGE_SIZE, (phys_bytes)orp->or_base_port);
#else
r = ENOSYS;
#endif
if(r!=OK)
panic(__FILE__, "map_hw_buffer: sys_vm_map failed:", r);
hw->locmem = abuf;
}
/*****************************************************************************
* 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, desired_bar;
int is_iospace, i;
u16_t check, check2;
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(__FILE__,"base address isn't properly configured",
NO_NUM);
/* 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 (debug) {
printf ("%s: using I/O space address 0x%lx, IRQ %d\n",
orp->or_name, bar, orp->or_irq);
}
panic(__FILE__, "Not implemente yet", NO_NUM);
/* 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 (debug){
printf ("%s: using shared memory address",
orp->or_name);
printf (" 0x%lx, IRQ %d\n", bar, orp->or_irq);
}
return bar;
}
}
/*****************************************************************************
* or_init_struct *
* *
* Set the orinoco structure to default values *
*****************************************************************************/
static void or_init_struct (t_or * orp) {
int i = 0;
static eth_stat_t empty_stat = { 0, 0, 0, 0, 0, 0 };
orp->or_mode = OR_M_DISABLED;
if (orp->or_seen)
orp->or_mode = OR_M_ENABLED;
if (orp->or_mode != OR_M_ENABLED)
return;
orp->or_got_int = 0;
orp->or_link_up = -1;
orp->or_send_int = 0;
orp->or_clear_rx = 0;
orp->or_tx_alive = 0;
orp->or_need_reset = 0;
orp->or_read_s = 0;
orp->or_tx_head = 0;
orp->or_tx_tail = 0;
orp->connected = 0;
orp->or_tx.ret_busy = FALSE;
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;
}
orp->rx_current = 0;
orp->rx_first = 0;
orp->rx_last = 0;
orp->or_stat = empty_stat;
orp->or_flags = OR_F_EMPTY;
/* 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_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 *
*****************************************************************************/
static void or_init_hw (t_or * orp) {
int i, 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)) {
printf ("error value of hermes_init(): %d\n", err);
}
/* Get the MAC address (which is a data item in the card)*/
or_readrids (hw, orp);
/* Write a few rids to the card, e.g. WEP key*/
or_writerids (hw, orp);
if (debug) {
printf ("%s: Ethernet address ", orp->or_name);
for (i = 0; i < 6; i++) {
printf ("%x%c", orp->or_address.ea_addr[i],
i < 5 ? ':' : '\n');
}
}
/* 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) {
int err, len, i;
struct hermes_idstring nickbuf;
u16_t reclen, d;
/* Read the MAC address */
err = hermes_read_ltv (hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
ETH_ALEN, NULL, &orp->or_address);
if (err) {
printf ("%s: failed to read MAC address!\n", orp->or_name);
return;
}
}
/*****************************************************************************
* 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;
u16_t port_type, max_data_len, reclen;
static char essid[IW_ESSID_MAX_SIZE + 1];
static char wepkey0[LARGE_KEY_LENGTH + 1];
/* Set the MAC port */
port_type = 1;
err = hermes_write_wordrec (hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
port_type);
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_rec_mode *
* *
* Set the desired receive mode, e.g. promiscuous mode. Not implemented yet *
*****************************************************************************/
static void or_rec_mode (t_or * orp) {
/* TODO */
}
/*****************************************************************************
* 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 int or_handler (t_or *orp) {
int i, err, length, nr = 0;
u16_t evstat, events, fid;
hermes_t *hw;
struct hermes_tx_descriptor desc;
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");
}
orp->or_tx.ret_busy = FALSE;
if(orp->or_flags & OR_F_SEND_AVAIL) {
orp->or_send_int = TRUE;
if (!orp->or_got_int){
orp->or_got_int = TRUE;
int_event_check = 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");
}
orp->or_tx.ret_busy = FALSE;
if(orp->or_flags & OR_F_SEND_AVAIL) {
orp->or_send_int = TRUE;
if (!orp->or_got_int){
orp->or_got_int = TRUE;
int_event_check = 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");
return ;
}
/* To be able to detect illegal fids */
hermes_write_reg(hw, HERMES_ALLOCFID, 0xFFFF);
events &= ~(HERMES_EV_ALLOC);
}
/* Occurs when a frame is received by the asynchronous reception
* process */
if (events & HERMES_EV_RX) {
orp->or_ev_rx = TRUE;
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;
if (!orp->or_got_int){
orp->or_got_int = TRUE;
}
int_event_check = 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;
}
return (1);
}
/*****************************************************************************
* or_watchdog_f *
* *
* Will be called regularly to see whether the driver has crashed. If that *
* condition is detected, reset the driver and card *
*****************************************************************************/
static void or_watchdog_f(timer_t *tp) {
int i;
t_or *orp;
/* Use a synchronous alarm instead of a watchdog timer. */
sys_setalarm(system_hz, 0);
for (i= 0, orp = &or_table[0]; i<OR_PORT_NR; i++, orp++) {
if (orp->or_mode != OR_M_ENABLED)
continue;
if (!(orp->or_flags & OR_F_SEND_AVAIL)) {
/* Assume that an idle system is alive */
orp->or_tx_alive= TRUE;
continue;
}
if (orp->connected == 0) {
orp->or_tx_alive= TRUE;
continue;
}
if (orp->or_tx_alive) {
orp->or_tx_alive= FALSE;
continue;
}
printf("or_watchdog_f: resetting port %d\n", i);
orp->or_need_reset= TRUE;
orp->or_got_int= TRUE;
check_int_events();
}
}
/*****************************************************************************
* mess_reply *
*****************************************************************************/
static void mess_reply (message * req, message * reply_mess) {
if (send (req->m_source, reply_mess) != 0)
panic(__FILE__, "orinoco: unable to mess_reply", NO_NUM);
}
/*****************************************************************************
* or_writev *
* *
* As far as we can see, this function is never called from 3.1.3. However, *
* it is still in rtl8139, so we'll keep it here as well. It's almost a copy *
* of or_writev_s. We left out the comments. For an explanation, see *
* or_writev_s *
******************************************************************************/
static void or_writev (message * mp, int from_int, int vectored) {
int port, or_client, count, size, err, data_len, data_off, tx_head;
int o, j, n, i, s, p, cps ;
struct ethhdr *eh;
t_or *orp;
clock_t timebefore, t0;
phys_bytes phys_user, iov_src;
hermes_t *hw;
struct hermes_tx_descriptor desc;
struct header_struct hdr;
iovec_t *iovp;
phys_bytes phys_databuf;
u16_t txfid;
static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1];
memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3);
port = mp->DL_PORT;
count = mp->DL_COUNT;
if (port < 0 || port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port", NO_NUM);
or_client = mp->DL_PROC;
orp = &or_table[port];
orp->or_client = or_client;
hw = &(orp->hw);
if (from_int) {
assert (orp->or_flags & OR_F_SEND_AVAIL);
orp->or_flags &= ~OR_F_SEND_AVAIL;
orp->or_send_int = FALSE;
orp->or_tx_alive = TRUE;
}
if (orp->or_tx.ret_busy) {
assert(!(orp->or_flags & OR_F_SEND_AVAIL));
orp->or_flags |= OR_F_SEND_AVAIL;
goto suspend_write;
}
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
if (vectored) {
int iov_offset = 0;
size = 0;
o = 0;
for (i = 0; i < count; i += IOVEC_NR,
iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]),
iov_offset += IOVEC_NR * sizeof (orp->or_iovec[0])) {
n = IOVEC_NR;
if (i + n > count)
n = count - i;
cps = sys_vircopy(or_client, D,
((vir_bytes) mp->DL_ADDR) + iov_offset,
SELF, D, (vir_bytes) orp->or_iovec,
n * sizeof(orp->or_iovec[0]));
if (cps != OK) printf("sys_vircopy failed: %d\n", cps);
for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) {
s = iovp->iov_size;
if (size + s > ETH_MAX_PACK_SIZE_TAGGED) {
printf("invalid packet size\n");
}
cps = sys_vircopy(or_client, D, iovp->iov_addr,
SELF, D, (vir_bytes) databuf + o, s);
if (cps != OK)
printf("sys_vircopy failed: %d\n",cps);
size += s;
o += s;
}
}
if (size < ETH_MIN_PACK_SIZE)
printf("invalid packet size %d\n", size);
} else {
size = mp->DL_COUNT;
if (size < ETH_MIN_PACK_SIZE
|| size > ETH_MAX_PACK_SIZE_TAGGED)
printf("invalid packet size %d\n", size);
cps = sys_vircopy(or_client, D, (vir_bytes)mp->DL_ADDR,
SELF, D, (vir_bytes) databuf, size);
if (cps != OK) printf("sys_abscopy failed: %d\n", cps);
}
memset (&desc, 0, sizeof (desc));
desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
err = hermes_bap_pwrite (hw, USER_BAP, &desc, sizeof (desc), txfid,
0);
if (err) {
or_reset();
goto fail;
}
eh = (struct ethhdr *) databuf;
if (ntohs (eh->h_proto) > 1500) {
data_len = size - ETH_HLEN;
data_off = HERMES_802_3_OFFSET + sizeof (hdr);
memcpy (hdr.dest, eh->h_dest, ETH_ALEN);
memcpy (hdr.src, eh->h_src, ETH_ALEN);
hdr.len = htons (data_len + ENCAPS_OVERHEAD);
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);
goto fail;
}
p = ETH_HLEN;
} else {
data_len = size + ETH_HLEN;
data_off = HERMES_802_3_OFFSET;
p = 0;
}
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);
goto fail;
}
orp->or_tx.ret_busy = TRUE;
err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL,
txfid, NULL);
if (err) {
orp->or_tx.ret_busy = FALSE;
printf ("hermes_docmd_wait(TX|RECL): error %d\n", err);
goto fail;
}
fail:
orp->or_flags |= OR_F_PACK_SENT;
if (from_int) {
return;
}
reply (orp, OK, FALSE);
return;
suspend_write:
orp->or_tx_mess = *mp;
reply (orp, OK, FALSE);
return;
}
/*****************************************************************************
* or_writev_s *
* *
* Write data which is denoted by the message to the card and send it. *
*****************************************************************************/
static void or_writev_s (message * mp, int from_int) {
int port, or_client, count, size, err, data_len, data_off, tx_head;
int o, j, n, i, s, p, cps ;
struct ethhdr *eh;
t_or *orp;
clock_t timebefore, t0;
phys_bytes phys_user, iov_src;
hermes_t *hw;
struct hermes_tx_descriptor desc;
int iov_offset = 0;
struct header_struct hdr;
iovec_s_t *iovp;
phys_bytes phys_databuf;
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];
memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3);
port = mp->DL_PORT;
count = mp->DL_COUNT;
if (port < 0 || port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port", NO_NUM);
or_client = mp->DL_PROC;
orp = &or_table[port];
orp->or_client = or_client;
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 */
if (from_int){
/* We were called with from_int, meaning that the last time we
* were called, no tx buffers were available, and we had to
* suspend. Now, we'll try again to find an empty buffer in the
* card */
assert (orp->or_flags & OR_F_SEND_AVAIL);
orp->or_flags &= ~OR_F_SEND_AVAIL;
orp->or_send_int = FALSE;
orp->or_tx_alive = TRUE;
}
txfid = orp->or_tx.or_txfid;
if (orp->or_tx.ret_busy || orp->connected == 0) {
/* there is no buffer in the card available */
assert(!(orp->or_flags & OR_F_SEND_AVAIL));
/* Remember that there is a packet to be sent available */
orp->or_flags |= OR_F_SEND_AVAIL;
goto suspend_write_s;
}
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
/* Copy the data to be send from the vector to the databuf */
size = 0;
o = 0;
for (i = 0; i < count; i += IOVEC_NR,
iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]),
iov_offset += IOVEC_NR * sizeof (orp->or_iovec_s[0])) {
n = IOVEC_NR;
if (i + n > count)
n = count - i;
cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset,
(vir_bytes) orp->or_iovec_s,
n * sizeof(orp->or_iovec_s[0]), D);
if (cps != OK)
printf("orinoco: sys_safecopyfrom failed: %d\n", cps);
for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++) {
s = iovp->iov_size;
if (size + s > ETH_MAX_PACK_SIZE_TAGGED) {
printf("Orinoco: invalid pkt size\n");
}
cps = sys_safecopyfrom(or_client, iovp->iov_grant, 0,
(vir_bytes) databuf + o, s, D);
if (cps != OK)
printf("orinoco: sys_safecopyfrom failed:%d\n",
cps);
size += s;
o += s;
}
}
assert(size >= ETH_MIN_PACK_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();
goto fail;
}
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);
goto fail;
}
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);
goto fail;
}
/* 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;
/* 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;
goto fail;
}
fail:
/* If the interrupt handler called, don't send a reply. The reply
* will be sent after all interrupts are handled.
*/
orp->or_flags |= OR_F_PACK_SENT;
if (from_int) {
return;
}
reply (orp, OK, FALSE);
return;
suspend_write_s:
orp->or_tx_mess = *mp;
reply (orp, OK, FALSE);
return;
}
/*****************************************************************************
* reply *
* *
* Send a message back to the caller, informing it about the data received *
* or sent *
*****************************************************************************/
static void reply (t_or * orp, int err, int may_block) {
message reply;
int status = 0, r;
clock_t now;
if (orp->or_flags & OR_F_PACK_SENT)
status |= DL_PACK_SEND;
if (orp->or_flags & OR_F_PACK_RECV)
status |= DL_PACK_RECV;
reply.m_type = DL_TASK_REPLY;
reply.DL_PORT = orp - or_table;
assert(reply.DL_PORT == 0);
reply.DL_PROC = orp->or_client;
reply.DL_STAT = status | ((u32_t) err << 16);
reply.DL_COUNT = orp->or_read_s;
if (OK != (r = getuptime(&now)))
panic(__FILE__, "orinoco: getuptime() failed:", r);
reply.DL_CLCK = now;
r = send (orp->or_client, &reply);
if (r == ELOCKED && may_block) {
return;
}
if (r < 0)
panic(__FILE__, "orinoco: send failed:", r);
orp->or_read_s = 0;
orp->or_flags &= ~(OR_F_PACK_SENT | OR_F_PACK_RECV);
}
/*****************************************************************************
* or_ev_info *
* *
* Process information which comes in from the card *
*****************************************************************************/
static void or_ev_info (t_or * orp) {
u16_t infofid;
int err, len, type, i;
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:
{
struct hermes_tallies_frame 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: {
u16_t newstatus;
struct hermes_linkstatus 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;
if(orp->or_flags & OR_F_SEND_AVAIL) {
orp->or_send_int = TRUE;
orp->or_got_int = TRUE;
int_event_check = 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 ("%s: Error %d \n", orp->or_name, err);
return;
}
printf("channel: %d, freq: %d MHz ",
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) {
int or_flags;
hermes_t *hw = &orp->hw;
if (orp->or_need_reset)
or_reset();
if ((orp->rx_first!=orp->rx_last) && (orp->or_flags & OR_F_READING)) {
orp->or_ev_rx = 0;
if (orp->or_rx_mess.m_type == DL_READV) {
or_readv (&orp->or_rx_mess, TRUE, TRUE);
} else if(orp->or_rx_mess.m_type == DL_READV_S) {
or_readv_s (&orp->or_rx_mess, TRUE);
} else {
assert(orp->or_rx_mess.m_type == DL_READ);
or_readv (&orp->or_rx_mess, TRUE, FALSE);
}
}
if (orp->or_send_int) {
if (orp->or_tx_mess.m_type == DL_WRITEV) {
or_writev (&orp->or_tx_mess, TRUE, TRUE);
}
else if(orp->or_tx_mess.m_type == DL_WRITEV_S) {
or_writev_s (&orp->or_tx_mess, TRUE);
} else {
assert(orp->or_tx_mess.m_type == DL_WRITE);
or_writev (&orp->or_tx_mess, TRUE, FALSE);
}
}
if (orp->or_flags & (OR_F_PACK_SENT | OR_F_PACK_RECV)) {
reply (orp, OK, TRUE);
}
}
/*****************************************************************************
* 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_readv *
* *
* As far as we can see, this function is never called from 3.1.3. However, *
* it is still in rtl8139, so we'll keep it here as well. It's almost a copy *
* of or_readv_s. We left out the comments. For an explanation, see *
* or_readv_s *
*****************************************************************************/
static void or_readv (message * mp, int from_int, int vectored) {
int i, j, n, o, s, s1, dl_port, or_client, count, size, err, yep, cps;
port_t port;
clock_t timebefore;
unsigned amount, totlen, packlen;
struct hermes_rx_descriptor desc;
phys_bytes dst_phys, iov_src;
u16_t d_start, d_end, rxfid, status;
struct header_struct hdr;
int length, offset;
u32_t l, rxstat;
struct ethhdr *eh;
struct header_struct *h;
t_or *orp;
hermes_t *hw;
iovec_t *iovp;
u8_t *databuf;
dl_port = mp->DL_PORT;
count = mp->DL_COUNT;
if (dl_port < 0 || dl_port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port:", dl_port);
orp = &or_table[dl_port];
or_client = mp->DL_PROC;
orp->or_client = or_client;
hw = &(orp->hw);
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
if (!from_int && (orp->rx_first==orp->rx_last)) {
goto suspend_readv;
}
rxfid = orp->rxfid[orp->rx_first];
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;
orp->rx_first++;
orp->rx_first %= NR_RX_BUFS;
o = 0;
if (vectored) {
int iov_offset = 0;
size = 0;
for (i = 0; i < count; i += IOVEC_NR,
iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]),
iov_offset += IOVEC_NR * sizeof(orp->or_iovec[0])) {
n = IOVEC_NR;
if (i + n > count)
n = count - i;
cps = sys_vircopy(or_client, D,
(vir_bytes) mp->DL_ADDR + iov_offset,
SELF, D, (vir_bytes) orp->or_iovec,
n * sizeof(orp->or_iovec[0]));
if (cps != OK) printf("sys_vircopy failed: %d (%d)\n",
cps, __LINE__);
for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) {
s = iovp->iov_size;
if (size + s > length) {
assert (length > size);
s = length - size;
}
cps = sys_vircopy(SELF, D,
(vir_bytes) databuf + o,
or_client, D,
iovp->iov_addr, s);
if (cps != OK)
printf("sys_vircopy failed:%d (%d)\n",
cps, __LINE__);
size += s;
if (size == length)
break;
o += s;
}
if (size == length)
break;
}
assert (size >= length);
}
orp->or_stat.ets_packetR++;
orp->or_read_s = length;
orp->or_flags &= ~OR_F_READING;
orp->or_flags |= OR_F_PACK_RECV;
if (!from_int)
reply (orp, OK, FALSE);
return;
suspend_readv :
if (from_int) {
assert (orp->or_flags & OR_F_READING);
return;
}
orp->or_rx_mess = *mp;
assert (!(orp->or_flags & OR_F_READING));
orp->or_flags |= OR_F_READING;
reply (orp, OK, FALSE);
}
/*****************************************************************************
* or_readv_s *
* *
* Copy the data which is stored in orp->rx_buf[orp->rx_first] in the vector *
* which was given with the message *mp *
*****************************************************************************/
static void or_readv_s (message * mp, int from_int) {
int i, j, n, o, s, s1, dl_port, or_client, count, size, err, cps;
int iov_offset = 0, length, offset;
port_t port;
clock_t timebefore;
unsigned amount, totlen, packlen;
struct hermes_rx_descriptor desc;
phys_bytes dst_phys, iov_src;
u16_t d_start, d_end, rxfid, status;
struct header_struct hdr;
u32_t l, rxstat;
struct ethhdr *eh;
struct header_struct *h;
t_or *orp;
hermes_t *hw;
iovec_s_t *iovp;
phys_bytes databuf_phys;
u8_t *databuf;
dl_port = mp->DL_PORT;
count = mp->DL_COUNT;
if (dl_port < 0 || dl_port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port:", dl_port);
orp = &or_table[dl_port];
or_client = mp->DL_PROC;
orp->or_client = or_client;
hw = &(orp->hw);
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
if (!from_int && (orp->rx_first==orp->rx_last))
{
/* if we are not called from a hard int (data is not yet available) and
* there are no buffers (or->rx_buf[x]) which contain any data, we cant
* copy any data to the inet server. Goto suspend, and wait for data
* to arrive */
goto suspend_readv_s;
}
/* get the buffer which contains new data */
rxfid = orp->rxfid[orp->rx_first];
/* and 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;
o = 0;
/* The data which we want to be copied to the vector starts at
* *databuf and will be copied to the vecor below */
size = 0;
for (i = 0; i < count; i += IOVEC_NR,
iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]),
iov_offset += IOVEC_NR * sizeof(orp->or_iovec_s[0])) {
n = IOVEC_NR;
if (i + n > count)
n = count - i;
cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset,
(vir_bytes)orp->or_iovec_s,
n * sizeof(orp->or_iovec_s[0]), D);
if (cps != OK)
panic(__FILE__,
"orinoco: warning, sys_safecopytp failed:", cps);
for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++) {
s = iovp->iov_size;
if (size + s > length) {
assert (length > size);
s = length - size;
}
cps = sys_safecopyto(or_client, iovp->iov_grant, 0,
(vir_bytes) databuf + o, s, D);
if (cps != OK)
panic(__FILE__,
"orinoco: warning, sys_safecopy failed:",
cps);
size += s;
if (size == length)
break;
o += s;
}
if (size == length)
break;
}
assert(size >= length);
orp->or_stat.ets_packetR++;
drop:
orp->or_read_s = length;
orp->or_flags &= ~OR_F_READING;
orp->or_flags |= OR_F_PACK_RECV;
if (!from_int) {
/* There was data in the orp->rx_buf[x] which is now copied to
* the inet sever. Tell the inet server */
reply (orp, OK, FALSE);
}
return;
suspend_readv_s:
if (from_int) {
assert (orp->or_flags & OR_F_READING);
/* No need to store any state */
return;
}
/* We want to store the message, so that next time when we are called
* by hard int, we know where to copy the received data */
orp->or_rx_mess = *mp;
assert (!(orp->or_flags & OR_F_READING));
orp->or_flags |= OR_F_READING;
reply (orp, OK, FALSE);
}
/*****************************************************************************
* 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;
struct ethhdr *eh;
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)) {
eh = (struct ethhdr *) databuf;
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_getstat *
* *
* Return the statistics structure. The statistics aren't updated until now, *
* so this won't return much interesting yet. *
*****************************************************************************/
static void or_getstat (message * mp) {
int r, port;
eth_stat_t stats;
t_or *orp;
port = mp->DL_PORT;
if (port < 0 || port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port:", port);
orp = &or_table[port];
orp->or_client = mp->DL_PROC;
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
stats = orp->or_stat;
r = sys_datacopy(SELF, (vir_bytes)&stats, mp->DL_PROC,
(vir_bytes) mp->DL_ADDR, sizeof(stats));
if(r != OK) {
panic(__FILE__, "or_getstat: send failed:", r);
}
mp->m_type = DL_STAT_REPLY;
mp->DL_PORT = port;
mp->DL_STAT = OK;
r = send(mp->m_source, mp);
if(r != OK)
panic(__FILE__, "orinoco: getstat failed:", r);
/*reply (orp, OK, FALSE);*/
}
/*****************************************************************************
* or_getstat_s *
* *
* Return the statistics structure. The statistics aren't updated until now, *
* so this won't return much interesting yet. *
*****************************************************************************/
static void or_getstat_s (message * mp) {
int r, port;
eth_stat_t stats;
t_or *orp;
port = mp->DL_PORT;
if (port < 0 || port >= OR_PORT_NR)
panic(__FILE__, "orinoco: illegal port:", port);
assert(port==0);
orp = &or_table[port];
orp->or_client = mp->DL_PROC;
assert (orp->or_mode == OR_M_ENABLED);
assert (orp->or_flags & OR_F_ENABLED);
stats = orp->or_stat;
r = sys_safecopyto(mp->DL_PROC, mp->DL_GRANT, 0,
(vir_bytes) &stats, sizeof(stats), D);
if(r != OK) {
panic(__FILE__, "or_getstat_s: sys_safecopyto failed:", r);
}
mp->m_type = DL_STAT_REPLY;
mp->DL_PORT = port;
mp->DL_STAT = OK;
r = send(mp->m_source, mp);
if(r != OK)
panic(__FILE__, "orinoco: getstat_s failed:", r);
}