1879 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1879 lines
		
	
	
		
			54 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	<string.h>
 | 
						|
#include	<minix/syslib.h>
 | 
						|
#include	<minix/type.h>
 | 
						|
#include	<minix/sysutil.h>
 | 
						|
#include	<timers.h>
 | 
						|
#include	<machine/pci.h>
 | 
						|
#include 	<minix/ds.h>
 | 
						|
#include	<minix/endpoint.h>
 | 
						|
#include	"kernel/const.h"
 | 
						|
#include	"kernel/config.h"
 | 
						|
#include	"kernel/type.h"
 | 
						|
 | 
						|
#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 }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#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	<machine/vm.h>
 | 
						|
#include	<sys/types.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
 | 
						|
 | 
						|
PRIVATE t_or or_state;
 | 
						|
PRIVATE int or_instance;
 | 
						|
 | 
						|
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 */
 | 
						|
PRIVATE 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_s, (message * mp, int from_int));
 | 
						|
_PROTOTYPE (static void or_readv_s, (message * mp, int from_int));
 | 
						|
_PROTOTYPE (static void reply, (t_or * orp));
 | 
						|
_PROTOTYPE (static int  or_probe, (t_or *, int skip));
 | 
						|
_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_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 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 do_hard_int, (void));
 | 
						|
_PROTOTYPE (static void check_int_events, (void));
 | 
						|
_PROTOTYPE (static void 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 an interrupt message.
 | 
						|
 */
 | 
						|
PRIVATE message m;
 | 
						|
PRIVATE int int_event_check;		/* set to TRUE if events arrived */
 | 
						|
 | 
						|
PRIVATE u32_t system_hz;
 | 
						|
 | 
						|
/* SEF functions and variables. */
 | 
						|
FORWARD _PROTOTYPE( void sef_local_startup, (void) );
 | 
						|
FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) );
 | 
						|
FORWARD _PROTOTYPE( void sef_cb_signal_handler, (int signo) );
 | 
						|
EXTERN char **env_argv;
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *            main                                                           *
 | 
						|
 *                                                                           *
 | 
						|
 *                                                                           *
 | 
						|
 * The main function of the driver, receiving and processing messages        *
 | 
						|
 *****************************************************************************/
 | 
						|
int main(int argc, char *argv[]) {
 | 
						|
	int r;
 | 
						|
	int ipc_status;
 | 
						|
 | 
						|
	/* SEF local startup. */
 | 
						|
	env_setargs(argc, argv);
 | 
						|
	sef_local_startup();
 | 
						|
 | 
						|
	while (TRUE) {
 | 
						|
		if ((r = netdriver_receive (ANY, &m, &ipc_status)) != OK)
 | 
						|
			panic("orinoco: netdriver_receive failed");
 | 
						|
 | 
						|
		if (is_ipc_notify(ipc_status)) {
 | 
						|
			switch (_ENDPOINT_P(m.m_source)) {
 | 
						|
				case CLOCK:
 | 
						|
					or_watchdog_f(NULL);     
 | 
						|
					break;		 
 | 
						|
				case HARDWARE:
 | 
						|
					do_hard_int();
 | 
						|
					if (int_event_check)
 | 
						|
						check_int_events();
 | 
						|
					break ;
 | 
						|
				case TTY_PROC_NR: 
 | 
						|
					or_dump(&m);	
 | 
						|
					break;
 | 
						|
				default:
 | 
						|
					panic("orinoco: illegal notify from: %d",
 | 
						|
						m.m_source);
 | 
						|
			}
 | 
						|
 | 
						|
			/* done, get new message */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		switch (m.m_type) {
 | 
						|
		case DL_WRITEV_S:
 | 
						|
			or_writev_s (&m, FALSE);
 | 
						|
			break;
 | 
						|
		case DL_READV_S:
 | 
						|
			or_readv_s (&m, FALSE);
 | 
						|
			break;
 | 
						|
		case DL_CONF:
 | 
						|
			or_init (&m);
 | 
						|
			break;
 | 
						|
		case DL_GETSTAT_S:
 | 
						|
			or_getstat_s (&m);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			panic("orinoco: illegal message: %d", m.m_type);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *			       sef_local_startup			     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void sef_local_startup()
 | 
						|
{
 | 
						|
  /* Register init callbacks. */
 | 
						|
  sef_setcb_init_fresh(sef_cb_init_fresh);
 | 
						|
  sef_setcb_init_lu(sef_cb_init_fresh);
 | 
						|
  sef_setcb_init_restart(sef_cb_init_fresh);
 | 
						|
 | 
						|
  /* Register live update callbacks. */
 | 
						|
  sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
 | 
						|
  sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
 | 
						|
 | 
						|
  /* Register signal callbacks. */
 | 
						|
  sef_setcb_signal_handler(sef_cb_signal_handler);
 | 
						|
 | 
						|
  /* Let SEF perform startup. */
 | 
						|
  sef_startup();
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *		            sef_cb_init_fresh                                *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
 | 
						|
{
 | 
						|
/* Initialize the orinoco driver. */
 | 
						|
	long v;
 | 
						|
	int fkeys, sfkeys, r;
 | 
						|
 | 
						|
	system_hz = sys_hz();
 | 
						|
 | 
						|
	v = 0;
 | 
						|
	(void) env_parse("instance", "d", 0, &v, 0, 255);
 | 
						|
	or_instance = (int) v;
 | 
						|
 | 
						|
	/* 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);
 | 
						|
 | 
						|
	/* Announce we are up! */
 | 
						|
	netdriver_announce();
 | 
						|
 | 
						|
	return(OK);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *		           sef_cb_signal_handler                             *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void sef_cb_signal_handler(int signo)
 | 
						|
{
 | 
						|
	t_or *orp;
 | 
						|
 | 
						|
	/* Only check for termination signal, ignore anything else. */
 | 
						|
	if (signo != SIGTERM) return;
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	if (orp->or_mode == OR_M_ENABLED) {
 | 
						|
		/* TODO: send a signal to the card to shut it down */
 | 
						|
	}
 | 
						|
	exit(0);
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                    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) {
 | 
						|
	t_or *orp;
 | 
						|
 | 
						|
	/* the interrupt message doesn't contain information about the port, try
 | 
						|
	 * to find it */
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	if (orp->or_mode != OR_M_ENABLED)
 | 
						|
		return;
 | 
						|
	if (!orp->or_got_int)
 | 
						|
		return;
 | 
						|
	orp->or_got_int = 0;
 | 
						|
	assert (orp->or_flags & OR_F_ENABLED);
 | 
						|
	or_check_ints (orp);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                do_hard_int                                                *
 | 
						|
 *                                                                           *
 | 
						|
 * Process the interrupts which the card generated                           *
 | 
						|
 *****************************************************************************/
 | 
						|
static void do_hard_int(void)
 | 
						|
{
 | 
						|
	int s;
 | 
						|
 | 
						|
	/* Run interrupt handler at driver level. */
 | 
						|
	or_handler(&or_state);
 | 
						|
 | 
						|
	/* Reenable interrupts for this hook. */
 | 
						|
	if ((s=sys_irqenable(&or_state.or_hook_id)) != OK) {
 | 
						|
		printf("orinoco: error, couldn't enable");
 | 
						|
		printf(" interrupts: %d\n", s);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                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, r;
 | 
						|
 | 
						|
	if (OK != (r = getuptime(&now)))
 | 
						|
		panic("orinoco: getuptime() failed: %d", r);
 | 
						|
 | 
						|
	if(now - last_reset < system_hz * 10) {
 | 
						|
		printf("Resetting card too often. Going to reset driver\n");
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
	last_reset = now;
 | 
						|
	
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	if(orp->or_mode == OR_M_DISABLED) 
 | 
						|
		printf("orinoco instance %d is disabled\n", or_instance);
 | 
						|
		
 | 
						|
	if(orp->or_mode != OR_M_ENABLED) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	orp->or_need_reset = 0;
 | 
						|
	or_init_hw(orp);
 | 
						|
 | 
						|
	orp->rx_last = orp->rx_first = 0;
 | 
						|
	for(i = 0; i < NR_RX_BUFS; i++) {
 | 
						|
		orp->rx_length[0] = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if(orp->or_flags & OR_F_SEND_AVAIL) {
 | 
						|
		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 (message *m)
 | 
						|
 {
 | 
						|
	t_or *orp;
 | 
						|
	
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	if(orp->or_mode == OR_M_DISABLED) {
 | 
						|
		printf("%s is disabled\n", orp->or_name);
 | 
						|
	}
 | 
						|
		
 | 
						|
	if(orp->or_mode != OR_M_ENABLED)
 | 
						|
		return;
 | 
						|
 | 
						|
	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) {
 | 
						|
	t_or *orp;
 | 
						|
	message reply;
 | 
						|
	static int first_time = 1;
 | 
						|
 | 
						|
	if (first_time) {
 | 
						|
		first_time = 0;
 | 
						|
		or_pci_conf ();	/* Configure PCI devices. */
 | 
						|
	
 | 
						|
		/* Use a synchronous alarm instead of a watchdog timer. */
 | 
						|
		sys_setalarm(system_hz, 0);
 | 
						|
	}	
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	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.DL_STAT = 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;
 | 
						|
 | 
						|
	or_rec_mode (orp);
 | 
						|
 | 
						|
	/* reply the caller that the configuration succeeded */
 | 
						|
	reply.m_type = DL_CONF_REPLY;
 | 
						|
	reply.DL_STAT = OK;
 | 
						|
	*(ether_addr_t *) reply.DL_HWADDR = 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;
 | 
						|
	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 */
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	strncpy (orp->or_name, OR_NAME, sizeof(OR_NAME));
 | 
						|
	orp->or_name[sizeof(OR_NAME) - 2] = or_instance + '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' + or_instance;
 | 
						|
	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 is in the pci bus */
 | 
						|
	if (or_probe (orp, or_instance))
 | 
						|
		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, int skip)
 | 
						|
{
 | 
						|
	u8_t ilr;
 | 
						|
	u32_t 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("or_probe:class check not implmnted");
 | 
						|
			}
 | 
						|
			/* we have found the card in the pci bus */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* unless we are looking for a specific device, we may have to
 | 
						|
		 * skip a number of cards because they are already reserved for
 | 
						|
		 * other (lower) ports of this driver */
 | 
						|
		if (pcitab[i].vid != 0) {
 | 
						|
			if (just_one || !skip)
 | 
						|
				break;
 | 
						|
			skip--;
 | 
						|
		}
 | 
						|
 | 
						|
		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;
 | 
						|
	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("map_hw_buffer: cannot malloc size: %d", 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("map_hw_buffer: sys_vm_map failed: %d", 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;
 | 
						|
	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 (debug) {
 | 
						|
			printf ("%s: using I/O space address 0x%lx, IRQ %d\n",
 | 
						|
				orp->or_name, bar, orp->or_irq);
 | 
						|
		}
 | 
						|
 | 
						|
		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 (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)) != 0) {
 | 
						|
		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)
 | 
						|
{
 | 
						|
	/* Read the MAC address */
 | 
						|
	int 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;
 | 
						|
	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 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");
 | 
						|
		}
 | 
						|
 | 
						|
		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;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                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)
 | 
						|
{
 | 
						|
	t_or *orp;
 | 
						|
	
 | 
						|
	/* Use a synchronous alarm instead of a watchdog timer. */
 | 
						|
	sys_setalarm(system_hz, 0);
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	if (orp->or_mode != OR_M_ENABLED)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (!(orp->or_flags & OR_F_SEND_AVAIL))	{
 | 
						|
		/* 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_watchdog_f: resetting instance %d\n", or_instance);
 | 
						|
	
 | 
						|
	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("orinoco: unable to mess_reply");
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                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 count, size, err, data_len, data_off;
 | 
						|
	int o, j, n, i, s, p, cps ;
 | 
						|
	struct ethhdr *eh;
 | 
						|
	t_or *orp;
 | 
						|
	hermes_t *hw;
 | 
						|
	struct hermes_tx_descriptor desc;
 | 
						|
	int iov_offset = 0;
 | 
						|
	struct header_struct hdr;
 | 
						|
	iovec_s_t *iovp;
 | 
						|
	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);
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	count = mp->DL_COUNT;
 | 
						|
 | 
						|
	orp->or_client = mp->m_source;
 | 
						|
	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_offset += IOVEC_NR * sizeof (orp->or_iovec_s[0])) {
 | 
						|
 | 
						|
		n = IOVEC_NR;
 | 
						|
		if (i + n > count)
 | 
						|
			n = count - i;
 | 
						|
 | 
						|
		cps = sys_safecopyfrom(mp->DL_ENDPT, 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(mp->DL_ENDPT, 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);
 | 
						|
	return;
 | 
						|
 | 
						|
suspend_write_s:
 | 
						|
	orp->or_tx_mess = *mp;
 | 
						|
 | 
						|
	reply (orp);
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                reply                                                      *
 | 
						|
 *                                                                           *
 | 
						|
 * Send a message back to the caller, informing it about the data received   *
 | 
						|
 * or sent                                                                   *
 | 
						|
 *****************************************************************************/
 | 
						|
static void reply (t_or * orp) {
 | 
						|
	message reply;
 | 
						|
	int flags = DL_NOFLAGS, r;
 | 
						|
 | 
						|
	if (orp->or_flags & OR_F_PACK_SENT)
 | 
						|
		flags |= DL_PACK_SEND;
 | 
						|
	if (orp->or_flags & OR_F_PACK_RECV)
 | 
						|
		flags |= DL_PACK_RECV;
 | 
						|
 | 
						|
	reply.m_type = DL_TASK_REPLY;
 | 
						|
	reply.DL_FLAGS = flags;
 | 
						|
	reply.DL_COUNT = orp->or_read_s;
 | 
						|
 | 
						|
	r = send (orp->or_client, &reply);
 | 
						|
 | 
						|
	if (r < 0)
 | 
						|
		panic("orinoco: send failed: %d", 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;
 | 
						|
	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: %ld 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)
 | 
						|
{
 | 
						|
	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;
 | 
						|
		or_readv_s (&orp->or_rx_mess, TRUE);
 | 
						|
	}
 | 
						|
 | 
						|
	if (orp->or_send_int) {
 | 
						|
		or_writev_s (&orp->or_tx_mess, TRUE);
 | 
						|
	}
 | 
						|
 | 
						|
	if (orp->or_flags & (OR_F_PACK_SENT | OR_F_PACK_RECV)) {
 | 
						|
		reply (orp);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *                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_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, count, size, cps;
 | 
						|
	int iov_offset = 0, length;
 | 
						|
	t_or *orp;
 | 
						|
	iovec_s_t *iovp;
 | 
						|
	u8_t *databuf;
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	orp->or_client = mp->m_source;
 | 
						|
	count = mp->DL_COUNT;
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
	
 | 
						|
 | 
						|
 | 
						|
	/* 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_offset += IOVEC_NR * sizeof(orp->or_iovec_s[0])) {
 | 
						|
		n = IOVEC_NR;
 | 
						|
		if (i + n > count)
 | 
						|
			n = count - i;
 | 
						|
 | 
						|
		cps = sys_safecopyfrom(mp->DL_ENDPT, mp->DL_GRANT, iov_offset, 
 | 
						|
				(vir_bytes)orp->or_iovec_s,
 | 
						|
				n * sizeof(orp->or_iovec_s[0]), D);
 | 
						|
		if (cps != OK) 
 | 
						|
			panic("orinoco: warning: sys_safecopytp failed: %d", 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(mp->DL_ENDPT, iovp->iov_grant, 0, 
 | 
						|
					(vir_bytes) databuf + o, s, D);
 | 
						|
			if (cps != OK) 
 | 
						|
				panic("orinoco: warning: sys_safecopy failed: %d", cps);
 | 
						|
 | 
						|
			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) {
 | 
						|
		/* There was data in the orp->rx_buf[x] which is now copied to 
 | 
						|
		 * the inet sever. Tell the inet server */
 | 
						|
		reply (orp);
 | 
						|
	}
 | 
						|
 | 
						|
	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);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 *            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_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;
 | 
						|
	eth_stat_t stats;
 | 
						|
	t_or *orp;
 | 
						|
 | 
						|
	orp = &or_state;
 | 
						|
 | 
						|
	assert (orp->or_mode == OR_M_ENABLED);
 | 
						|
	assert (orp->or_flags & OR_F_ENABLED);
 | 
						|
 | 
						|
	stats = orp->or_stat;
 | 
						|
 | 
						|
	r = sys_safecopyto(mp->DL_ENDPT, mp->DL_GRANT, 0, 
 | 
						|
				(vir_bytes) &stats, sizeof(stats), D);
 | 
						|
	if(r != OK) {
 | 
						|
		panic("or_getstat_s: sys_safecopyto failed: %d", r);
 | 
						|
	}
 | 
						|
 | 
						|
	mp->m_type = DL_STAT_REPLY;
 | 
						|
 | 
						|
	r = send(mp->m_source, mp);
 | 
						|
	if(r != OK)
 | 
						|
		panic("orinoco: getstat_s failed: %d", r);
 | 
						|
}
 | 
						|
 |