/* ibmtr.c:  A shared-memory IBM Token Ring II 16/4 driver for linux */
/*
	Written 1993 by Mark Swanson and Peter De Schrijver.
	This software may be used and distributed according to the terms
	of the GNU Public License, incorporated herein by reference.

	This device driver should work with Any IBM Token Ring Card that does
   not use DMA.

	The Author may be reached at ag010@freenet.carleton.ca.  The 
	response will not be swift due to the limited number of
	phone lines and the difficulty of logging on to the freenet.

	I used Donald Becker's (becker@super.org) device driver work
	as a base for most of my initial work.
*/

/*
   Changes by Peter De Schrijver (stud11@cc4.kuleuven.ac.be) :
	
	+ changed name to ibmtr.c in anticipation of other tr boards.
	+ changed reset code and adapter open code.
	+ added SAP open code.
	+ a first attempt to write interrupt, transmit and receive routines.
*/
	
static char *version =
	"ibmtr.c:v1.1.24 2/21/94 Mark Swanson  (mswanson@hookup.net)\n";
static char pcchannelid[]={0x05, 0x00, 0x04, 0x09,
			 0x04, 0x03, 0x04, 0x0f,
			 0x03, 0x06, 0x03, 0x01,
			 0x03, 0x01, 0x03, 0x00,
			 0x03, 0x09, 0x03, 0x09,
			 0x03, 0x00, 0x02, 0x00}; 
static char mcchannelid[]={0x04, 0x0d, 0x04, 0x01,
			 0x05, 0x02, 0x05, 0x03,
			 0x03, 0x06, 0x03, 0x03,
			 0x05, 0x08, 0x03, 0x04,
			 0x03, 0x05, 0x03, 0x01,
			 0x03, 0x08, 0x02, 0x00};

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/trdevice.h>
#include <stddef.h>
#include "ibmtr.h"

#define DPRINTK(x) printk("%s: ",dev->name),printk x

struct tok_info ti;
static struct wait_queue *wait_for_tok_int=NULL;
extern void *irq2dev_map[16];

void (*do_tok_int)(struct device *dev)=NULL;

int tok_probe(struct device *dev);
/* int tok_probe1(int ioaddr, struct device *dev); */

static int tok_init_card(struct device *dev);
static void tok_interrupt(int reg_ptr);

static void initial_tok_int(struct device *dev);
static void regular_tok_int(struct device *dev);

static int open_sap(unsigned char type,struct device *dev);
static void tr_rx(struct device *dev);
static void tr_tx(struct device *dev);

static int tok_open(struct device *dev);
static int tok_close(struct device *dev);
static int tok_send_packet(struct sk_buff *skb, struct device *dev);
/* static void tok_reset(struct device *dev);
static void tok_block_output(struct device *dev,int count, const unsigned char *buf,
				const start_page);
static int tok_block_input(struct device *dev, int count, char *buf, int ring_offset);
*/

int tok_probe(struct device *dev)
{
	short ioaddr = dev->base_addr;
	unsigned char segment=0, intr=0, irq=0, i=0, j=0, cardpresent=NOTOK,temp=0;

	if (ioaddr <0) return ENXIO;
/*	if (ioaddr >0) return ! tokprobe1(ioaddr, dev); WHY!!!???????? */

	if ( check_region(MMIOStartLocP,4) ) {
		DPRINTK(("Could not check_region MMIO.\n"));
		return ENODEV;  /*  Must be done before I muck with I/O addresses */
	}
	segment = inb(MMIOStartLocP); /* Returns segment address of the start of the MMIO */
	ti.mmio=(char *) (((segment & 0xfc) << 11) + 0x80000); /* fix it up, since we don't care about segments */
DPRINTK(("mmio segment : %p\n",ti.mmio));
	intr = segment & 0x03;
#ifdef 1
	for (i=0;i<=46;i+=2) DPRINTK(("%d, %p ",* (char *)(i+CHANNEL_ID + ti.mmio), (char *) (i+CHANNEL_ID+ti.mmio))); 
#endif
	j=0;
	for (i=0; i<=46; i=i+2) {
		if ( * (char *)( i + CHANNEL_ID + ti.mmio) != pcchannelid[j]) {
			cardpresent=NOTOK;
			goto next;
		}
 		cardpresent=ISA;
		++j;
	}
next:
	if (cardpresent != ISA) {
		for (i=0,j=0; i<=46; i=i+2,j++) {
			DPRINTK(("%p: %01X\\   ",i + CHANNEL_ID + ti.mmio,*(char *)(i+CHANNEL_ID+ti.mmio)));
			if ( * (char *)( i + CHANNEL_ID + ti.mmio) != mcchannelid[j]) {
				DPRINTK(("Warning: Did not pass channel identification..\n"));
#if 0
				return ENODEV;
#endif
			}
		}
		cardpresent=MCA;
	} 

	DPRINTK((version)); /* As we have passed card identification, let the world know we're here! */

	/* Now, allocate some of the pl0 buffers for this driver.. */
	snarf_region(MMIOStartLocP,4);
	/* dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); */

	switch (cardpresent) {
	case ISA:
	if (intr==0) irq=9; /* irq2 really is irq9 */
	if (intr==1) irq=3;
	if (intr==2) irq=6;
	if (intr==3) irq=7;
	break;
	case MCA:
	if (intr==0) irq=9;
	if (intr==1) irq=3;
	if (intr==2) irq=10;
	if (intr==3) irq=11;
	}
	if (request_irq (dev->irq = irq, &tok_interrupt,0,"IBM TR") != 0) {
		DPRINTK(("Could not grab irq %d.  Halting Token Ring driver.\n",irq));
		return ENODEV;
	}
	irq2dev_map[irq]=dev;

DPRINTK(("hw address: "));
	/* Get hw address of token ring card */
	j=0;
	for (i=0; i<0x18; i=i+2) {
		temp = *(char *)((ulong)AIP + (ulong)i + ti.mmio) & 0x0f; /* Tech ref states must do this */
		printk("%02X ",ti.hw_address[j]=temp);
		if(j&1) 
			dev->dev_addr[(j/2)]=ti.hw_address[j]+(ti.hw_address[j-1]<<4);
		++j;
	}
DPRINTK(("\n"));
	/* get Adapter type */
	ti.adapter_type = *(char *)(ti.mmio + AIPADAPTYPE); /* 'F' = Adapter/A */
	/* get Data Rate */
	ti.data_rate = *(char *)(ti.mmio + AIPDATARATE); /* F=4Mb, E=16Mb, D=4Mb & 16Mb ?? */
	/* Does card support Early Token Release? */
	ti.token_release = *(char *)(ti.mmio + AIPEARLYTOKEN); /* F=no, E=4Mb, D=16Mb, C=4&16Mb */
	/* How much shared RAM is on adapter ? */
	ti.avail_shared_ram = *(char *)(ti.mmio + AIPAVAILSHRAM); /* E=8k, D=16k, C=32k, B=64k(s), A=64k(s) */
	if (ti.avail_shared_ram == 0xf) ti.avail_shared_ram = *(char *)(ti.mmio + 0x1e01);
	/* We need to set or do a bunch of work here based on previous results.. */
	/* Support paging?  If so, what sizes? */
	ti.shared_ram_paging = *(char *)(ti.mmio + AIPSHRAMPAGE); /* F=no, E=16k, D=32k, C=16 & 32k, */
	/* Available DHB  4Mb size */
	ti.dhb_size4mb = *(char *) (ti.mmio + AIP4MBDHB); /* F=2048, E=4096, D=4464 */
	/* Available DHB 16Mb size */
	ti.dhb_size16mb = *(char *)(ti.mmio + AIP16MBDHB); /* F=2048, E=4096, D=8192, C=16384, B=17960 */

	DPRINTK(("ti.mmio=%p, ",ti.mmio));
	DPRINTK((" irq=%d.",irq));
	DPRINTK((" segment= %d.",segment));
	for (i=0;i<32;i++) DPRINTK(("%x",ti.hw_address[i]));
	printk("\natype=%x, drate=%x, trel=%x, asram=%x, srp=%x, mhdb=%x",ti.adapter_type, \
		ti.data_rate, ti.token_release, ti.avail_shared_ram, ti.shared_ram_paging, ti.dhb_size4mb);

/* DONE!  This routine is finished!  It is completely working and tested with */
/* Several known kinds of IBM Token Ring cards.  We still need : */
/* To fill out dev-> stuff */
/* To fill out ei_status. stuff */
/* tok_open(struct device *dev)  */
/* tok_close(struct device *dev) */
/* tok_init_card(struct device *dev) */
/* tok_block_output(struct device *dev, int count, const unsigned char *buf, const start_page) */
/* tok_block_input(struct device *dev, int count, char *buf, int ring_offset) */

	dev->open=tok_open;
	dev->stop=tok_close;
	dev->hard_start_xmit=tok_send_packet;
	dev->get_stats = NULL;
	dev->set_multicast_list = NULL;
	tr_setup(dev);

	return 0;  /* Return 0 if we have found a Token Ring card.  Obviously, we have. */
}

static int tok_open(struct device *dev) {

	int rc=0;

	if(!(rc=tok_init_card(dev))) {
		dev->tbusy=0;
		dev->interrupt=0;
		dev->start=1;
	}

	return rc;
	
}
		
static int tok_close(struct device *dev) {

	struct srb_close_adapter *close_adapter=(struct srb_close_adapter *)ti.srb;

	close_adapter->command=DIR_CLOSE_ADAPTER;
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;

	sleep_on(&wait_for_tok_int);		

	if(close_adapter->ret_code) 
		DPRINTK(("close adapter failed: %02X\n",close_adapter->ret_code));
	
	return 0;
}
static void tok_interrupt (int reg_ptr)
{

   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
   struct device *dev = (struct device *)(irq2dev_map[irq]);
#if 0
	DPRINTK(("Int from tok_driver, dev : %p\n",dev));
#endif

	if(do_tok_int)
		do_tok_int(dev);
	else
		DPRINTK(("Unexpected interrupt from tr adapter\n"));

}

static void initial_tok_int(struct device *dev) {

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE);
	wake_up(&wait_for_tok_int);
	DPRINTK(("Initial tok int received\n"));

	do_tok_int=NULL;
}

static int tok_init_card(struct device *dev)
{

	int i;
	struct dir_open_adapter *open_adapter;
	struct srb_open_response *open_response;
	unsigned char *encoded_addr;

	do_tok_int=initial_tok_int; /* Special processing for first interrupt after reset */ 

	/* Reset adapter */

DPRINTK(("resetting card\n"));
	outb(ADAPTRESETP,0);
	for(i=jiffies+5;i<=jiffies;); /* wait 50ms */
	outb(ADAPTRESETRELP,0);
DPRINTK(("card reset\n"));

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE+INT_IRQ;

	sleep_on(&wait_for_tok_int);
	
DPRINTK(("now opening the board...\n"));

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)=0xD0 ;  /* set RRR even to D000 for shared ram address */
	ti.sram=(char *)0xd0000;
	ti.init_srb=ti.sram+ntohs(*(unsigned short *)(ti.mmio+ ACA_OFFSET + WRBR_EVEN));

/*	
DPRINTK(("init_srb:"));
	for(i=0;i<17;i++) 
		printk("%02X ",*(ti.init_srb+i));
	printk("\n");
		
DPRINTK(("offsetof encoded_address: %d\n",offsetof(struct srb_init_response,encoded_address)));
DPRINTK(("srb_init_response->encoded_address: %04X\n",*(unsigned short *)(ti.init_srb+offsetof(struct srb_init_response,encoded_address))));
DPRINTK(("ntohs(srb_init_response->encoded_address): %04X\n",ntohs(*(unsigned short *)(ti.init_srb+offsetof(struct srb_init_response,encoded_address)))));
	encoded_addr=(unsigned char *)(ti.sram + ntohs(*(unsigned char *)(ti.init_srb+offsetof(struct srb_init_response,encoded_address))));

DPRINTK(("encoded addr (%p): ",encoded_addr));
*/
	for(i=0;i<TR_ALEN;i++) 
		printk("%02X ",dev->dev_addr[i]);
			/* =encoded_addr[i],(i==TR_ALEN-1) ? "" : ":" ); */
				 

	DPRINTK(("ti.sram: %p, ti.init_srb: %p\n",ti.sram,ti.init_srb));
	
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);

	do_tok_int=regular_tok_int;

	open_adapter=(struct dir_open_adapter *)(ti.init_srb);
	memset(open_adapter,0,sizeof(struct dir_open_adapter));

	open_adapter->command=DIR_OPEN_ADAPTER;
	open_adapter->open_options=htons(OPEN_PASS_BCON_MAC);
	open_adapter->num_rcv_buf=htons(NUM_RCV_BUF);
	open_adapter->rcv_buf_len=htons(RCV_BUF_LEN);
	open_adapter->dhb_length=htons(DHB_LENGTH);
	open_adapter->num_dhb=NUM_DHB;
	open_adapter->dlc_max_sap=DLC_MAX_SAP;
	open_adapter->dlc_max_sta=DLC_MAX_STA;

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;

	ti.srb=ti.init_srb; /* We use this one the interrupt handler */

DPRINTK(("waiting for open...\n"));

	sleep_on(&wait_for_tok_int);

DPRINTK(("board opened...\n"));
	open_response=(struct srb_open_response *)(ti.init_srb);

	if(open_response->ret_code) {
		DPRINTK(("open failed: ret_code = %02X\n",open_response->ret_code));
		return -EAGAIN;
	}

	cli();
	ti.srb=ti.sram+ntohs(open_response->srb_addr);
	ti.ssb=ti.sram+ntohs(open_response->ssb_addr);
	ti.arb=ti.sram+ntohs(open_response->arb_addr);
	ti.asb=ti.sram+ntohs(open_response->asb_addr);
	sti();
	
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);

	if(open_sap(EXTENDED_SAP,dev)) 
		return -EAGAIN;
	
	ti.current_skb=NULL;

	DPRINTK(("Tok_init_card executed.\n"));

	return 0;
}

static int open_sap(unsigned char type,struct device *dev) {

	struct dlc_open_sap *open_sap=(struct dlc_open_sap *)ti.srb;

	memset(open_sap,0,sizeof(struct dlc_open_sap));

	open_sap->command=DLC_OPEN_SAP;
	open_sap->max_i_field=htons(MAX_I_FIELD);
	open_sap->sap_options=SAP_OPEN_IND_SAP | SAP_OPEN_PRIORITY;
	open_sap->station_count=SAP_OPEN_STATION_CNT;
	open_sap->sap_value=type;

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;

	sleep_on(&wait_for_tok_int);
	
	if(open_sap->ret_code) {
		DPRINTK(("open_sap failed: ret_code = %02X\n",open_sap->ret_code));
		return open_sap->ret_code;
	}

	ti.exsap_station_id=open_sap->station_id;

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);

	return 0;
}
		

static void regular_tok_int(struct device *dev) {

	unsigned char status;

	dev->interrupt=1;

	/* Disable interrupts till processing is finished */
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE);
	
	status=*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RW + ISRP_ODD);

	if(status & ADAP_CHK_INT) {
		int i;
		unsigned char *check_reason=ti.mmio + ntohs(*(unsigned short *)(ti.mmio + ACA_OFFSET + ACA_RW +WWCR_EVEN)); 

		DPRINTK(("adapter check interrupt\n"));

		DPRINTK(("8 reason bytes follow: "));
		for(i=0;i<	8;i++,check_reason++)
			DPRINTK(("%02X ",*check_reason));	
		DPRINTK(("\n"));

		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=(~ADAP_CHK_INT);
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET  + ISRP_EVEN)=INT_ENABLE;
	}	

	else if((*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN)) & (TCR_INT + ERR_INT + ACCESS_INT)) {

		printk("adapter error: ISRP_EVEN : %02x\n",
				*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN));
				
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=~(TCR_INT + ERR_INT + ACCESS_INT);
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET  + ISRP_EVEN)=INT_ENABLE;
	}
					
	else if(status & (SRB_RESP_INT + ASB_FREE_INT + ARB_CMD_INT + SSB_RESP_INT)) {

		if(status & SRB_RESP_INT) {
			switch(*ti.srb) {
				case XMIT_DIR_FRAME: {
					struct srb_xmit *xmit=(struct srb_xmit *)(ti.srb);
					if(xmit->ret_code!=0xff) {
						DPRINTK(("error on xmit_dir_frame request: %02X\n",xmit->ret_code));
						if(ti.current_skb) {
							dev_kfree_skb(ti.current_skb, FREE_WRITE);
							ti.current_skb=NULL;
						}
						dev->tbusy=0;
					}												
				}
				break;
				
				case XMIT_UI_FRAME: {
					struct srb_xmit *xmit=(struct srb_xmit *)(ti.srb);
					if(xmit->ret_code!=0xff) {
						DPRINTK(("error on xmit_ui_frame request: %02X\n",xmit->ret_code));
						if(ti.current_skb) {
							dev_kfree_skb(ti.current_skb, FREE_WRITE);
							ti.current_skb=NULL;
						}
						dev->tbusy=0;
					}												
				}
				break;
	
				case DIR_OPEN_ADAPTER:
				case DIR_CLOSE_ADAPTER:
				case DLC_OPEN_SAP:
					wake_up(&wait_for_tok_int);
					break;

				case DIR_INTERRUPT:
				case DIR_MOD_OPEN_PARAMS:	
				case DIR_SET_GRP_ADDR:
				case DIR_SET_FUNC_ADDR:
				case DLC_CLOSE_SAP: {
					struct srb_interrupt *intr=(struct srb_interrupt *)(ti.srb);
					if(intr->ret_code) 
						DPRINTK(("error on %02X: %02X\n",intr->command,intr->ret_code));	
				}
				break;

				case DIR_READ_LOG: {
					struct srb_read_log *read_log=(struct srb_read_log *)(ti.srb);
					if(read_log->ret_code)
						DPRINTK(("error on dir_read_log: %02X\n",read_log->ret_code));
					else {
						printk("Line errors %02X, Internal errors %02X, Burst errors %02X\n",
                          read_log->line_errors,read_log->internal_errors,read_log->burst_errors);
						printk("A/C errors %02X, Abort delimiters %02X, Lost frames %02X\n",
                          read_log->A_C_errors,read_log->abort_delimiters,read_log->lost_frames);
						printk("Receive congestion count %02X, Frame copied errors %02X, Frequency errors %02X\n",
                          read_log->recv_congest_count,read_log->frame_copied_errors,read_log->frequency_errors);
						DPRINTK(("Token errors %02X\n",read_log->token_errors));
					}
					dev->tbusy=0;
				}
				break;

				default:
					DPRINTK(("Unknown command %02X encountered\n",*(ti.srb)));
			}
				
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
		}

		if(status & ASB_FREE_INT) {
			switch(*ti.asb) {
				case REC_DATA:
				case XMIT_UI_FRAME:
				case XMIT_DIR_FRAME:
					if(*(ti.asb+2)!=0xff)
						DPRINTK(("ASB error %02X in cmd %02X\n",	*(ti.asb+2),*(ti.asb)));
					break;
				default:
					DPRINTK(("unknown command in asb %02X\n",*ti.asb));
			}
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(ASB_FREE_INT);
		}

		if(status & ARB_CMD_INT) {
			switch(*ti.arb) {
				case DLC_STATUS: {
					struct arb_dlc_status *dlc_status=(struct arb_dlc_status *)(ti.arb);			
					DPRINTK(("DLC_STATUS new status: %02X on station %02X\n",ntohs(dlc_status->status),ntohs(dlc_status->station_id)));
				}
				break;

				case REC_DATA: 
					tr_rx(dev);
					break;

				case RING_STAT_CHANGE: {
					struct arb_ring_stat_change *ring_stat_change=(struct arb_ring_stat_change *)(ti.arb);
					unsigned short ring_status=ntohs(ring_stat_change->ring_status);

					if(ring_status & (SIGNAL_LOSS + LOBE_FAULT)) {
						DPRINTK(("Signal loss/Lobe fault\n"));
						DPRINTK(("We should probably try to reopen the adapter, but we don't do this yet.\n"));	
					}
					else DPRINTK(("New ring status: %02X\n",ring_status));

					if(ring_status & LOG_OVERFLOW) {
						*(ti.srb)=DIR_READ_LOG;
						*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
						*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
						dev->tbusy=1; /* really srb busy... */
					}
				}
				break;

				case XMIT_DATA_REQ:
					tr_tx(dev);
					break;

				default:
					DPRINTK(("Unknown command %02X in arb\n",*(ti.arb)));
					break;
			}
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(ARB_CMD_INT);
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=ARB_FREE;
		}

		if(status & SSB_RESP_INT) {
			switch(*ti.ssb) {
				case XMIT_DIR_FRAME:
				case XMIT_UI_FRAME:
					if(*(ti.ssb+2)) 
						DPRINTK(("xmit ret_code: %02X xmit error code: %02X\n",*(ti.ssb+2),*(ti.ssb+6)));		
					break;
				
				case XMIT_XID_CMD:
					DPRINTK(("xmit xid ret_code: %02X\n",*(ti.ssb+2)));
				
				default:
					DPRINTK(("Unknown command %02X in ssb\n",*(ti.ssb)));
			}
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SSB_RESP_INT);
			*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=SSB_FREE;
		}
	}	

	dev->interrupt=0;
	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;

	return;
}	
			
static void tr_tx(struct device *dev) {

	struct asb_xmit_resp *xmit_resp=(struct asb_xmit_resp *)ti.asb;
	struct arb_xmit_req *xmit_req=(struct arb_xmit_req *)ti.arb;
	struct srb_xmit *xmit=(struct srb_xmit *)ti.srb;
	unsigned int rif_len;
	unsigned char *dhb;

#if 0
DPRINTK(("transmitting...\n"));
#endif

	if(xmit_resp->ret_code!=0xff) 
		DPRINTK(("ASB not free !!!\n"));

	dhb=ti.sram+ntohs(xmit_req->dhb_address);

	xmit_resp->command=xmit->command;
	xmit_resp->station_id=xmit_req->station_id;
	xmit_resp->rsap_value=EXTENDED_SAP;
	xmit_resp->cmd_corr=xmit_req->cmd_corr;
	xmit_resp->ret_code=0;

	if((xmit->command==XMIT_XID_CMD) && (xmit->command==XMIT_TEST_CMD)) {
		xmit_resp->frame_length=htons(0x11);
		xmit_resp->hdr_length=0x0e;		
		dhb[0]=AC;			
		dhb[1]=LLC_FRAME;
		memset(dhb+2,0xff,TR_ALEN);
		memset(dhb+8,0,TR_ALEN);
	}

	memcpy(dhb,ti.current_skb->data,sizeof(struct trh_hdr));
	dhb+=sizeof(struct trh_hdr);
	if(!(((struct trh_hdr *)(&ti.current_skb->data))->saddr[0] & 0x80)) {
		dhb-=18;
		xmit_resp->hdr_length=sizeof(struct trh_hdr)-18;
		xmit_resp->frame_length=htons(ti.current_skb->len-18);
#if 0
DPRINTK(("hdr_length: %d, frame length: %ld\n",sizeof(struct trh_hdr)-18,
			ti.current_skb->len-18));
#endif
	}
	else {
		rif_len=(ntohs(((struct trh_hdr *)(&ti.current_skb->data))->rcf) & TR_RCF_LEN_MASK)>>8;
#if 0
DPRINTK(("rcf: %02X rif_len: %d\n",((struct trh_hdr *)&ti.current_skb->data)->rcf,rif_len));
DPRINTK(("hdr_length: %d, frame length: %ld\n",sizeof(struct trh_hdr)-18+rif_len,
			ti.current_skb->len-18+rif_len));
#endif
		xmit_resp->hdr_length=sizeof(struct trh_hdr)-18+rif_len;
		xmit_resp->frame_length=htons(ti.current_skb->len-18+rif_len);
		dhb+=rif_len-18;
	}	
	
	memcpy(dhb,ti.current_skb->data+sizeof(struct trh_hdr),ti.current_skb->len-sizeof(struct trh_hdr));

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
	dev->tbusy=0;
	dev_kfree_skb(ti.current_skb,FREE_WRITE);
	ti.current_skb=NULL;
	mark_bh(NET_BH);
}
	
static void tr_rx(struct device *dev) {

	struct arb_rec_req *rec_req=(struct arb_rec_req *)ti.arb;
	struct asb_rec *rec_resp=(struct asb_rec *)ti.asb;
	struct rec_buf *rbuffer;	
	struct trllc *llc;
	unsigned char *data;
	unsigned int rbuffer_len,lan_hdr_len;
	struct sk_buff *skb;

	rbuffer=(struct rec_buf *)(ti.sram+ntohs(rec_req->rec_buf_addr));
	
	if(rec_resp->ret_code!=0xff) 
		DPRINTK(("ASB	not free !!!\n"));
	
	rec_resp->command=REC_DATA;
	rec_resp->station_id=rec_req->station_id;
	rec_resp->rec_buf_addr=rec_req->rec_buf_addr; 

	lan_hdr_len=rec_req->lan_hdr_len;

	llc=(struct trllc *)((unsigned char *)rbuffer+offsetof(struct rec_buf,data)+lan_hdr_len);

#if 0
DPRINTK(("offsetof data: %02X lan_hdr_len: %02X\n",offsetof(struct rec_buf,data),lan_hdr_len));
DPRINTK(("llc: %p rec_buf_addr: %04X ti.sram: %p\n",llc,ntohs(rec_req->rec_buf_addr),ti.sram));
DPRINTK(("dsap: %02X, ssap: %02X, llc: %02X, protid: %02X%02X%02X, ethertype: %04X\n",
			llc->dsap,llc->ssap,llc->llc,llc->protid[0],llc->protid[1],llc->protid[2],llc->ethertype));
#endif

	if(llc->llc!=UI_CMD) {
		
		DPRINTK(("non-UI frame arrived. dropped. llc= %02X\n",llc->llc));
		rec_resp->ret_code=DATA_LOST;
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
		return;
	}

#if 0
	if((llc->dsap!=0xaa) || (llc->ssap!=0xaa)) {

	struct trh_hdr *trhdr=(struct trh_hdr *)((unsigned char *)rbuffer+offsetof(struct rec_buf,data));

DPRINTK(("Probably non-IP frame received.\n"));
DPRINTK(("ssap: %02X dsap: %02X saddr: %02X:%02X:%02X:%02X:%02X:%02X daddr: %02X:%02X:%02X:%02X:%02X:%02X\n",
			llc->ssap,llc->dsap,trhdr->saddr[0],trhdr->saddr[1],trhdr->saddr[2],trhdr->saddr[3],trhdr->saddr[4],trhdr->saddr[5],
			trhdr->daddr[0],trhdr->daddr[1],trhdr->daddr[2],trhdr->daddr[3],trhdr->daddr[4],trhdr->daddr[5]));
	}
#endif


	if(!(skb=alloc_skb(ntohs(rec_req->frame_len)-lan_hdr_len+sizeof(struct trh_hdr), GFP_ATOMIC))) {
		DPRINTK(("out of memory. frame dropped.\n"));	
		rec_resp->ret_code=DATA_LOST;
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
		return;
	}

	skb->len=ntohs(rec_req->frame_len)-lan_hdr_len+sizeof(struct trh_hdr);
	skb->dev=dev;

#if 0
DPRINTK(("Now copying data...\n"));
#endif


	data=skb->data;
	memcpy(data,&(rbuffer->data),lan_hdr_len);


	if(lan_hdr_len<sizeof(struct trh_hdr))
		memset(data+lan_hdr_len,0,sizeof(struct trh_hdr)-lan_hdr_len);

	data+=sizeof(struct trh_hdr);
	rbuffer_len=ntohs(rbuffer->buf_len)-lan_hdr_len;
#if 0
DPRINTK(("rbuffer_len: %d, data: %p\n",rbuffer_len,data));
#endif
	memcpy(data,(unsigned char *)(&(rbuffer->data))+lan_hdr_len,rbuffer_len);
	data+=rbuffer_len;


	if(rbuffer->buf_ptr) 
		for(rbuffer=(struct rec_buf *)(ti.sram+ntohs(rbuffer->buf_ptr)-2);
				memcpy(data,&(rbuffer->data),rbuffer_len=ntohs(rbuffer->buf_len)),rbuffer->buf_ptr;
				data+=rbuffer_len,rbuffer=(struct rec_buf *)(ti.sram+ntohs(rbuffer->buf_ptr)-2))
#if 0
	DPRINTK(("buf_ptr: %d,data =%p\n",ntohs(rbuffer->buf_ptr),data));
#endif

	rec_resp->ret_code=0;

	*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;

	netif_rx(skb);

	return;
}

static int tok_send_packet(struct sk_buff *skb, struct device *dev) {


#if 0
DPRINTK(("tada: sending packet...\n"));
#endif

	if (dev->tbusy) {
		int ticks_waited=jiffies - dev->trans_start;
		if(ticks_waited<5)
			return 1;
		DPRINTK(("Arrg. Transmitter busy for more than 50 msec. Donald resets adapter, but resetting\n \
the IBM tokenring adapter takes a long time. It might not even help when the\n \
ring is very busy, so we just wait a little longer and hope for the best.\n"));		
		dev->trans_start+=5; /* we fake the transmission start time... */
	}

	/* Donald does this, so we do too. */

	if(skb==NULL) {
		dev_tint(dev);
		return 0;
	}

	if(set_bit(0,(void *)&dev->tbusy)!=0)
		DPRINTK(("Transmitter access conflict\n"));
	else {
		struct srb_xmit *xmit=(struct srb_xmit *)ti.srb;

		ti.current_skb=skb; /* save skb. We will need it when the adapter
                                  asks for the data */
		xmit->command=XMIT_UI_FRAME;
		xmit->station_id=ti.exsap_station_id;
		*(unsigned char *)(ti.mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
		dev->trans_start=jiffies;
	}

	return 0;
}	
