1794 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1794 lines
		
	
	
		
			52 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 */
 | |
| 
 | |
| 
 | |
| #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
 | |
| 
 | |
| static t_or or_state;
 | |
| static 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 */
 | |
| static const long channel_frequency[] = {
 | |
| 	2412, 2417, 2422, 2427, 2432, 2437, 2442,
 | |
| 	2447, 2452, 2457, 2462, 2467, 2472, 2484
 | |
| };
 | |
| 
 | |
| #define NUM_CHANNELS (sizeof(channel_frequency) / sizeof(channel_frequency[0]))
 | |
| 
 | |
| /* This tables gives the actual meanings of the bitrate IDs returned by the 
 | |
|  * firmware. Not used yet */
 | |
| struct {
 | |
| 	int bitrate;		/* in 100s of kilobits */
 | |
| 	int automatic;
 | |
| 	u16_t txratectrl;
 | |
| } bitrate_table[] =
 | |
| {
 | |
| 	{110, 1, 15},		/* Entry 0 is the default */
 | |
| 	{10, 0, 1},
 | |
| 	{10, 1, 1},
 | |
| 	{20, 0, 2},
 | |
| 	{20, 1, 3},
 | |
| 	{55, 0, 4},
 | |
| 	{55, 1, 7},
 | |
| 	{110, 0, 8},};
 | |
| 
 | |
| #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 | |
| 
 | |
| 
 | |
| static void or_writev_s(message * mp, int from_int);
 | |
| static void or_readv_s(message * mp, int from_int);
 | |
| static void reply(t_or * orp);
 | |
| static int or_probe(t_or *, int skip);
 | |
| static void or_ev_info(t_or *);
 | |
| static void or_init(message *);
 | |
| static void or_pci_conf(void);
 | |
| static void or_init_struct(t_or *);
 | |
| static void map_hw_buffer(t_or *);
 | |
| static void or_init_hw(t_or *);
 | |
| static void or_check_ints(t_or *);
 | |
| static void or_writerids(hermes_t *, t_or *);
 | |
| static void or_readrids(hermes_t *, t_or *);
 | |
| static void or_rec_mode(t_or *);
 | |
| static void mess_reply(message *, message *);
 | |
| static u32_t or_get_bar(int devind, t_or * orp);
 | |
| static void or_getstat_s(message * mp);
 | |
| static void print_linkstatus(t_or * orp, u16_t status);
 | |
| static int or_get_recvd_packet(t_or *orp, u16_t rxfid, u8_t *databuf);
 | |
| static void or_reset(void);
 | |
| static void or_watchdog_f(timer_t *tp);
 | |
| static void setup_wepkey(t_or *orp, char *wepkey0);
 | |
| static void do_hard_int(void);
 | |
| static void check_int_events(void);
 | |
| static void or_handler(t_or *orp);
 | |
| 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.
 | |
|  */
 | |
| static message m;
 | |
| static int int_event_check;		/* set to TRUE if events arrived */
 | |
| 
 | |
| static u32_t system_hz;
 | |
| 
 | |
| /* SEF functions and variables. */
 | |
| static void sef_local_startup(void);
 | |
| static int sef_cb_init_fresh(int type, sef_init_info_t *info);
 | |
| static void sef_cb_signal_handler(int signo);
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            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			     *
 | |
|  *===========================================================================*/
 | |
| static 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                                *
 | |
|  *===========================================================================*/
 | |
| static 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                             *
 | |
|  *===========================================================================*/
 | |
| static 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 = getticks(&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 = TTY_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 () {
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	/* 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;
 | |
| 
 | |
| 	/* 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 r, devind;
 | |
| 
 | |
| 	/* Start looking from the beginning */
 | |
| 	r = pci_first_dev (&devind, &vid, &did);
 | |
| 	if (r == 0)
 | |
| 		return (0);
 | |
| 
 | |
| 	/* Skip as many instances as requested */
 | |
| 	while (skip--) {
 | |
| 		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 PAGE_SIZE big: see
 | |
| 	 * calculation with the offset */
 | |
| 	size = 2 * PAGE_SIZE;
 | |
| 
 | |
| 	buf = (char *)malloc(size);
 | |
| 	if(buf == NULL) 
 | |
| 		panic("map_hw_buffer: cannot malloc size: %d", size);
 | |
| 
 | |
| 	/* Let the mapped memory by PAGE_SIZE aligned */
 | |
| 	o = PAGE_SIZE - ((vir_bytes)buf % PAGE_SIZE);
 | |
| 	abuf = buf + o;
 | |
| 
 | |
| #if 0
 | |
| 	r = sys_vm_map(SELF, 1, (vir_bytes)abuf, 
 | |
| 			1 * 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%x, 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%x, 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->m_source, mp->DL_GRANT, iov_offset,
 | |
| 			(vir_bytes) orp->or_iovec_s, 
 | |
| 			n * sizeof(orp->or_iovec_s[0]));
 | |
| 		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->m_source, iovp->iov_grant,
 | |
| 					0, (vir_bytes) databuf + o, s);
 | |
| 			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->m_source, mp->DL_GRANT, iov_offset, 
 | |
| 				(vir_bytes)orp->or_iovec_s,
 | |
| 				n * sizeof(orp->or_iovec_s[0]));
 | |
| 		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->m_source, iovp->iov_grant, 0, 
 | |
| 					(vir_bytes) databuf + o, s);
 | |
| 			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->m_source, mp->DL_GRANT, 0, 
 | |
| 				(vir_bytes) &stats, sizeof(stats));
 | |
| 	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);
 | |
| }
 | |
| 
 | 
