2381 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2381 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * orinoco.c
 | |
|  *
 | |
|  * This file contains a wireless device driver for Prism based wireless
 | |
|  * cards. 
 | |
|  *
 | |
|  * Created by Stevens Le Blond <slblond@few.vu.nl> 
 | |
|  *        and Michael Valkering <mjvalker@cs.vu.nl>
 | |
|  *
 | |
|  * * The valid messages and their parameters are:
 | |
|  *
 | |
|  *   m_type	  DL_PORT    DL_PROC   DL_COUNT   DL_MODE   DL_ADDR   DL_GRANT
 | |
|  * |------------+----------+---------+----------+---------+---------+---------|
 | |
|  * | HARDINT	|          |         |          |         |         |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_WRITE	| port nr  | proc nr | count    | mode    | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_WRITEV	| port nr  | proc nr | count    | mode    | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_WRITEV_S| port nr  | proc nr | count    | mode    |         |  grant  |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_READ	| port nr  | proc nr | count    |         | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_READV	| port nr  | proc nr | count    |         | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_READV_S	| port nr  | proc nr | count    |         |         |  grant  |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_CONF	| port nr  | proc nr |          | mode    | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_GETSTAT	| port nr  | proc nr |          |         | address |	      |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * |DL_GETSTAT_S| port nr  | proc nr |          |         |         |  grant  |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  * | DL_STOP	| port_nr  |         |          |         |         |         |
 | |
|  * |------------|----------|---------|----------|---------|---------|---------|
 | |
|  *
 | |
|  * The messages sent are:
 | |
|  *
 | |
|  *   m_type	  DL_PORT    DL_PROC   DL_COUNT   DL_STAT   DL_CLCK
 | |
|  * |------------|----------|---------|----------|---------|---------|
 | |
|  * |DL_TASK_REPL| port nr  | proc nr | rd-count | err|stat| clock   |
 | |
|  * |------------|----------|---------|----------|---------|---------|
 | |
|  *
 | |
|  *   m_type	  m3_i1     m3_i2       m3_ca1
 | |
|  * |------------|---------|-----------|---------------|
 | |
|  * |DL_CONF_REPL| port nr | last port | ethernet addr |
 | |
|  * |------------|---------|-----------|---------------|
 | |
|  *
 | |
|  *   m_type	  DL_PORT    DL_STAT       
 | |
|  * |------------|---------|-----------|
 | |
|  * |DL_STAT_REPL| port nr |   err     |
 | |
|  * |------------|---------|-----------|
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include 	"../drivers.h" 
 | |
| #include	<string.h>
 | |
| #include 	<stddef.h>
 | |
| #include	<minix/keymap.h>
 | |
| #include	<minix/syslib.h>
 | |
| #include	<minix/type.h>
 | |
| #include	<minix/sysutil.h>
 | |
| #include	<timers.h>
 | |
| #include 	<sys/ioc_memory.h>
 | |
| #include	<ibm/pci.h>
 | |
| #include 	<minix/ds.h>
 | |
| #include	"../../kernel/const.h"
 | |
| #include	"../../kernel/config.h"
 | |
| #include	"../../kernel/type.h"
 | |
| 
 | |
| #define		tmra_ut			timer_t
 | |
| #define		tmra_inittimer(tp)	tmr_inittimer(tp)
 | |
| #define		VERBOSE		1	/* display message during init */
 | |
| 
 | |
| PRIVATE struct pcitab {
 | |
| 	u16_t vid;
 | |
| 	u16_t did;
 | |
| 	int checkclass;
 | |
| } pcitab[]=
 | |
| {
 | |
| 	{ 0x1260, 0x3873, 0 },	
 | |
| 	{ 0x1186, 0x1300, 0 },	
 | |
| 	{ 0x0000, 0x0000, 0 }
 | |
| };
 | |
| 
 | |
| 
 | |
| static tmra_ut or_watchdog;
 | |
| 
 | |
| #include 	<stdio.h>
 | |
| #include	<stdlib.h>
 | |
| #include	<minix/com.h>
 | |
| #include	<minix/portio.h>
 | |
| #include	<net/hton.h>
 | |
| #include	<net/gen/ether.h>
 | |
| #include	<net/gen/eth_io.h>
 | |
| #include	<sys/vm_i386.h>
 | |
| #include	<sys/types.h>
 | |
| #include	<fcntl.h>
 | |
| #include 	<unistd.h>
 | |
| #include	<errno.h>
 | |
| 
 | |
| #include	"assert.h"
 | |
| #include	"hermes.h"
 | |
| #include	"hermes_rid.h"
 | |
| #include	"orinoco.h"
 | |
| 
 | |
| #define 	ERR -1
 | |
| 
 | |
| #define		debug 0
 | |
| 
 | |
| #define		OR_M_ENABLED 1
 | |
| #define		OR_M_DISABLED 0
 | |
| #define		OR_F_EMPTY 0
 | |
| #define		OR_F_MULTI 1
 | |
| #define		OR_F_BROAD (1<<1)
 | |
| #define		OR_F_ENABLED (1<<2)
 | |
| #define		OR_F_PROMISC (1<<3)
 | |
| #define		OR_F_READING (1<<4)
 | |
| #define		OR_F_SEND_AVAIL (1<<5)
 | |
| #define		OR_F_PACK_SENT (1<<6)
 | |
| #define		OR_F_PACK_RECV (1<<7)
 | |
| #define 	ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC |\
 | |
| 					HERMES_EV_WTERR | HERMES_EV_TXEXC|\
 | |
| 					HERMES_EV_INFO | HERMES_EV_INFDROP|\
 | |
| 					HERMES_EV_TX)
 | |
| 
 | |
| #define		NO_FID (-1)
 | |
| #define		ETH_ALEN 6
 | |
| #define		USER_BAP 0
 | |
| #define 	IRQ_BAP 1
 | |
| #define		ETH_HLEN		14
 | |
| 
 | |
| static int or_nr_task = ANY;
 | |
| static t_or or_table[OR_PORT_NR];
 | |
| 
 | |
| struct ethhdr {
 | |
| 	u8_t h_dest[ETH_ALEN];
 | |
| 	u8_t h_src[ETH_ALEN];
 | |
| 	u16_t h_proto;
 | |
| };
 | |
| 
 | |
| struct header_struct {
 | |
| 	/* 802.3 */
 | |
| 	u8_t dest[ETH_ALEN];
 | |
| 	u8_t src[ETH_ALEN];
 | |
| 	u16_t len;
 | |
| 	/* 802.2 */
 | |
| 	u8_t dsap;
 | |
| 	u8_t ssap;
 | |
| 	u8_t ctrl;
 | |
| 	/* SNAP */
 | |
| 	u8_t oui[3];
 | |
| 	u16_t ethertype;
 | |
| };
 | |
| 
 | |
| #define			RUP_EVEN(x)	(((x) + 1) & (~1))
 | |
| 
 | |
| u8_t encaps_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 | |
| #define	ENCAPS_OVERHEAD	(sizeof (encaps_hdr) + 2)
 | |
| 
 | |
| /********************************************************************
 | |
|  *              Data tables                                         *
 | |
|  ********************************************************************/
 | |
| 
 | |
| /* The frequency of each channel in MHz */
 | |
| const long channel_frequency[] = {
 | |
| 	2412, 2417, 2422, 2427, 2432, 2437, 2442,
 | |
| 	2447, 2452, 2457, 2462, 2467, 2472, 2484
 | |
| };
 | |
| 
 | |
| #define NUM_CHANNELS (sizeof(channel_frequency) / sizeof(channel_frequency[0]))
 | |
| 
 | |
| /* This tables gives the actual meanings of the bitrate IDs returned by the 
 | |
|  * firmware. Not used yet */
 | |
| struct {
 | |
| 	int bitrate;		/* in 100s of kilobits */
 | |
| 	int automatic;
 | |
| 	u16_t txratectrl;
 | |
| } bitrate_table[] =
 | |
| {
 | |
| 	{110, 1, 15},		/* Entry 0 is the default */
 | |
| 	{10, 0, 1},
 | |
| 	{10, 1, 1},
 | |
| 	{20, 0, 2},
 | |
| 	{20, 1, 3},
 | |
| 	{55, 0, 4},
 | |
| 	{55, 1, 7},
 | |
| 	{110, 0, 8},};
 | |
| 
 | |
| #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 | |
| 
 | |
| 
 | |
| _PROTOTYPE (static void or_writev, (message * mp, int from_int, int vectored));
 | |
| _PROTOTYPE (static void or_readv, (message * mp, int from_int, int vectored));
 | |
| _PROTOTYPE (static void or_writev_s, (message * mp, int from_int));
 | |
| _PROTOTYPE (static void or_readv_s, (message * mp, int from_int));
 | |
| _PROTOTYPE (static void reply, (t_or * orp, int err, int may_block));
 | |
| _PROTOTYPE (static int  or_probe, (t_or *));
 | |
| _PROTOTYPE (static void or_ev_info, (t_or *));
 | |
| _PROTOTYPE (static void or_init, (message *));
 | |
| _PROTOTYPE (static void or_pci_conf, (void));
 | |
| _PROTOTYPE (static void or_init_struct, (t_or *));
 | |
| _PROTOTYPE (static void map_hw_buffer, (t_or *));
 | |
| _PROTOTYPE (static void or_init_hw, (t_or *));
 | |
| _PROTOTYPE (static void or_check_ints, (t_or *));
 | |
| _PROTOTYPE (static void or_writerids, (hermes_t *, t_or *));
 | |
| _PROTOTYPE (static void or_readrids, (hermes_t *, t_or *));
 | |
| _PROTOTYPE (static void or_rec_mode, (t_or *));
 | |
| _PROTOTYPE (static void mess_reply, (message *, message *));
 | |
| _PROTOTYPE (static u32_t or_get_bar, (int devind, t_or * orp));
 | |
| _PROTOTYPE (static void or_getstat, (message * mp));
 | |
| _PROTOTYPE (static void or_getstat_s, (message * mp));
 | |
| _PROTOTYPE (static void print_linkstatus, (t_or * orp, u16_t status));
 | |
| _PROTOTYPE (static int  or_get_recvd_packet, (t_or *orp, u16_t rxfid, 
 | |
| 					u8_t *databuf));
 | |
| _PROTOTYPE (static void orinoco_stop, (void));
 | |
| _PROTOTYPE (static void or_reset, (void));
 | |
| _PROTOTYPE (static void or_watchdog_f, (timer_t *tp) );
 | |
| _PROTOTYPE (static void setup_wepkey, (t_or *orp, char *wepkey0) );
 | |
| _PROTOTYPE (static void or_getstat, (message *m));
 | |
| _PROTOTYPE (static int  do_hard_int, (void));
 | |
| _PROTOTYPE (static void check_int_events, (void));
 | |
| _PROTOTYPE (static void or_getname, (message *m));
 | |
| _PROTOTYPE (static int or_handler, (t_or *orp));
 | |
| _PROTOTYPE (static void or_dump, (message *m));
 | |
| 
 | |
| /* The message used in the main loop is made global, so that rl_watchdog_f()
 | |
|  * can change its message type to fake a HARD_INT message.
 | |
|  */
 | |
| PRIVATE message m;
 | |
| PRIVATE int int_event_check;		/* set to TRUE if events arrived */
 | |
| 
 | |
| u32_t system_hz;
 | |
| 
 | |
| static char *progname;
 | |
| extern int errno;
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            main                                                           *
 | |
|  *                                                                           *
 | |
|  *                                                                           *
 | |
|  * The main function of the driver, receiving and processing messages        *
 | |
|  *****************************************************************************/
 | |
| int main(int argc, char *argv[]) {
 | |
| 	int fkeys, sfkeys, r, i, ret;
 | |
| 	u32_t inet_proc_nr;
 | |
| 	long v = 0;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	system_hz = sys_hz();
 | |
| 
 | |
| 	(progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]);
 | |
| 
 | |
| 	env_setargs(argc, argv);
 | |
| 
 | |
| 	/* Observe some function key for debug dumps. */
 | |
| 	fkeys = sfkeys = 0; bit_set(sfkeys, 11);
 | |
| 	if ((r=fkey_map(&fkeys, &sfkeys)) != OK) 
 | |
| 	    printf("Warning: orinoco couldn't observe F-key(s): %d\n",r);
 | |
| 
 | |
| 	/* Try to notify INET that we are present (again). If INET cannot
 | |
| 	 * be found, assume this is the first time we started and INET is
 | |
| 	 * not yet alive. */
 | |
| 	r = ds_retrieve_u32("inet", &inet_proc_nr);
 | |
| 	if (r == OK) 
 | |
| 		notify(inet_proc_nr);
 | |
| 	else if (r != ESRCH)
 | |
| 		printf("orinoco: ds_retrieve_u32 failed for 'inet': %d\n", r);
 | |
| 
 | |
| 	while (TRUE) {
 | |
| 		if ((r = receive (ANY, &m)) != OK)
 | |
| 			panic(__FILE__, "orinoco: receive failed", NO_NUM);
 | |
| 
 | |
| 		switch (m.m_type) {
 | |
| 		case DEV_PING: 
 | |
| 			notify(m.m_source);	
 | |
| 			break;
 | |
| 		case DL_WRITEV:
 | |
| 			or_writev (&m, FALSE, TRUE);
 | |
| 			break;
 | |
| 		case DL_WRITEV_S:
 | |
| 			or_writev_s (&m, FALSE);
 | |
| 			break;
 | |
| 		case DL_WRITE:
 | |
| 			or_writev (&m, FALSE, FALSE);
 | |
| 			break;
 | |
| 		case DL_READ:
 | |
| 			or_readv (&m, FALSE, FALSE);
 | |
| 			break;
 | |
| 		case DL_READV:
 | |
| 			or_readv (&m, FALSE, TRUE);
 | |
| 			break;
 | |
| 		case DL_READV_S:
 | |
| 			or_readv_s (&m, FALSE);
 | |
| 			break;
 | |
| 		case DL_CONF:
 | |
| 			or_init (&m);
 | |
| 			break;
 | |
| 		case DL_GETSTAT:
 | |
| 			or_getstat (&m);
 | |
| 			break;
 | |
| 		case DL_GETSTAT_S:
 | |
| 			or_getstat_s (&m);
 | |
| 			break;
 | |
| 		case DL_GETNAME: 
 | |
| 			or_getname(&m);
 | |
| 			break;
 | |
| 		case SYN_ALARM:
 | |
| 			or_watchdog_f(NULL);     
 | |
| 			break;		 
 | |
| 		case SYS_SIG:
 | |
| 		{
 | |
| 			sigset_t sigset = m.NOTIFY_ARG;
 | |
| 			if ( sigismember( &sigset, SIGKSTOP ) )
 | |
| 				orinoco_stop();
 | |
| 		}
 | |
| 			break;
 | |
| 		case HARD_INT:
 | |
| 			do_hard_int();
 | |
| 			if (int_event_check)
 | |
| 				check_int_events();
 | |
| 			break ;
 | |
| 		case FKEY_PRESSED: 
 | |
| 			or_dump(&m);	
 | |
| 			break;
 | |
| 		case PROC_EVENT:
 | |
| 			break;
 | |
| 		default:
 | |
| 			panic(__FILE__,"orinoco: illegal message:", m.m_type);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                    check_int_events                                       *
 | |
|  *                                                                           *
 | |
|  * If a hard interrupt message came in, call the or_check_ints for the right *
 | |
|  * card                                                                      *
 | |
|  *****************************************************************************/
 | |
| static void check_int_events(void) {
 | |
| 	int i;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	/* the HARD_INT message doesn't contain information about the port, try
 | |
|          * to find it */
 | |
| 	for (orp = or_table;
 | |
| 		 orp < or_table + OR_PORT_NR; orp++) {
 | |
| 		if (orp->or_mode != OR_M_ENABLED)
 | |
| 			continue;
 | |
| 		if (!orp->or_got_int)
 | |
| 			continue;
 | |
| 		orp->or_got_int = 0;
 | |
| 		assert (orp->or_flags & OR_F_ENABLED);
 | |
| 		or_check_ints (orp);
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  *                    or_getname                                            *
 | |
|  *                                                                          *
 | |
|  * Gets the drivers name, orinoco                                           *
 | |
|  ****************************************************************************/
 | |
| static void or_getname(message *mp) {
 | |
| 	int r;
 | |
| 	
 | |
| 	strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME));
 | |
| 	mp->DL_NAME[sizeof(mp->DL_NAME) - 1] = '\0';
 | |
| 	mp->m_type = DL_NAME_REPLY;
 | |
| 
 | |
| 	r = send(mp->m_source, mp);
 | |
| 	if(r != OK) {
 | |
| 		panic(__FILE__, "or_getname: send failed", r);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                do_hard_int                                                *
 | |
|  *                                                                           *
 | |
|  * Process the interrupts which the card generated                           *
 | |
|  *****************************************************************************/
 | |
| static int do_hard_int(void) {
 | |
| 	u16_t evstat;
 | |
| 	hermes_t *hw;
 | |
| 	int i,s;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	for (i=0; i < OR_PORT_NR; i ++) {
 | |
| 		/* Run interrupt handler at driver level. */
 | |
| 		or_handler( &or_table[i]);
 | |
| 
 | |
| 		/* Reenable interrupts for this hook. */
 | |
| 		if ((s=sys_irqenable(&or_table[i].or_hook_id)) != OK) {
 | |
| 			printf("orinoco: error, couldn't enable");
 | |
| 			printf(" interrupts: %d\n", s);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                orinoco_stop                                               *
 | |
|  *                                                                           *
 | |
|  * Stops the card. The signal to the card itself is not implemented yet.     *
 | |
|  *****************************************************************************/
 | |
| static void orinoco_stop () {
 | |
| 	int i;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	for (i= 0, orp= &or_table[0]; i<OR_PORT_NR; i++, orp++) {
 | |
| 		if (orp->or_mode != OR_M_ENABLED)
 | |
| 			continue;
 | |
| 		/* TODO: send a signal to the card to shut it down */
 | |
| 	}
 | |
| 	sys_exit(0);
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_reset                                                   *
 | |
|  *                                                                           *
 | |
|  * Sometime the card gets screwed, behaving erratically. Solution: reset the *
 | |
|  * card. This is actually largely redoing the initialization                 *
 | |
|  *****************************************************************************/
 | |
| static void or_reset() {
 | |
| 	static clock_t last_reset, now;	
 | |
| 	t_or *orp;
 | |
| 	int i, j, r;
 | |
| 	u16_t irqmask;
 | |
| 	hermes_t *hw = &(orp->hw);
 | |
| 
 | |
| 	if (OK != (r = getuptime(&now)))
 | |
| 		panic(__FILE__, "orinoco: getuptime() failed:", r);
 | |
| 
 | |
| 	if(now - last_reset < system_hz * 10) {
 | |
| 		printf("Resetting card too often. Going to reset driver\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	
 | |
| 	for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) {
 | |
| 		if(orp->or_mode == OR_M_DISABLED) 
 | |
| 			printf("orinoco port %d is disabled\n", i);
 | |
| 		
 | |
| 		if(orp->or_mode != OR_M_ENABLED) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		orp->or_need_reset = 0;
 | |
| 		or_init_hw(orp);
 | |
| 
 | |
| 		orp->rx_last = orp->rx_first = 0;
 | |
| 		for(j = 0; j < NR_RX_BUFS; j++) {
 | |
| 			orp->rx_length[0] = 0;
 | |
| 		}
 | |
| 
 | |
| 		if(orp->or_flags & OR_F_SEND_AVAIL) {
 | |
| 			orp->or_tx.ret_busy = FALSE;
 | |
| 		 	orp->or_send_int = TRUE;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	last_reset = now;
 | |
| 
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_dump                                                    *
 | |
|  *                                                                           *
 | |
|  * Dump interesting information about the card on F-key pressed.             *
 | |
|  * Not implemented yet                                                       *
 | |
|  *****************************************************************************/
 | |
| static void or_dump (message *m) {
 | |
| 	t_or *orp;
 | |
| 	int i, err;
 | |
| 	u16_t evstat =0, d;
 | |
| 	hermes_t *hw;
 | |
| 
 | |
| 	for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) {
 | |
| 		if(orp->or_mode == OR_M_DISABLED) {
 | |
| 			printf("%s is disabled\n", orp->or_name);
 | |
| 		}
 | |
| 		
 | |
| 		if(orp->or_mode != OR_M_ENABLED)
 | |
| 			continue;
 | |
| 
 | |
| 		m->m_type = FKEY_CONTROL;
 | |
| 		m->FKEY_REQUEST = FKEY_EVENTS;
 | |
| 		if(OK!=(sendrec(TTY_PROC_NR,m)) )
 | |
| 			printf("Contacting the TTY failed\n");
 | |
| 		
 | |
| 		if(bit_isset(m->FKEY_SFKEYS, 11)) {
 | |
| 			print_linkstatus(orp, orp->last_linkstatus);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_init                                                    *
 | |
|  *                                                                           *
 | |
|  * The main initialization function, called when a DL_INIT message comes in. *
 | |
|  *****************************************************************************/
 | |
| static void or_init (message * mp) {
 | |
| 	int port, err, i;
 | |
| 	t_or *orp;
 | |
| 	message reply;
 | |
| 	static int first_time = 1;
 | |
| 	hermes_t *hw;
 | |
| 	clock_t t0,t1;
 | |
| 
 | |
| 	if (first_time) {
 | |
| 		first_time = 0;
 | |
| 		or_pci_conf ();	/* Configure PCI devices. */
 | |
| 	
 | |
| 		tmra_inittimer(&or_watchdog);
 | |
| 		/* Use a synchronous alarm instead of a watchdog timer. */
 | |
| 		sys_setalarm(system_hz, 0);
 | |
| 	}	
 | |
| 
 | |
| 	port = mp->DL_PORT;
 | |
| 	if (port < 0 || port >= OR_PORT_NR)	{
 | |
| 		/* illegal port in message */
 | |
| 		reply.m_type = DL_CONF_REPLY;
 | |
| 		reply.m3_i1 = ENXIO;
 | |
| 		mess_reply (mp, &reply);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* the port resolves to the main orinoco structure */
 | |
| 	orp = &or_table[port];
 | |
| 	/* resolving to the main hardware structure */
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	if (orp->or_mode == OR_M_DISABLED) {
 | |
| 		/* Initialize the orp structure */
 | |
| 		or_init_struct (orp);
 | |
| 		if (orp->or_mode == OR_M_DISABLED) {
 | |
| 			reply.m_type = DL_CONF_REPLY;
 | |
| 			reply.m3_i1 = ENXIO;
 | |
| 			mess_reply (mp, &reply);
 | |
| 			return;
 | |
| 		}
 | |
| 		if (orp->or_mode == OR_M_ENABLED) {
 | |
| 			/* initialize card, hardware/firmware */
 | |
| 			orp->or_flags |= OR_F_ENABLED;
 | |
| 			or_init_hw (orp);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	/* Not supported by the driver yet, but set a couple of options:
 | |
| 	 * multicasting, promiscuity, broadcasting, depending on the users 
 | |
|          * needs */
 | |
| 	orp->or_flags &= ~(OR_F_PROMISC | OR_F_MULTI | OR_F_BROAD);
 | |
| 	if (mp->DL_MODE & DL_PROMISC_REQ)
 | |
| 		orp->or_flags |= OR_F_PROMISC;
 | |
| 	if (mp->DL_MODE & DL_MULTI_REQ)
 | |
| 		orp->or_flags |= OR_F_MULTI;
 | |
| 	if (mp->DL_MODE & DL_BROAD_REQ)
 | |
| 		orp->or_flags |= OR_F_BROAD;
 | |
| 
 | |
| 	orp->or_client = mp->m_source;
 | |
| 	or_rec_mode (orp);
 | |
| 
 | |
| 	/* reply the caller that the configuration succeeded */
 | |
| 	reply.m_type = DL_CONF_REPLY;
 | |
| 	reply.m3_i1 = mp->DL_PORT;
 | |
| 	reply.m3_i2 = OR_PORT_NR;
 | |
| 	*(ether_addr_t *) reply.m3_ca1 = orp->or_address;
 | |
| 	mess_reply (mp, &reply);
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_pci_conf                                                *
 | |
|  *                                                                           *
 | |
|  * Configure the pci related issues of the card, e.g. finding out where the  *
 | |
|  * card is in the pci configuration, it's assigned irq, etc. This can be     *
 | |
|  * done if the boot monitor is provided with information, or the pci bus     *
 | |
|  * can be searched (at the end: or_probe function)                           *
 | |
|  *****************************************************************************/
 | |
| static void or_pci_conf () {
 | |
| 	long v;
 | |
| 	t_or *orp;
 | |
| 	int i, h;
 | |
| 	static char envfmt[] = "*:d.d.d";
 | |
| 	static char envvar[] = OR_ENVVAR "#";
 | |
| 	static char val[128];
 | |
| 
 | |
| 	/* extract information from the boot monitor about the pci 
 | |
| 	 * configuration if provided */
 | |
| 	for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++)	{
 | |
| 		strncpy (orp->or_name, OR_NAME, sizeof(OR_NAME));
 | |
| 		orp->or_name[sizeof(OR_NAME) - 2] = i + '0';
 | |
| 		orp->or_seen = FALSE;
 | |
| 		/* whats this envvar; whats the definition;*/
 | |
| 		/* i guess this whole loop could be removed*/
 | |
| 		envvar[sizeof (OR_ENVVAR) - 1] = '0' + i;
 | |
| 		if (0 == env_get_param(envvar, val, sizeof(val)) && 
 | |
| 			! env_prefix(envvar, "pci")) {
 | |
| 			env_panic(envvar);
 | |
| 		}
 | |
| 		v = 0;
 | |
| 		(void) env_parse (envvar, envfmt, 1, &v, 0, 255);
 | |
| 		orp->or_pci_bus = v;
 | |
| 		v = 0;
 | |
| 		(void) env_parse (envvar, envfmt, 2, &v, 0, 255);
 | |
| 		orp->or_pci_dev = v;
 | |
| 		v = 0;
 | |
| 		(void) env_parse (envvar, envfmt, 3, &v, 0, 255);
 | |
| 		orp->or_pci_func = v;
 | |
| 	}
 | |
| 
 | |
| 	/* Initialize the pci bus, bridges and cards, if not yet done */
 | |
| 	pci_init ();
 | |
| 	
 | |
| 	/* Try to find out where the card(s) are in the pci bus */
 | |
| 	for (h = 1; h >= 0; h--)
 | |
| 		for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++)	{
 | |
| 			if (((orp->or_pci_bus | orp->or_pci_dev |
 | |
| 			      orp->or_pci_func) != 0) != h)	{
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (or_probe (orp))
 | |
| 				orp->or_seen = TRUE;
 | |
| 		}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_probe                                                   *
 | |
|  *                                                                           *
 | |
|  * Try to find the card based on information provided by pci and get irq and *
 | |
|  * bar                                                                       *
 | |
|  *****************************************************************************/
 | |
| static int or_probe (t_or * orp) {
 | |
| 	u8_t ilr;
 | |
| 	u32_t bar, reg, cpuspace_bar;
 | |
| 	char *dname;
 | |
| 	u16_t vid, did;
 | |
| 	int i, r, devind, just_one;
 | |
| 
 | |
| 	if ((orp->or_pci_bus | orp->or_pci_dev | orp->or_pci_func) != 0) {
 | |
| 		/* The monitor has provided us with clues about where the 
 | |
|                  * device is. Try to find it at that place */
 | |
| 		r = pci_find_dev (orp->or_pci_bus, orp->or_pci_dev,
 | |
| 				  orp->or_pci_func, &devind);
 | |
| 		if (r == 0)	{
 | |
| 			printf ("%s: no PCI found at %d.%d.%d\n",
 | |
| 				orp->or_name, orp->or_pci_bus,
 | |
| 				orp->or_pci_dev, orp->or_pci_func);
 | |
| 			return (0);
 | |
| 		}
 | |
| 		/* get the information about the card, vendor id and device
 | |
| 		 * id */
 | |
| 		pci_ids (devind, &vid, &did);
 | |
| 		just_one = TRUE;
 | |
| 	} else {
 | |
| 		/* no clue where the card is. Start looking from the
 | |
| 		 * beginning */
 | |
| 		r = pci_first_dev (&devind, &vid, &did);
 | |
| 		if (r == 0)
 | |
| 			return (0);
 | |
| 		just_one = FALSE;
 | |
| 	}
 | |
| 
 | |
| 	while (TRUE) {
 | |
| 		/* loop through the pcitab to find a maching entry. The match
 | |
| 		 * being between one of the values in pcitab and the 
 | |
| 		 * information provided by the pci bus */
 | |
| 		for (i = 0; pcitab[i].vid != 0; i++) {
 | |
| 			if (pcitab[i].vid != vid)
 | |
| 				continue;
 | |
| 			if (pcitab[i].did != did)
 | |
| 				continue;
 | |
| 			if (pcitab[i].checkclass) {
 | |
|                            panic(__FILE__, "or_probe:class check not implmnted", 
 | |
|                                  NO_NUM);
 | |
| 			}
 | |
| 			/* we have found the card in the pci bus */
 | |
| 			break;
 | |
| 		}
 | |
| 		if (pcitab[i].vid != 0)
 | |
| 			break;
 | |
| 
 | |
| 		if (just_one) {
 | |
| 			printf ("%s: wrong PCI device", orp->or_name);
 | |
| 			printf (" (%04x/%04x) found at %d.%d.%d\n", vid, did,
 | |
| 				orp->or_pci_bus, orp->or_pci_dev,
 | |
| 				orp->or_pci_func);
 | |
| 			return (0);
 | |
| 		}
 | |
| 
 | |
| 		/* if the pci device which was under consideration was not
 | |
| 		 * of the desired brand or type, get the next device */
 | |
| 		r = pci_next_dev (&devind, &vid, &did);
 | |
| 		if (!r)
 | |
| 			return (0);
 | |
| 	}
 | |
| 
 | |
| 	/* Get the name as advertised by pci */
 | |
| 	dname = pci_dev_name (vid, did);
 | |
| 	if (!dname)
 | |
| 		dname = "unknown device";
 | |
| 	printf ("%s: %s (%04x/%04x) at %s\n",
 | |
| 		orp->or_name, dname, vid, did, pci_slot_name (devind));
 | |
| 
 | |
| 	pci_reserve (devind);
 | |
| 
 | |
| 	orp->devind = devind;	
 | |
| 	/* Get the irq */
 | |
| 	ilr = pci_attr_r8 (devind, PCI_ILR);
 | |
| 	orp->or_irq = ilr;
 | |
| 
 | |
| 	/* Get the base address */
 | |
| 	bar = or_get_bar (devind, orp);
 | |
| 	orp->or_base_port = bar;
 | |
| 
 | |
| 	map_hw_buffer(orp);
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                map_hw_buffer                                              *
 | |
|  *                                                                           *
 | |
|  * Map the memory mapped registers into user space memory                    *
 | |
|  *****************************************************************************/
 | |
| static void map_hw_buffer(t_or *orp) {
 | |
| 	int r;
 | |
| 	size_t o, size, reg_size;
 | |
| 	char *buf, *abuf;	
 | |
| 	hermes_t *hw = &(orp->hw);	
 | |
| 
 | |
| 	/* This way, the buffer will be at least I386_PAGE_SIZE big: see 
 | |
| 	 * calculation with the offset */
 | |
| 	size = 2 * I386_PAGE_SIZE;	
 | |
| 
 | |
| 	buf = (char *)malloc(size);
 | |
| 	if(buf == NULL) 
 | |
| 		panic(__FILE__, "map_hw_buffer: cannot malloc size:", size);
 | |
| 
 | |
| 	/* Let the mapped memory by I386_PAGE_SIZE aligned */
 | |
| 	o = I386_PAGE_SIZE - ((vir_bytes)buf % I386_PAGE_SIZE);
 | |
| 	abuf = buf + o;
 | |
| 
 | |
| #if 0
 | |
| 	r = sys_vm_map(SELF, 1, (vir_bytes)abuf, 
 | |
| 			1 * I386_PAGE_SIZE, (phys_bytes)orp->or_base_port);
 | |
| #else
 | |
| 	r = ENOSYS;
 | |
| #endif
 | |
| 
 | |
| 	if(r!=OK) 
 | |
| 		panic(__FILE__, "map_hw_buffer: sys_vm_map failed:", r);
 | |
| 
 | |
| 
 | |
| 	hw->locmem = abuf;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_get_bar                                                 *
 | |
|  *                                                                           *
 | |
|  * Get the base address from pci (from Base Address Register) and find out   * 
 | |
|  * whether the card is memory mapped or in I/O space. Currently, only        *
 | |
|  * memmory mapped is supported.                                              *
 | |
|  *****************************************************************************/
 | |
| static u32_t or_get_bar (int devind, t_or * orp) {
 | |
| 
 | |
| 	u32_t bar, desired_bar;
 | |
| 	int is_iospace, i;
 | |
| 	u16_t check, check2;
 | |
| 	hermes_t *hw = &(orp->hw);
 | |
| 
 | |
| 	/* bit 1 off the PCI_BAR register indicates whether the cards registers
 | |
| 	 * are mapped in io-space or shared memory */
 | |
| 	is_iospace = pci_attr_r32 (devind, PCI_BAR) & 1;
 | |
| 
 | |
| 	if (is_iospace)	{
 | |
| 		/* read where the base address is in I/O space */
 | |
| 		bar = pci_attr_r32 (devind, PCI_BAR) & 0xffffffe0;
 | |
| 
 | |
| 		if ((bar & 0x3ff) >= 0x100 - 32 || bar < 0x400)
 | |
| 			panic(__FILE__,"base address isn't properly configured",
 | |
|                               NO_NUM);
 | |
| 
 | |
| 		/* In I/O space registers are 2 bytes wide, without any spacing
 | |
| 		 * in between */
 | |
| 		hermes_struct_init (hw, bar, is_iospace,
 | |
| 				    HERMES_16BIT_REGSPACING);
 | |
| 
 | |
| 		if (debug) {
 | |
| 			printf ("%s: using I/O space address 0x%lx, IRQ %d\n",
 | |
| 				orp->or_name, bar, orp->or_irq);
 | |
| 		}
 | |
| 
 | |
| 		panic(__FILE__, "Not implemente yet", NO_NUM);
 | |
| 		/* Although we are able to find the desired bar and irq for an 
 | |
| 		 * I/O spaced card, we haven't implemented the right register 
 | |
|  		 * accessing functions. This wouldn't be difficult, but we were
 | |
| 		 * not able to test them. Therefore, give an alert here */
 | |
| 
 | |
| 		return bar;
 | |
| 	} else {
 | |
| 		/* read where the base address is in shared memory */
 | |
| 		bar = pci_attr_r32 (devind, PCI_BAR) & 0xfffffff0;
 | |
| 		/* maybe some checking whether the address is legal... */
 | |
| 
 | |
| 		/* Memory mapped registers are 2 bytes wide, aligned on 4 
 | |
| 		 * bytes */
 | |
| 		hermes_struct_init (hw, bar, is_iospace,
 | |
| 				    HERMES_32BIT_REGSPACING);
 | |
| 
 | |
| 		if (debug){
 | |
| 			printf ("%s: using shared memory address",
 | |
| 				orp->or_name);
 | |
| 			printf (" 0x%lx, IRQ %d\n", bar, orp->or_irq);
 | |
| 		}
 | |
| 
 | |
| 		return bar;
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_init_struct                                             *
 | |
|  *                                                                           *
 | |
|  * Set the orinoco structure to default values                               *
 | |
|  *****************************************************************************/
 | |
| static void or_init_struct (t_or * orp) {
 | |
| 	int i = 0;
 | |
| 	static eth_stat_t empty_stat = { 0, 0, 0, 0, 0, 0 };
 | |
| 
 | |
| 	orp->or_mode = OR_M_DISABLED;
 | |
| 
 | |
| 	if (orp->or_seen)
 | |
| 		orp->or_mode = OR_M_ENABLED;
 | |
| 
 | |
| 	if (orp->or_mode != OR_M_ENABLED)
 | |
| 		return;
 | |
| 
 | |
| 	orp->or_got_int = 0;
 | |
| 	orp->or_link_up = -1;
 | |
| 	orp->or_send_int = 0;
 | |
| 	orp->or_clear_rx = 0;
 | |
| 	orp->or_tx_alive = 0;
 | |
| 	orp->or_need_reset = 0;
 | |
| 
 | |
| 	orp->or_read_s = 0;
 | |
| 	orp->or_tx_head = 0;
 | |
| 	orp->or_tx_tail = 0;
 | |
| 	orp->connected = 0;
 | |
|  	
 | |
| 	orp->or_tx.ret_busy = FALSE;
 | |
| 	orp->or_tx.or_txfid = NO_FID;
 | |
| 
 | |
| 	for(i = 0; i < NR_RX_BUFS; i++) {
 | |
| 		orp->rxfid[i] = NO_FID;
 | |
| 		orp->rx_length[i] = 0;
 | |
| 	}
 | |
| 	orp->rx_current = 0;
 | |
| 	orp->rx_first = 0;
 | |
| 	orp->rx_last = 0;
 | |
| 	
 | |
| 	orp->or_stat = empty_stat;
 | |
| 	orp->or_flags = OR_F_EMPTY;
 | |
| 
 | |
| 	/* Keep an administration in the driver whether the internal
 | |
| 	   buffer is in use. That's what ret_busy is for */
 | |
| 	orp->or_tx.ret_busy = FALSE;
 | |
| 
 | |
| 	orp->or_nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
 | |
| 
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_init_hw                                                 *
 | |
|  *                                                                           *
 | |
|  * Initialize hardware and prepare for intercepting the interrupts. At the   *
 | |
|  * end, the card is up and running                                           *
 | |
|  *****************************************************************************/
 | |
| static void or_init_hw (t_or * orp) {
 | |
| 	int i, err, s;
 | |
| 	hermes_t *hw = &(orp->hw);
 | |
| 	static int first_time = TRUE;
 | |
| 
 | |
| 	/* first step in starting the card */
 | |
| 	if (hermes_cor_reset(hw) != 0) {
 | |
| 		printf ("%s: Failed to start the card\n", orp->or_name);
 | |
| 	}
 | |
| 
 | |
| 	/* here begins the real things, yeah! ;) */
 | |
| 	if (err = hermes_init (hw))	{
 | |
| 		printf ("error value of hermes_init(): %d\n", err);
 | |
| 	}
 | |
| 
 | |
| 	/* Get the MAC address (which is a data item in the card)*/
 | |
| 	or_readrids (hw, orp);
 | |
| 
 | |
| 	/* Write a few rids to the card, e.g. WEP key*/
 | |
| 	or_writerids (hw, orp);
 | |
| 
 | |
| 	if (debug) {
 | |
| 		printf ("%s: Ethernet address ", orp->or_name);
 | |
| 		for (i = 0; i < 6; i++)	{
 | |
| 			printf ("%x%c", orp->or_address.ea_addr[i],
 | |
| 				i < 5 ? ':' : '\n');
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Prepare internal TX buffer in the card */
 | |
| 	err = hermes_allocate (hw,
 | |
| 				   orp->or_nicbuf_size,
 | |
| 				   &(orp->or_tx.or_txfid));
 | |
| 
 | |
| 	if (err)
 | |
| 		printf ("%s:Error %d allocating Tx buffer\n",
 | |
| 			orp->or_name, err);
 | |
| 
 | |
| 	/* Establish event handle */
 | |
| 	if(first_time) {
 | |
| 		orp->or_hook_id = orp->or_irq;	
 | |
| 		if ((s=sys_irqsetpolicy(orp->or_irq, 0, 
 | |
| 			&orp->or_hook_id)) != OK)
 | |
| 			printf("orinoco: couldn't set IRQ policy: %d\n", s);
 | |
| 
 | |
| 		if ((s=sys_irqenable(&orp->or_hook_id)) != OK)
 | |
| 			printf("orinoco: couldn't enable interrupts: %d\n", s);
 | |
| 		first_time = FALSE;
 | |
| 	}
 | |
| 
 | |
| 	/* Tell the card which events should raise an interrupt to the OS */
 | |
| 	hermes_set_irqmask (hw, ORINOCO_INTEN);
 | |
| 
 | |
| 	/* Enable operation */
 | |
| 	err = hermes_docmd_wait (hw, HERMES_CMD_ENABLE, 0, NULL);
 | |
| 	if (err) {
 | |
| 		printf ("%s: Error %d enabling MAC port\n", orp->or_name, err);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_readrids                                                *
 | |
|  *                                                                           *
 | |
|  * Read some default rids from the card. A rid (resource identifier)         *
 | |
|  * is a data item in the firmware, some configuration variable.              *
 | |
|  * In our case, we are mostly interested in the MAC address for now          *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static void or_readrids (hermes_t * hw, t_or * orp) {
 | |
| 	int err, len, i;
 | |
| 	struct hermes_idstring nickbuf;
 | |
| 	u16_t reclen, d;
 | |
| 
 | |
| 	/* Read the MAC address */
 | |
| 	err = hermes_read_ltv (hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
 | |
| 			       ETH_ALEN, NULL, &orp->or_address);
 | |
| 	if (err) {
 | |
| 		printf ("%s: failed to read MAC address!\n", orp->or_name);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_writerids                                               *
 | |
|  *                                                                           *
 | |
|  * Write some default rids to the card. A rid (resource identifier)          *
 | |
|  * is a data item in the firmware, some configuration variable, e.g. WEP key *
 | |
|  *****************************************************************************/
 | |
| static void or_writerids (hermes_t * hw, t_or * orp) {
 | |
| 	int err;
 | |
| 	struct hermes_idstring idbuf;
 | |
| 	u16_t port_type, max_data_len, reclen;
 | |
| 	static char essid[IW_ESSID_MAX_SIZE + 1];
 | |
| 	static char wepkey0[LARGE_KEY_LENGTH + 1];
 | |
| 
 | |
| 	/* Set the MAC port */
 | |
| 	port_type = 1;
 | |
| 	err = hermes_write_wordrec (hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
 | |
| 				    port_type);
 | |
| 	if (err) {
 | |
| 		printf ("%s: Error %d setting port type\n", orp->or_name, err);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (OK != env_get_param("essid", essid, sizeof(essid))) {
 | |
| 		essid[0] = 0;
 | |
| 	}
 | |
| 
 | |
| 	if(strlen(essid) == 0) {
 | |
| 		printf("%s: no essid provided in boot monitor!\n",
 | |
| 			orp->or_name);
 | |
| 		printf("Hope you'll connect to the right network... \n");
 | |
| 	}
 | |
| 
 | |
| 	/* Set the desired ESSID */
 | |
| 	idbuf.len = strlen (essid);
 | |
| 	memcpy (&idbuf.val, essid, sizeof (idbuf.val));
 | |
| 
 | |
| 	err = hermes_write_ltv (hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
 | |
| 				HERMES_BYTES_TO_RECLEN (strlen (essid) + 2),
 | |
|                                 &idbuf);
 | |
| 	if (err) {
 | |
| 		printf ("%s: Error %d setting DESIREDSSID\n", 
 | |
| 				orp->or_name, err);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (OK != env_get_param("wep", wepkey0, sizeof(wepkey0))) {
 | |
| 		wepkey0[0] = 0;
 | |
| 	}
 | |
| 
 | |
| 	switch(strlen(wepkey0)) {
 | |
| 		case 0:
 | |
| 			/* No key found in monitor, using no encryption */
 | |
| 		break;
 | |
| 		case LARGE_KEY_LENGTH:
 | |
| 			setup_wepkey(orp, wepkey0);
 | |
| 		break;
 | |
| 		default:
 | |
| 			printf("Invalid key provided. Has to be 13 chars\n");
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                setup_wepkey                                               *
 | |
|  *                                                                           *
 | |
|  * If a wepkey is provided in the boot monitor, set the necessary rids so    *
 | |
|  * that the card will decrypt received data and encrypt data to send by      *
 | |
|  * by default with this key.                                                 *
 | |
|  * It appears that there is a severe bug in setting up WEP. If the driver    *
 | |
|  * doesnt function properly, please turn WEP off.                            *
 | |
|  *****************************************************************************/
 | |
| static void setup_wepkey(t_or *orp, char *wepkey0) {
 | |
| 	int default_key = 0, err = 0;
 | |
| 	hermes_t *hw = &(orp->hw);
 | |
| 
 | |
| 	err = hermes_write_wordrec (hw, USER_BAP,
 | |
| 					HERMES_RID_CNFWEPDEFAULTKEYID,
 | |
| 					default_key);
 | |
| 	if (err)
 | |
| 		printf ("%s: Error %d setting the default WEP-key entry\n",
 | |
| 				orp->or_name, err);	
 | |
| 	
 | |
| 	err = hermes_write_ltv (hw, USER_BAP, 
 | |
| 				HERMES_RID_CNFDEFAULTKEY0,
 | |
| 				HERMES_BYTES_TO_RECLEN(LARGE_KEY_LENGTH),
 | |
| 				wepkey0);
 | |
| 	if (err) 
 | |
| 		printf ("%s: Error %d setting the WEP-key0\n",
 | |
| 				orp->or_name, err);	
 | |
| 	
 | |
| 	err = hermes_write_wordrec (hw, USER_BAP, 
 | |
| 					HERMES_RID_CNFAUTHENTICATION,
 | |
| 					HERMES_AUTH_OPEN);
 | |
| 	if (err)
 | |
| 		printf ("%s: Error %d setting the authentication flag\n",
 | |
| 			orp->or_name, err);	
 | |
| 
 | |
| 	err = hermes_write_wordrec (hw, USER_BAP, 
 | |
| 					HERMES_RID_CNFWEPFLAGS_INTERSIL,
 | |
| 					HERMES_WEP_PRIVACY_INVOKED);
 | |
| 	if (err)
 | |
| 		printf ("%s: Error %d setting the master wep setting flag\n",
 | |
| 			orp->or_name, err);	
 | |
| 	
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_rec_mode                                                *
 | |
|  *                                                                           *
 | |
|  * Set the desired receive mode, e.g. promiscuous mode. Not implemented yet   *
 | |
|  *****************************************************************************/
 | |
| static void or_rec_mode (t_or * orp) {
 | |
| 	/* TODO */
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_handler                                                 *
 | |
|  *                                                                           *
 | |
|  * The handler which is called when the card generated an interrupt. Events  *
 | |
|  * like EV_INFO and EV_RX have to be handled before an acknowledgement for   *
 | |
|  * the event is returned to the card. See also the documentation             *
 | |
|  *****************************************************************************/
 | |
| static int or_handler (t_or *orp) {
 | |
| 	int i, err, length, nr = 0;
 | |
| 	u16_t evstat, events, fid;
 | |
| 	hermes_t *hw;
 | |
| 	struct hermes_tx_descriptor desc;
 | |
| 	
 | |
| 	hw = &(orp->hw);
 | |
| 	
 | |
| beginning:
 | |
| 	/* Retrieve which kind of event happened */
 | |
| 	evstat = hermes_read_reg (hw, HERMES_EVSTAT);
 | |
| 	events = evstat;
 | |
| 
 | |
| 	/* There are plenty of events possible. The more interesting events
 | |
| 	   are actually implemented. Whether the following events actually
 | |
| 	   raise an interrupt depends on the value of ORINOCO_INTEN. For more
 | |
| 	   information about the events, see the specification in pdf */
 | |
| 
 | |
| 	/* Occurs at each tick of the auxiliary time */
 | |
| 	if (events & HERMES_EV_TICK) {
 | |
| 		events &= ~HERMES_EV_TICK;
 | |
| 	}
 | |
| 	/* Occurs when a wait time-out error is detected */
 | |
| 	if (events & HERMES_EV_WTERR) {
 | |
| 		events &= ~HERMES_EV_WTERR;
 | |
| 	}
 | |
| 
 | |
| 	/* Occurs when an info frame is dropped because there is not enough
 | |
| 	   buffer space available */
 | |
| 	if (events & HERMES_EV_INFDROP) {
 | |
| 		events &= ~(HERMES_EV_INFDROP);
 | |
| 	}
 | |
| 	
 | |
| 	/* This AP-only event will be asserted at the beacon interval prior to 
 | |
| 	   the DTIM interval */
 | |
| 	if (events & HERMES_EV_DTIM) {
 | |
| 		events &= ~(HERMES_EV_DTIM);
 | |
| 	}
 | |
| 
 | |
|  	/* Occurs when a command execution is completed */
 | |
| 	if (events & HERMES_EV_CMD) {
 | |
| 		events &= ~(HERMES_EV_CMD);
 | |
| 	}
 | |
| 
 | |
| 	/* Occurs when the asynchronous transmission process is unsuccessfully
 | |
| 	   completed */
 | |
| 	if (events & HERMES_EV_TXEXC) {
 | |
| 
 | |
| 		/* What buffer generated the event? Represented by an fid */
 | |
| 		fid = hermes_read_reg(hw, HERMES_TXCOMPLFID);
 | |
| 		if(fid == 0xFFFF) {
 | |
| 			/* Illegal fid found */
 | |
| 			printf("unexpected txexc_fid interrupted\n");
 | |
| 		}
 | |
| 
 | |
| 		orp->or_tx.ret_busy = FALSE;
 | |
| 
 | |
| 		if(orp->or_flags & OR_F_SEND_AVAIL)	{
 | |
| 		 	orp->or_send_int = TRUE;
 | |
| 			if (!orp->or_got_int){
 | |
| 				orp->or_got_int = TRUE;
 | |
| 				int_event_check = TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* To detect illegal fids */
 | |
| 		hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF);
 | |
| 		events &= ~(HERMES_EV_TXEXC);
 | |
| 		/* We don't do anything else yet. 
 | |
| 		 * Could be used for statistics */
 | |
| 	}
 | |
| 
 | |
| 	/* Occurs when the asynchronous transmission process is successfully
 | |
| 	   completed */
 | |
| 	if (events & HERMES_EV_TX) {
 | |
| 		events &= ~(HERMES_EV_TX);
 | |
| 		/* Which buffer was sent, represented by an fid */
 | |
| 		fid = hermes_read_reg (hw, HERMES_TXCOMPLFID);
 | |
| 		if(fid == 0xFFFF) {
 | |
| 			/* Illegal fid found */
 | |
| 			printf("unexpected tx_fid interrupted\n");
 | |
| 		}
 | |
| 
 | |
| 		orp->or_tx.ret_busy = FALSE;
 | |
| 
 | |
| 		if(orp->or_flags & OR_F_SEND_AVAIL)	{
 | |
| 		 	orp->or_send_int = TRUE;
 | |
| 			if (!orp->or_got_int){
 | |
| 				orp->or_got_int = TRUE;
 | |
| 				int_event_check = TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* To detect illegal fids */
 | |
| 		hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF);
 | |
| 		/* We don't do anything else when such event happens */
 | |
| 	}
 | |
| 
 | |
| 	/* Occurs when an info frame is available in the card */
 | |
| 	if (events & HERMES_EV_INFO) {
 | |
| 		events &= ~(HERMES_EV_INFO);
 | |
| 		/* Process the information, inside the handler (!) */
 | |
| 		or_ev_info(orp);
 | |
| 	}
 | |
| 
 | |
| 	/* Occurs when a TX buffer is available again for usage */
 | |
| 	if (events & HERMES_EV_ALLOC) {
 | |
| 		/* Which frame is now marked as free? */
 | |
| 		fid = hermes_read_reg (hw, HERMES_ALLOCFID);
 | |
| 		if (fid == 0xFFFF){
 | |
| 			/* An illegal frame identifier is found. Ignore */
 | |
| 			printf("Allocate event on unexpected fid\n");
 | |
| 			return ;
 | |
| 		}
 | |
| 
 | |
| 		/* To be able to detect illegal fids */
 | |
| 		hermes_write_reg(hw, HERMES_ALLOCFID, 0xFFFF);
 | |
| 		
 | |
| 		events &= ~(HERMES_EV_ALLOC);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* Occurs when a frame is received by the asynchronous reception 
 | |
| 	 * process */
 | |
| 
 | |
| 	if (events & HERMES_EV_RX) {
 | |
| 		orp->or_ev_rx = TRUE;
 | |
| 		events &= ~(HERMES_EV_RX);
 | |
| 
 | |
| 		/* If the last buffer is still filled with data, then we don't 
 | |
| 		 * have any buffers available to store the data */
 | |
| 		if(orp->rx_length[orp->rx_last] != 0) {
 | |
| 			/* indeed, we are going to overwrite information
 | |
| 			 * in a buffer */
 | |
| 		}
 | |
| 
 | |
| 		/* Which buffer is storing the data (represented by a fid) */
 | |
| 		orp->rxfid[orp->rx_last]
 | |
| 				 = hermes_read_reg (hw, HERMES_RXFID);
 | |
| 
 | |
| 		/* Get the packet from the card and store it in 
 | |
| 		 * orp->rx_buf[orp->rx_last]. The length is returned by this 
 | |
| 		 * function */
 | |
| 		length = or_get_recvd_packet(orp, orp->rxfid[orp->rx_last],
 | |
| 						(orp->rx_buf[orp->rx_last]));
 | |
| 
 | |
| 		if(length < 0) {
 | |
| 			/* Error happened. */
 | |
| 			printf("length < 0\n");
 | |
| 			goto next;
 | |
| 		} else {
 | |
| 			orp->rx_length[orp->rx_last] = length;
 | |
| 		}
 | |
| 
 | |
| 		/* The next buffer will be used the next time, circularly */
 | |
| 		orp->rx_last++;
 | |
|  		orp->rx_last %= NR_RX_BUFS;
 | |
| 
 | |
| 		if (!orp->or_got_int){
 | |
| 			orp->or_got_int = TRUE;
 | |
| 		}
 | |
| 		int_event_check = TRUE;
 | |
| 	}
 | |
| next:
 | |
| 	if (events)	{
 | |
| 		printf("Unknown event: 0x%x\n", events);
 | |
| 	}
 | |
| 
 | |
| 	/* Acknowledge to the card that the events have been processed. After 
 | |
| 	 * this the card will assume we have processed any buffer which were in
 | |
| 	 * use for this event. */
 | |
| 	hermes_write_reg (hw, HERMES_EVACK, evstat);
 | |
| 
 | |
| 	evstat = hermes_read_reg (hw, HERMES_EVSTAT);
 | |
| 	if(evstat != 0 && !(evstat & HERMES_EV_TICK)) {
 | |
| 		goto beginning;
 | |
| 	}
 | |
| 
 | |
| 	return (1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_watchdog_f                                              *
 | |
|  *                                                                           *
 | |
|  * Will be called regularly to see whether the driver has crashed. If that   *
 | |
|  * condition is detected, reset the driver and card                          *
 | |
|  *****************************************************************************/
 | |
| static void or_watchdog_f(timer_t *tp) {
 | |
| 	int i;
 | |
| 	t_or *orp;
 | |
| 	
 | |
| 	/* Use a synchronous alarm instead of a watchdog timer. */
 | |
| 	sys_setalarm(system_hz, 0);
 | |
| 
 | |
| 	for (i= 0, orp = &or_table[0]; i<OR_PORT_NR; i++, orp++) {
 | |
| 		if (orp->or_mode != OR_M_ENABLED)
 | |
| 			continue;
 | |
| 
 | |
| 		if (!(orp->or_flags & OR_F_SEND_AVAIL))	{
 | |
| 			/* Assume that an idle system is alive */
 | |
| 			orp->or_tx_alive= TRUE;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if (orp->connected == 0) {
 | |
| 			orp->or_tx_alive= TRUE;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (orp->or_tx_alive) {
 | |
| 			orp->or_tx_alive= FALSE;
 | |
| 			continue;
 | |
| 		}
 | |
| 		
 | |
| 		printf("or_watchdog_f: resetting port %d\n", i);
 | |
| 		
 | |
| 		orp->or_need_reset= TRUE;
 | |
| 		orp->or_got_int= TRUE;
 | |
| 		check_int_events();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                mess_reply                                                 *
 | |
|  *****************************************************************************/
 | |
| 
 | |
| static void mess_reply (message * req, message * reply_mess) {
 | |
| 	if (send (req->m_source, reply_mess) != 0)
 | |
| 		panic(__FILE__, "orinoco: unable to mess_reply", NO_NUM);
 | |
| 
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_writev                                                  *
 | |
|  *                                                                           *
 | |
|  * As far as we can see, this function is never called from 3.1.3. However,  *
 | |
|  * it is still in rtl8139, so we'll keep it here as well. It's almost a copy *
 | |
|  * of or_writev_s. We left out the comments. For an explanation, see         *
 | |
|  * or_writev_s                                                               *
 | |
| ******************************************************************************/
 | |
| static void or_writev (message * mp, int from_int, int vectored) {
 | |
| 	int port, or_client, count, size, err, data_len, data_off, tx_head;
 | |
| 	int o, j, n, i, s, p, cps ;
 | |
| 	struct ethhdr *eh;
 | |
| 	t_or *orp;
 | |
| 	clock_t timebefore, t0;	
 | |
| 	phys_bytes phys_user, iov_src;
 | |
| 	hermes_t *hw;
 | |
| 	struct hermes_tx_descriptor desc;
 | |
| 	struct header_struct hdr;
 | |
| 
 | |
| 	iovec_t *iovp;
 | |
| 	phys_bytes phys_databuf;
 | |
| 	u16_t txfid;
 | |
| 	static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1];
 | |
| 	memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3);
 | |
| 
 | |
| 	port = mp->DL_PORT;
 | |
| 	count = mp->DL_COUNT;
 | |
| 	if (port < 0 || port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port", NO_NUM);
 | |
| 	
 | |
| 	or_client = mp->DL_PROC;
 | |
| 	orp = &or_table[port];
 | |
| 	orp->or_client = or_client;
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	if (from_int) {
 | |
| 		assert (orp->or_flags & OR_F_SEND_AVAIL);
 | |
| 		orp->or_flags &= ~OR_F_SEND_AVAIL;
 | |
| 		orp->or_send_int = FALSE;
 | |
| 		orp->or_tx_alive = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	if (orp->or_tx.ret_busy) {
 | |
| 		assert(!(orp->or_flags & OR_F_SEND_AVAIL));
 | |
| 		orp->or_flags |= OR_F_SEND_AVAIL;
 | |
| 		goto suspend_write;
 | |
| 	}
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	if (vectored) {
 | |
| 
 | |
| 		int iov_offset = 0;
 | |
| 		size = 0;
 | |
| 		o = 0;
 | |
| 
 | |
| 		for (i = 0; i < count; i += IOVEC_NR,
 | |
| 		     iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]), 
 | |
| 			 iov_offset += IOVEC_NR * sizeof (orp->or_iovec[0])) {
 | |
| 
 | |
| 			n = IOVEC_NR;
 | |
| 			if (i + n > count)
 | |
| 				n = count - i;
 | |
| 			cps = sys_vircopy(or_client, D, 
 | |
| 				((vir_bytes) mp->DL_ADDR) + iov_offset,
 | |
| 				SELF, D, (vir_bytes) orp->or_iovec,
 | |
| 				n * sizeof(orp->or_iovec[0]));
 | |
| 			if (cps != OK) printf("sys_vircopy failed: %d\n", cps);
 | |
| 
 | |
| 			for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) {
 | |
| 				s = iovp->iov_size;
 | |
| 				if (size + s > ETH_MAX_PACK_SIZE_TAGGED) {
 | |
| 					printf("invalid packet size\n");
 | |
| 				}
 | |
| 				cps = sys_vircopy(or_client, D, iovp->iov_addr,
 | |
| 					SELF, D, (vir_bytes) databuf + o, s);
 | |
| 				if (cps != OK) 
 | |
| 					printf("sys_vircopy failed: %d\n",cps);
 | |
| 
 | |
| 				size += s;
 | |
| 				o += s;
 | |
| 			}
 | |
| 		}
 | |
| 		if (size < ETH_MIN_PACK_SIZE)
 | |
| 			printf("invalid packet size %d\n", size);
 | |
| 	} else {
 | |
| 		size = mp->DL_COUNT;
 | |
| 		if (size < ETH_MIN_PACK_SIZE
 | |
| 		    || size > ETH_MAX_PACK_SIZE_TAGGED)
 | |
| 			printf("invalid packet size %d\n", size);
 | |
| 
 | |
| 		cps = sys_vircopy(or_client, D, (vir_bytes)mp->DL_ADDR, 
 | |
| 			SELF, D, (vir_bytes) databuf, size);
 | |
| 		if (cps != OK) printf("sys_abscopy failed: %d\n", cps);
 | |
| 	}
 | |
| 
 | |
| 	memset (&desc, 0, sizeof (desc));
 | |
| 	desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
 | |
| 
 | |
| 	err = hermes_bap_pwrite (hw, USER_BAP, &desc, sizeof (desc), txfid,
 | |
| 				 0);
 | |
| 	if (err) {
 | |
| 		or_reset();
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	eh = (struct ethhdr *) databuf;
 | |
| 	if (ntohs (eh->h_proto) > 1500) {
 | |
| 
 | |
| 		data_len = size - ETH_HLEN;
 | |
| 		data_off = HERMES_802_3_OFFSET + sizeof (hdr);
 | |
| 
 | |
| 		memcpy (hdr.dest, eh->h_dest, ETH_ALEN);
 | |
| 		memcpy (hdr.src, eh->h_src, ETH_ALEN);
 | |
| 		hdr.len = htons (data_len + ENCAPS_OVERHEAD);
 | |
| 
 | |
| 		memcpy (&hdr.dsap, &encaps_hdr, sizeof (encaps_hdr));
 | |
| 		hdr.ethertype = eh->h_proto;
 | |
| 
 | |
| 		err = hermes_bap_pwrite (hw, USER_BAP, &hdr, sizeof (hdr),
 | |
| 					 txfid, HERMES_802_3_OFFSET);
 | |
| 		if (err) {
 | |
| 			printf ("%s: Error %d writing packet header to BAP\n",
 | |
| 				orp->or_name, err);
 | |
| 			goto fail;
 | |
| 		}
 | |
| 
 | |
| 		p = ETH_HLEN;
 | |
| 	} else {
 | |
| 		data_len = size + ETH_HLEN;
 | |
| 		data_off = HERMES_802_3_OFFSET;
 | |
| 		p = 0;
 | |
| 	}
 | |
| 
 | |
| 	err = hermes_bap_pwrite (hw, USER_BAP,
 | |
| 				 (void *) &(databuf[p]), RUP_EVEN (data_len),
 | |
| 				 txfid, data_off);
 | |
| 	if (err) {
 | |
| 		printf ("hermes_bap_pwrite(data): error %d\n", err);
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	orp->or_tx.ret_busy = TRUE;
 | |
| 	
 | |
| 	err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL,
 | |
| 				 txfid, NULL);
 | |
| 	if (err) {
 | |
| 		orp->or_tx.ret_busy = FALSE;
 | |
| 		printf ("hermes_docmd_wait(TX|RECL): error %d\n", err);
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| fail:
 | |
| 	orp->or_flags |= OR_F_PACK_SENT;
 | |
| 
 | |
| 	if (from_int) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	reply (orp, OK, FALSE);
 | |
| 	return;
 | |
| 
 | |
| suspend_write:
 | |
| 	orp->or_tx_mess = *mp;
 | |
| 	reply (orp, OK, FALSE);
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_writev_s                                                *
 | |
|  *                                                                           *
 | |
|  * Write data which is denoted by the message to the card and send it.       *
 | |
|  *****************************************************************************/
 | |
| static void or_writev_s (message * mp, int from_int) {
 | |
| 	int port, or_client, count, size, err, data_len, data_off, tx_head;
 | |
| 	int o, j, n, i, s, p, cps ;
 | |
| 	struct ethhdr *eh;
 | |
| 	t_or *orp;
 | |
| 	clock_t timebefore, t0;	
 | |
| 	phys_bytes phys_user, iov_src;
 | |
| 	hermes_t *hw;
 | |
| 	struct hermes_tx_descriptor desc;
 | |
| 	int iov_offset = 0;
 | |
| 	struct header_struct hdr;
 | |
| 	iovec_s_t *iovp;
 | |
| 	phys_bytes phys_databuf;
 | |
| 	u16_t txfid;
 | |
| 
 | |
| 	/* We need space for the max packet size itself, plus an ethernet
 | |
| 	 * header, plus 2 bytes so we can align the IP header on a
 | |
| 	 * 32bit boundary, plus 1 byte so we can read in odd length
 | |
| 	 * packets from the card, which has an IO granularity of 16
 | |
| 	 * bits */
 | |
| 	static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1];
 | |
| 	memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3);
 | |
| 
 | |
| 	port = mp->DL_PORT;
 | |
| 	count = mp->DL_COUNT;
 | |
| 	if (port < 0 || port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port", NO_NUM);
 | |
| 
 | |
| 	or_client = mp->DL_PROC;
 | |
| 	orp = &or_table[port];
 | |
| 	orp->or_client = or_client;
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	/* Switch off interrupts. The card is accessable via 2 BAPs, one for
 | |
| 	 * reading and one for writing. In theory these BAPs should be 
 | |
| 	 * independent, but in practice, the are not. By switching off the
 | |
| 	 * interrupts of the card, the chances of one interfering with the
 | |
| 	 * other should be less */
 | |
| 	if (from_int){
 | |
| 		/* We were called with from_int, meaning that the last time we 
 | |
| 		 * were called, no tx buffers were available, and we had to 
 | |
| 		 * suspend. Now, we'll try again to find an empty buffer in the
 | |
| 		 * card */
 | |
| 		assert (orp->or_flags & OR_F_SEND_AVAIL);
 | |
| 		orp->or_flags &= ~OR_F_SEND_AVAIL;
 | |
| 		orp->or_send_int = FALSE;
 | |
| 		orp->or_tx_alive = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	txfid = orp->or_tx.or_txfid;
 | |
| 
 | |
| 	if (orp->or_tx.ret_busy || orp->connected == 0) {
 | |
| 		/* there is no buffer in the card available */
 | |
| 		assert(!(orp->or_flags & OR_F_SEND_AVAIL));
 | |
| 		/* Remember that there is a packet to be sent available */
 | |
| 		orp->or_flags |= OR_F_SEND_AVAIL;
 | |
| 		goto suspend_write_s;
 | |
| 	}
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 
 | |
| 	/* Copy the data to be send from the vector to the databuf */
 | |
| 	size = 0;
 | |
| 	o = 0;
 | |
| 	for (i = 0; i < count; i += IOVEC_NR,
 | |
| 	     iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]),
 | |
| 		 iov_offset += IOVEC_NR * sizeof (orp->or_iovec_s[0])) {
 | |
| 
 | |
| 		n = IOVEC_NR;
 | |
| 		if (i + n > count)
 | |
| 			n = count - i;
 | |
| 
 | |
| 		cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset,
 | |
| 			(vir_bytes) orp->or_iovec_s, 
 | |
| 			n * sizeof(orp->or_iovec_s[0]), D);
 | |
| 		if (cps != OK) 
 | |
| 			printf("orinoco: sys_safecopyfrom failed: %d\n", cps);
 | |
| 
 | |
| 		for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++)	{
 | |
| 			s = iovp->iov_size;
 | |
| 			if (size + s > ETH_MAX_PACK_SIZE_TAGGED) {
 | |
| 				printf("Orinoco: invalid pkt size\n");
 | |
| 			}
 | |
| 
 | |
| 			cps = sys_safecopyfrom(or_client, iovp->iov_grant, 0,
 | |
| 						(vir_bytes) databuf + o, s, D);
 | |
| 			if (cps != OK) 
 | |
| 				printf("orinoco: sys_safecopyfrom failed:%d\n",
 | |
| 						cps);
 | |
| 
 | |
| 			size += s;
 | |
| 			o += s;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	assert(size >= ETH_MIN_PACK_SIZE); 
 | |
| 
 | |
| 	memset (&desc, 0, sizeof (desc));
 | |
| 	/* Reclaim the tx buffer once the data is sent (OK), or it is clear 
 | |
| 	 * that transmission failed (EX). Reclaiming means that we can reuse 
 | |
| 	 * the buffer again for transmission */
 | |
| 	desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
 | |
| 	/* Actually, this reclaim bit is the only thing which needs to be set 
 | |
| 	 * in the descriptor */
 | |
| 	err = hermes_bap_pwrite (hw, USER_BAP, &desc, sizeof (desc), txfid,
 | |
| 				 0);
 | |
| 	if (err) {
 | |
| 		printf("hermes_bap_pwrite() descriptor error:resetting card\n");
 | |
| 		/* When this happens, the card is quite confused: it will not 
 | |
| 		 * recover. Reset it */
 | |
| 		or_reset();
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	eh = (struct ethhdr *) databuf;
 | |
| 	/* Encapsulate Ethernet-II frames */
 | |
| 	if (ntohs (eh->h_proto) > 1500)	{
 | |
| 		/* Ethernet-II frame */
 | |
| 		data_len = size - ETH_HLEN;
 | |
| 		data_off = HERMES_802_3_OFFSET + sizeof (hdr);
 | |
| 
 | |
| 		/* 802.3 header */
 | |
| 		memcpy (hdr.dest, eh->h_dest, ETH_ALEN);
 | |
| 		memcpy (hdr.src, eh->h_src, ETH_ALEN);
 | |
| 		hdr.len = htons (data_len + ENCAPS_OVERHEAD);
 | |
| 
 | |
| 		/* 802.2 header */
 | |
| 		memcpy (&hdr.dsap, &encaps_hdr, sizeof (encaps_hdr));
 | |
| 		hdr.ethertype = eh->h_proto;
 | |
| 
 | |
| 		err = hermes_bap_pwrite (hw, USER_BAP, &hdr, sizeof (hdr),
 | |
| 					 txfid, HERMES_802_3_OFFSET);
 | |
| 		if (err) {
 | |
| 			printf ("%s: Error %d writing packet header to BAP\n",
 | |
| 				orp->or_name, err);
 | |
| 			goto fail;
 | |
| 		}
 | |
| 
 | |
| 		p = ETH_HLEN;
 | |
| 	} else {
 | |
| 		/* IEEE 802.3 frame */
 | |
| 		data_len = size + ETH_HLEN;
 | |
| 		data_off = HERMES_802_3_OFFSET;
 | |
| 		p = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* Round up for odd length packets */
 | |
| 	err = hermes_bap_pwrite (hw, USER_BAP,
 | |
| 				 (void *) &(databuf[p]), RUP_EVEN (data_len),
 | |
| 				 txfid, data_off);
 | |
| 	if (err) {
 | |
| 		printf ("hermes_bap_pwrite(data): error %d\n", err);
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	/* this should be before the docmd_wait. Cause otherwise the bit can 
 | |
| 		be cleared in the handler (if irq's not off) before it is set
 | |
| 		and then 1 reset (ret_busy=false) is lost */
 | |
| 	orp->or_tx.ret_busy = TRUE;
 | |
| 
 | |
| 	/* Send the packet which was constructed in txfid */
 | |
| 	err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL,
 | |
| 				 txfid, NULL);
 | |
| 	if (err) {
 | |
| 		printf ("hermes_docmd_wait(TX|RECL): error %d\n", err);
 | |
| 		/* Mark the buffer as available again */
 | |
| 		orp->or_tx.ret_busy = FALSE;
 | |
| 		goto fail;
 | |
| 	} 
 | |
| 	
 | |
| fail:
 | |
| 	/* If the interrupt handler called, don't send a reply. The reply
 | |
| 	 * will be sent after all interrupts are handled. 
 | |
| 	 */
 | |
| 	orp->or_flags |= OR_F_PACK_SENT;
 | |
| 
 | |
| 	if (from_int) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	reply (orp, OK, FALSE);
 | |
| 	return;
 | |
| 
 | |
| suspend_write_s:
 | |
| 	orp->or_tx_mess = *mp;
 | |
| 
 | |
| 	reply (orp, OK, FALSE);
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                reply                                                      *
 | |
|  *                                                                           *
 | |
|  * Send a message back to the caller, informing it about the data received   *
 | |
|  * or sent                                                                   *
 | |
|  *****************************************************************************/
 | |
| static void reply (t_or * orp, int err, int may_block) {
 | |
| 	message reply;
 | |
| 	int status = 0, r;
 | |
| 	clock_t now;
 | |
| 
 | |
| 	if (orp->or_flags & OR_F_PACK_SENT)
 | |
| 		status |= DL_PACK_SEND;
 | |
| 	if (orp->or_flags & OR_F_PACK_RECV)
 | |
| 		status |= DL_PACK_RECV;
 | |
| 
 | |
| 	reply.m_type = DL_TASK_REPLY;
 | |
| 	reply.DL_PORT = orp - or_table;
 | |
| 	assert(reply.DL_PORT == 0);
 | |
| 	reply.DL_PROC = orp->or_client;
 | |
| 	reply.DL_STAT = status | ((u32_t) err << 16);
 | |
| 	reply.DL_COUNT = orp->or_read_s;
 | |
| 
 | |
| 	if (OK != (r = getuptime(&now)))
 | |
| 		panic(__FILE__, "orinoco: getuptime() failed:", r);
 | |
| 
 | |
| 	reply.DL_CLCK = now;
 | |
| 	r = send (orp->or_client, &reply);
 | |
| 
 | |
| 	if (r == ELOCKED && may_block) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (r < 0)
 | |
| 		panic(__FILE__, "orinoco: send failed:", r);
 | |
| 
 | |
| 	orp->or_read_s = 0;
 | |
| 	orp->or_flags &= ~(OR_F_PACK_SENT | OR_F_PACK_RECV);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_ev_info                                                 *
 | |
|  *                                                                           *
 | |
|  * Process information which comes in from the card                          *
 | |
|  *****************************************************************************/
 | |
| static void or_ev_info (t_or * orp) {
 | |
| 	u16_t infofid;
 | |
| 	int err, len, type, i;
 | |
| 	hermes_t *hw = &orp->hw;
 | |
| 
 | |
| 	struct {
 | |
| 		u16_t len;
 | |
| 		u16_t type;
 | |
| 	} info;
 | |
| 
 | |
| 	infofid = hermes_read_reg (hw, HERMES_INFOFID);
 | |
| 	err = hermes_bap_pread (hw, IRQ_BAP, &info, sizeof (info), infofid,
 | |
| 				0);
 | |
| 	if (err) {
 | |
| 		printf ("%s: error %d reading info frame.\n", orp->or_name,
 | |
| 			err);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	len = HERMES_RECLEN_TO_BYTES (info.len);
 | |
| 	type = info.type;
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case HERMES_INQ_TALLIES:
 | |
| 		{
 | |
| 			struct hermes_tallies_frame tallies;
 | |
| 
 | |
| 			if (len > sizeof (tallies))	{
 | |
| 				printf ("%s: Tallies frame too long ",
 | |
| 					orp->or_name);
 | |
| 				printf ("(%d bytes)\n", len);
 | |
| 				len = sizeof (tallies);
 | |
| 			}
 | |
| 			hermes_read_words (hw, HERMES_DATA1,
 | |
| 					   (void *) &tallies, len / 2);
 | |
| 			/* TODO: do something with the tallies structure */
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case HERMES_INQ_LINKSTATUS: {
 | |
| 			u16_t newstatus;
 | |
| 			struct hermes_linkstatus linkstatus;
 | |
| 
 | |
| 			if (len != sizeof (linkstatus))	{
 | |
| 				printf ("%s: Unexpected size for linkstatus ",
 | |
| 					orp->or_name);
 | |
| 				printf ("frame (%d bytes)\n", len);
 | |
| 			}
 | |
| 
 | |
| 			hermes_read_words (hw, HERMES_DATA1,
 | |
| 					   (void *) &linkstatus, len / 2);
 | |
| 			newstatus = linkstatus.linkstatus;
 | |
| 
 | |
| 			if ((newstatus == HERMES_LINKSTATUS_CONNECTED)
 | |
| 			    || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
 | |
| 			    || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE)) {
 | |
| 				orp->connected = 1;
 | |
| 
 | |
| 		if(orp->or_flags & OR_F_SEND_AVAIL)	{
 | |
| 		 	orp->or_send_int = TRUE;
 | |
| 			orp->or_got_int = TRUE;
 | |
| 			int_event_check = TRUE;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 			}
 | |
| 			else if ((newstatus ==
 | |
| 				  HERMES_LINKSTATUS_NOT_CONNECTED)
 | |
| 				 || (newstatus ==
 | |
| 				     HERMES_LINKSTATUS_DISCONNECTED)
 | |
| 				 || (newstatus ==
 | |
| 				     HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
 | |
| 				 || (newstatus ==
 | |
| 				     HERMES_LINKSTATUS_ASSOC_FAILED)) {
 | |
| 				orp->connected = 0;
 | |
| 			}
 | |
| 
 | |
| 			if (newstatus != orp->last_linkstatus)
 | |
| 				print_linkstatus(orp, newstatus);
 | |
| 
 | |
| 			orp->last_linkstatus = newstatus;
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf ("%s:Unknown information frame received(type %04x).\n",
 | |
| 			orp->or_name, type);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_print_linkstatus                                        *
 | |
|  *                                                                           *
 | |
|  * Process information which comes in from the card                          *
 | |
|  *****************************************************************************/
 | |
| static void print_linkstatus (t_or * orp, u16_t status) {
 | |
| 	int err;
 | |
| 	u16_t d;
 | |
| 	char *s;
 | |
| 	hermes_t *hw = &(orp->hw);
 | |
| 
 | |
| 	switch (status) {
 | |
| 	case HERMES_LINKSTATUS_NOT_CONNECTED:
 | |
| 		s = "Not Connected";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_CONNECTED:
 | |
| 		s = "Connected";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_DISCONNECTED:
 | |
| 		s = "Disconnected";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_AP_CHANGE:
 | |
| 		s = "AP Changed";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
 | |
| 		s = "AP Out of Range";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_AP_IN_RANGE:
 | |
| 		s = "AP In Range";
 | |
| 		break;
 | |
| 	case HERMES_LINKSTATUS_ASSOC_FAILED:
 | |
| 		s = "Association Failed";
 | |
| 		break;
 | |
| 	default:
 | |
| 		s = "UNKNOWN";
 | |
| 	}
 | |
| 
 | |
| 	printf ("%s: link status: %s, ", orp->or_name, s);
 | |
| 
 | |
| 	err = hermes_read_wordrec (hw, USER_BAP, 
 | |
| 			HERMES_RID_CURRENTCHANNEL, &d);
 | |
| 	if (err) {
 | |
| 		printf ("%s: Error %d \n", orp->or_name, err);
 | |
| 		return;
 | |
| 	}
 | |
| 	printf("channel: %d, freq: %d MHz ", 
 | |
| 		d, (channel_frequency[d-1]));
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_check_ints                                              *
 | |
|  *                                                                           *
 | |
|  * Process events which have been postponed in the interrupt handler         *
 | |
|  *****************************************************************************/
 | |
| static void or_check_ints (t_or * orp) {
 | |
| 	int or_flags;
 | |
| 	hermes_t *hw = &orp->hw;
 | |
| 
 | |
| 	if (orp->or_need_reset)
 | |
| 		or_reset();
 | |
| 	if ((orp->rx_first!=orp->rx_last) && (orp->or_flags & OR_F_READING)) {
 | |
| 		orp->or_ev_rx = 0;
 | |
| 		if (orp->or_rx_mess.m_type == DL_READV) {
 | |
| 			or_readv (&orp->or_rx_mess, TRUE, TRUE);
 | |
| 		} else if(orp->or_rx_mess.m_type == DL_READV_S) {
 | |
| 			or_readv_s (&orp->or_rx_mess, TRUE);
 | |
| 		} else {
 | |
| 			assert(orp->or_rx_mess.m_type == DL_READ);
 | |
| 			or_readv (&orp->or_rx_mess, TRUE, FALSE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (orp->or_send_int) {
 | |
| 		if (orp->or_tx_mess.m_type == DL_WRITEV) {
 | |
| 			or_writev (&orp->or_tx_mess, TRUE, TRUE);
 | |
| 		}
 | |
| 		else if(orp->or_tx_mess.m_type == DL_WRITEV_S) {
 | |
| 			or_writev_s (&orp->or_tx_mess, TRUE);
 | |
| 		} else {
 | |
| 			assert(orp->or_tx_mess.m_type == DL_WRITE);
 | |
| 			or_writev (&orp->or_tx_mess, TRUE, FALSE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (orp->or_flags & (OR_F_PACK_SENT | OR_F_PACK_RECV)) {
 | |
| 		reply (orp, OK, TRUE);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                is_ethersnap                                               *
 | |
|  *                                                                           *
 | |
|  * is there an LLC and SNAP header in the ethernet packet? The inet task     *
 | |
|  * isn't very interested in it...                                            *
 | |
|  *****************************************************************************/
 | |
| static int is_ethersnap(struct header_struct *hdr)  {
 | |
| 
 | |
| 	/* We de-encapsulate all packets which, a) have SNAP headers
 | |
| 	 * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
 | |
| 	 * and where b) the OUI of the SNAP header is 00:00:00 or
 | |
| 	 * 00:00:f8 - we need both because different APs appear to use
 | |
| 	 * different OUIs for some reason */
 | |
| 	return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0)
 | |
| 		&& ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
 | |
| }
 | |
| 	
 | |
| /*****************************************************************************
 | |
|  *                or_readv                                                   *
 | |
|  *                                                                           *
 | |
|  * As far as we can see, this function is never called from 3.1.3. However,  *
 | |
|  * it is still in rtl8139, so we'll keep it here as well. It's almost a copy *
 | |
|  * of or_readv_s. We left out the comments. For an explanation, see          *
 | |
|  * or_readv_s                                                                *
 | |
|  *****************************************************************************/
 | |
| static void or_readv (message * mp, int from_int, int vectored) {
 | |
| 	int i, j, n, o, s, s1, dl_port, or_client, count, size, err, yep, cps;
 | |
| 	port_t port;
 | |
| 	clock_t timebefore;
 | |
| 	unsigned amount, totlen, packlen;
 | |
| 	struct hermes_rx_descriptor desc;
 | |
| 	phys_bytes dst_phys, iov_src;
 | |
| 	u16_t d_start, d_end, rxfid, status;
 | |
| 	struct header_struct hdr;
 | |
| 	int length, offset;
 | |
| 	u32_t l, rxstat;
 | |
| 	struct ethhdr *eh;
 | |
| 	struct header_struct *h;
 | |
| 	t_or *orp;
 | |
| 	hermes_t *hw;
 | |
| 	iovec_t *iovp;
 | |
| 	u8_t *databuf;
 | |
| 
 | |
| 	dl_port = mp->DL_PORT;
 | |
| 	count = mp->DL_COUNT;
 | |
| 	if (dl_port < 0 || dl_port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port:", dl_port);
 | |
| 
 | |
| 	orp = &or_table[dl_port];
 | |
| 	or_client = mp->DL_PROC;
 | |
| 	orp->or_client = or_client;
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	if (!from_int && (orp->rx_first==orp->rx_last)) {
 | |
| 		goto suspend_readv;
 | |
| 	}
 | |
| 
 | |
| 	rxfid = orp->rxfid[orp->rx_first];
 | |
| 	databuf = &(orp->rx_buf[orp->rx_first][0]);
 | |
| 	length = orp->rx_length[orp->rx_first];
 | |
| 
 | |
| 	orp->rxfid[orp->rx_first] = NO_FID;
 | |
| 	orp->rx_length[orp->rx_first] = 0;
 | |
| 
 | |
| 	orp->rx_first++;
 | |
| 	orp->rx_first %= NR_RX_BUFS;
 | |
| 
 | |
| 	o = 0;
 | |
| 	
 | |
| 	if (vectored) {
 | |
| 		int iov_offset = 0;
 | |
| 		size = 0;
 | |
| 
 | |
| 		for (i = 0; i < count; i += IOVEC_NR,
 | |
|  			iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]),
 | |
| 			iov_offset += IOVEC_NR * sizeof(orp->or_iovec[0])) {
 | |
| 
 | |
| 			n = IOVEC_NR;
 | |
| 			if (i + n > count)
 | |
| 				n = count - i;
 | |
| 			
 | |
| 			cps = sys_vircopy(or_client, D, 
 | |
| 					(vir_bytes) mp->DL_ADDR + iov_offset,
 | |
| 					SELF, D, (vir_bytes) orp->or_iovec, 
 | |
| 					n * sizeof(orp->or_iovec[0]));
 | |
| 			if (cps != OK) printf("sys_vircopy failed: %d (%d)\n", 
 | |
| 							cps, __LINE__);
 | |
| 
 | |
| 			for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) {
 | |
| 				s = iovp->iov_size;
 | |
| 				if (size + s > length) {
 | |
| 					assert (length > size);
 | |
| 					s = length - size;
 | |
| 				}
 | |
| 
 | |
| 				cps = sys_vircopy(SELF, D, 
 | |
| 						(vir_bytes) databuf + o,
 | |
| 						or_client, D, 
 | |
| 						iovp->iov_addr, s);
 | |
| 				if (cps != OK) 
 | |
| 					printf("sys_vircopy failed:%d (%d)\n", 
 | |
| 						cps, __LINE__);
 | |
| 
 | |
| 				size += s;
 | |
| 				if (size == length)
 | |
| 					break;
 | |
| 				o += s;
 | |
| 			}
 | |
| 			if (size == length)
 | |
| 				break;
 | |
| 		}
 | |
| 		assert (size >= length);
 | |
| 	} 
 | |
| 
 | |
| 	orp->or_stat.ets_packetR++;
 | |
| 	orp->or_read_s = length;
 | |
| 	orp->or_flags &= ~OR_F_READING;
 | |
| 	orp->or_flags |= OR_F_PACK_RECV;
 | |
| 
 | |
| 	if (!from_int)
 | |
| 		reply (orp, OK, FALSE);
 | |
| 
 | |
| 	return;
 | |
| 
 | |
| suspend_readv :
 | |
| 	if (from_int) {
 | |
| 		assert (orp->or_flags & OR_F_READING);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	orp->or_rx_mess = *mp;
 | |
| 	assert (!(orp->or_flags & OR_F_READING));
 | |
| 	orp->or_flags |= OR_F_READING;
 | |
| 
 | |
| 	reply (orp, OK, FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *                or_readv_s                                                 *
 | |
|  *                                                                           *
 | |
|  * Copy the data which is stored in orp->rx_buf[orp->rx_first] in the vector *
 | |
|  * which was given with the message *mp                                      *
 | |
|  *****************************************************************************/
 | |
| static void or_readv_s (message * mp, int from_int) {
 | |
| 	int i, j, n, o, s, s1, dl_port, or_client, count, size, err, cps;
 | |
| 	int iov_offset = 0, length, offset;
 | |
| 	port_t port;
 | |
| 	clock_t timebefore;
 | |
| 	unsigned amount, totlen, packlen;
 | |
| 	struct hermes_rx_descriptor desc;
 | |
| 	phys_bytes dst_phys, iov_src;
 | |
| 	u16_t d_start, d_end, rxfid, status;
 | |
| 	struct header_struct hdr;
 | |
| 	u32_t l, rxstat;
 | |
| 	struct ethhdr *eh;
 | |
| 	struct header_struct *h;
 | |
| 	t_or *orp;
 | |
| 	hermes_t *hw;
 | |
| 
 | |
| 	iovec_s_t *iovp;
 | |
| 	phys_bytes databuf_phys;
 | |
| 
 | |
| 	u8_t *databuf;
 | |
| 
 | |
| 	dl_port = mp->DL_PORT;
 | |
| 	count = mp->DL_COUNT;
 | |
| 	if (dl_port < 0 || dl_port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port:", dl_port);
 | |
| 
 | |
| 	orp = &or_table[dl_port];
 | |
| 	or_client = mp->DL_PROC;
 | |
| 	orp->or_client = or_client;
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	if (!from_int && (orp->rx_first==orp->rx_last))
 | |
| 
 | |
| 	{
 | |
| 	/* if we are not called from a hard int (data is not yet available) and
 | |
| 	 * there are no buffers (or->rx_buf[x]) which contain any data, we cant
 | |
| 	 * copy any data to the inet server. Goto suspend, and wait for data 
 | |
| 	 * to arrive */
 | |
| 		goto suspend_readv_s;
 | |
| 	}
 | |
| 	
 | |
| 
 | |
| 
 | |
| 	/* get the buffer which contains new data */
 | |
| 	rxfid = orp->rxfid[orp->rx_first];
 | |
| 	/* and store the pointer to this data in databuf */
 | |
| 	databuf = &(orp->rx_buf[orp->rx_first][0]);
 | |
| 	length = orp->rx_length[orp->rx_first];
 | |
| 
 | |
| 	orp->rxfid[orp->rx_first] = NO_FID;
 | |
| 	orp->rx_length[orp->rx_first] = 0;
 | |
| 
 | |
| 	/* Next time, the next buffer with data will be retrieved */
 | |
| 	orp->rx_first++;
 | |
| 	orp->rx_first %= NR_RX_BUFS;
 | |
| 
 | |
| 	o = 0;
 | |
| 	/* The data which we want to be copied to the vector starts at 
 | |
| 	 * *databuf and will be copied to the vecor below */
 | |
| 	size = 0;
 | |
| 	for (i = 0; i < count; i += IOVEC_NR,
 | |
| 		iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]),
 | |
| 		iov_offset += IOVEC_NR * sizeof(orp->or_iovec_s[0])) {
 | |
| 		n = IOVEC_NR;
 | |
| 		if (i + n > count)
 | |
| 			n = count - i;
 | |
| 
 | |
| 		cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset, 
 | |
| 				(vir_bytes)orp->or_iovec_s,
 | |
| 				n * sizeof(orp->or_iovec_s[0]), D);
 | |
| 		if (cps != OK) 
 | |
| 			panic(__FILE__, 
 | |
| 			"orinoco: warning, sys_safecopytp failed:", cps);
 | |
| 
 | |
| 		for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++)	{
 | |
| 			s = iovp->iov_size;
 | |
| 			if (size + s > length) {
 | |
| 				assert (length > size);
 | |
| 				s = length - size;
 | |
| 			}
 | |
| 			cps = sys_safecopyto(or_client, iovp->iov_grant, 0, 
 | |
| 					(vir_bytes) databuf + o, s, D);
 | |
| 			if (cps != OK) 
 | |
| 				panic(__FILE__, 
 | |
| 				"orinoco: warning, sys_safecopy failed:", 
 | |
| 				cps);
 | |
| 
 | |
| 			size += s;
 | |
| 			if (size == length)
 | |
| 				break;
 | |
| 			o += s;
 | |
| 		}
 | |
| 		if (size == length)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	assert(size >= length);
 | |
| 
 | |
| 	orp->or_stat.ets_packetR++;
 | |
| drop:
 | |
| 	orp->or_read_s = length;
 | |
| 	orp->or_flags &= ~OR_F_READING;
 | |
| 	orp->or_flags |= OR_F_PACK_RECV;
 | |
| 
 | |
| 	if (!from_int) {
 | |
| 		/* There was data in the orp->rx_buf[x] which is now copied to 
 | |
| 		 * the inet sever. Tell the inet server */
 | |
| 		reply (orp, OK, FALSE);
 | |
| 	}
 | |
| 
 | |
| 	return;
 | |
| suspend_readv_s:
 | |
| 	if (from_int) {
 | |
| 		assert (orp->or_flags & OR_F_READING);
 | |
| 		/* No need to store any state */
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* We want to store the message, so that next time when we are called 
 | |
| 	 * by hard int, we know where to copy the received data */
 | |
| 	orp->or_rx_mess = *mp;
 | |
| 	assert (!(orp->or_flags & OR_F_READING));
 | |
| 	orp->or_flags |= OR_F_READING;
 | |
| 
 | |
| 	reply (orp, OK, FALSE);
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            or_get_recvd_packet                                            *
 | |
|  *                                                                           *
 | |
|  * The card has received data. Retrieve the data from the card and put it    *
 | |
|  * in a buffer in the driver (in the orp structure)                          *
 | |
|  *****************************************************************************/
 | |
| static int or_get_recvd_packet(t_or *orp, u16_t rxfid, u8_t *databuf) {
 | |
| 	struct hermes_rx_descriptor desc;
 | |
| 	hermes_t *hw;
 | |
| 	struct header_struct hdr;
 | |
| 	int err, length, offset;
 | |
| 	struct ethhdr *eh;
 | |
| 	u16_t status;
 | |
| 	
 | |
| 	memset(databuf, 0, IEEE802_11_FRAME_LEN);
 | |
| 
 | |
| 	hw = &(orp->hw);
 | |
| 
 | |
| 	/* Read the data from the buffer in the card which holds the data. 
 | |
| 	 * First get the descriptor which will tell us whether the packet is 
 | |
| 	 * healthy*/
 | |
| 	err = hermes_bap_pread (hw, IRQ_BAP, &desc, sizeof (desc), rxfid, 0);
 | |
| 	if (err) {
 | |
| 		printf("Orinoco: error %d reading Rx descriptor. "
 | |
| 			"Frame dropped\n", err);
 | |
| 		orp->or_stat.ets_recvErr++;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	status = desc.status;
 | |
| 
 | |
| 	if (status & HERMES_RXSTAT_ERR)	{
 | |
| 		if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
 | |
| 			printf("Error reading Orinoco Rx descriptor.Dropped");
 | |
| 		} else {
 | |
| 			orp->or_stat.ets_CRCerr++;
 | |
| 			printf("Orinoco: Bad CRC on Rx. Frame dropped\n");
 | |
| 		}
 | |
| 		orp->or_stat.ets_recvErr++;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* For now we ignore the 802.11 header completely, assuming
 | |
| 	   that the card's firmware has handled anything vital. The only
 | |
| 	   thing we want to know is the length of the received data */
 | |
| 	err = hermes_bap_pread (hw, IRQ_BAP, &hdr, sizeof (hdr),
 | |
| 				rxfid, HERMES_802_3_OFFSET);
 | |
| 
 | |
| 	if (err) {
 | |
| 		printf("Orinoco: error %d reading frame header. "
 | |
| 			"Frame dropped\n", err);
 | |
| 		orp->or_stat.ets_recvErr++;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	length = ntohs (hdr.len);
 | |
| 	
 | |
| 	/* Sanity checks */
 | |
| 	if (length < 3)	{
 | |
| 		/* No for even an 802.2 LLC header */
 | |
| 		printf("Orinoco: error in frame length: length = %d\n",
 | |
| 			length);
 | |
| 		/* orp->or_stat.ets_recvErr++; */
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (length > IEEE802_11_DATA_LEN) {
 | |
| 		printf("Orinoco: Oversized frame received (%d bytes)\n",
 | |
| 			length);
 | |
| 		orp->or_stat.ets_recvErr++;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	length += sizeof (struct ethhdr);
 | |
| 	offset = HERMES_802_3_OFFSET;
 | |
| 
 | |
| 	/* Read the interesting parts of the data to the drivers memory. This
 | |
| 	 * would be everything from the 802.3 layer and up */
 | |
| 	err = hermes_bap_pread (hw,
 | |
| 				IRQ_BAP, (void *) databuf, RUP_EVEN (length),
 | |
| 				rxfid, offset);
 | |
| 
 | |
| 	if (err) {
 | |
| 		printf("Orinoco: error doing hermes_bap_pread()\n");
 | |
| 		orp->or_stat.ets_recvErr++;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Some types of firmware give us the SNAP and OUI headers. Remove these.
 | |
| 	 */
 | |
| 	if (is_ethersnap(&hdr))	{
 | |
| 		eh = (struct ethhdr *) databuf;
 | |
| 		length -= 8;
 | |
| 
 | |
| 		
 | |
| 		memcpy (databuf + ETH_ALEN * 2, 
 | |
| 			databuf + sizeof(struct header_struct) - 2, 
 | |
| 			length - ETH_ALEN * 2);
 | |
| 	}
 | |
| 
 | |
| 	if(length<60) length=60;
 | |
| 	
 | |
| 	return length;
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            or_getstat                                                     *
 | |
|  *                                                                           *
 | |
|  * Return the statistics structure. The statistics aren't updated until now, *
 | |
|  * so this won't return much interesting yet.                                *
 | |
|  *****************************************************************************/
 | |
| static void or_getstat (message * mp) {
 | |
| 	int r, port;
 | |
| 	eth_stat_t stats;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	port = mp->DL_PORT;
 | |
| 	if (port < 0 || port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port:", port);
 | |
| 	orp = &or_table[port];
 | |
| 	orp->or_client = mp->DL_PROC;
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	stats = orp->or_stat;
 | |
| 
 | |
| 	r = sys_datacopy(SELF, (vir_bytes)&stats, mp->DL_PROC,
 | |
| 			(vir_bytes) mp->DL_ADDR, sizeof(stats));
 | |
| 	if(r != OK) {
 | |
| 		panic(__FILE__, "or_getstat: send failed:", r);	
 | |
| 	}
 | |
| 
 | |
| 	mp->m_type = DL_STAT_REPLY;
 | |
| 	mp->DL_PORT = port;
 | |
| 	mp->DL_STAT = OK;
 | |
| 
 | |
| 	r = send(mp->m_source, mp);
 | |
| 	if(r != OK)
 | |
| 		panic(__FILE__, "orinoco: getstat failed:", r);
 | |
| 
 | |
| 	/*reply (orp, OK, FALSE);*/
 | |
| 
 | |
| }
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            or_getstat_s                                                   *
 | |
|  *                                                                           *
 | |
|  * Return the statistics structure. The statistics aren't updated until now, *
 | |
|  * so this won't return much interesting yet.                                *
 | |
|  *****************************************************************************/
 | |
| static void or_getstat_s (message * mp) {
 | |
| 	int r, port;
 | |
| 	eth_stat_t stats;
 | |
| 	t_or *orp;
 | |
| 
 | |
| 	port = mp->DL_PORT;
 | |
| 	if (port < 0 || port >= OR_PORT_NR)
 | |
| 		panic(__FILE__, "orinoco: illegal port:", port);
 | |
| 	assert(port==0);
 | |
| 	orp = &or_table[port];
 | |
| 	orp->or_client = mp->DL_PROC;
 | |
| 
 | |
| 	assert (orp->or_mode == OR_M_ENABLED);
 | |
| 	assert (orp->or_flags & OR_F_ENABLED);
 | |
| 
 | |
| 	stats = orp->or_stat;
 | |
| 
 | |
| 	r = sys_safecopyto(mp->DL_PROC,	mp->DL_GRANT, 0, 
 | |
| 				(vir_bytes) &stats, sizeof(stats), D);
 | |
| 	if(r != OK) {
 | |
| 		panic(__FILE__, "or_getstat_s: sys_safecopyto failed:", r);
 | |
| 	}
 | |
| 
 | |
| 	mp->m_type = DL_STAT_REPLY;
 | |
| 	mp->DL_PORT = port;
 | |
| 	mp->DL_STAT = OK;
 | |
| 
 | |
| 	r = send(mp->m_source, mp);
 | |
| 	if(r != OK)
 | |
| 		panic(__FILE__, "orinoco: getstat_s failed:", r);
 | |
| }
 | |
| 
 | 
