diff options
Diffstat (limited to 'drivers/net/ibmtr.c')
-rw-r--r-- | drivers/net/ibmtr.c | 1180 |
1 files changed, 1180 insertions, 0 deletions
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c new file mode 100644 index 000000000..70f524225 --- /dev/null +++ b/drivers/net/ibmtr.c @@ -0,0 +1,1180 @@ +/* ibmtr.c: A shared-memory IBM Token Ring 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. + + 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. + + Changes by David W. Morris (dwm@shell.portal.com) : + 941003 dwm: - Restructure tok_probe for multiple adapters, devices + - Add comments, misc reorg for clarity + - Flatten interrupt handler levels + + Warnings !!!!!!!!!!!!!! + This driver is only partially sanitized for support of multiple + adapters. It will almost definately fail if more than one + active adapter is identified. +*/ + +#define NO_AUTODETECT 1 +#undef NO_AUTODETECT +#undef ENABLE_PAGING + +#define FALSE 0 +#define TRUE (!FALSE) + +static char *version = "ibmtr.c:v1.1.48 8/7/94 Peter De Schrijver and Mark Swanson\n" + " modified 10/3/94 David W. Morris\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/timer.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(format, args...) printk("%s: " format, dev->name , ## args) +#define DPRINTD(format, args...) DummyCall("%s: " format, dev->name , ## args) + + +#if 0 +struct tok_info tok_info1; /* WARNING: this area must be replicated + or 'malloced' to support > 1 adapter */ + +static struct wait_queue *wait_for_tok_int=NULL, *wait_for_reset; +void (*do_tok_int)(struct device *dev)=NULL; +#endif + +unsigned char ibmtr_debug_trace=1; /* Patch or otherwise alter to + control tokenring tracing. */ +#define TRC_INIT 0x01 /* Trace initialization & PROBEs */ +#define TRC_INITV 0x02 /* verbose init trace points */ + +static short TokBaseAddrs[]={MMIOStartLocP, /* Addr-s to scan */ + MMIOStartLocA}; + + +int tok_probe(struct device *dev); +unsigned char get_sram_size(struct tok_info *adapt_info); + +static void tok_init_card(unsigned long dev_addr); +static void tok_interrupt(int irq, struct pt_regs *regs); + +static void initial_tok_int(struct device *dev); + +static void open_sap(unsigned char type,struct device *dev); +void tok_open_adapter(unsigned long dev_addr); +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 struct enet_statistics * tok_get_stats(struct device *dev); + +static struct timer_list tr_timer={NULL,NULL,0,0L,tok_open_adapter}; + +int DummyCallCount=0; + +/* This routine combined with the #DEFINE DPRINTD serves + to workaround the gcc apparent bug. in tr_tx() */ + +static void DummyCall(char * fmt,...) {DummyCallCount++;return;} + +static void PrtChanID(char *pcid, short stride) { + short i, j; + for (i=0,j=0;i<24;i++,j=j+stride) printk("%1x",((int) pcid[j])&0x0f); + printk("\n"); +} + +/* tok_probe(): Routine specified in the network device structure + to probe for an IBM Token Ring Adapter. Routine outline: + I. Interrogate hardware to determine if an adapter exists + and what the speeds and feeds are + II. Setup data structures to control execution based upon + adapter characteristics. + III. Initialize adapter operation + We expect tok_probe to be called once for each device entry + which references it. + */ + +int tok_probe(struct device *dev) { + + unsigned char segment=0, intr=0, irq=0, i=0, j=0, + cardpresent=NOTOK,temp=0; + unsigned char *t_mmio=0; + short PIOaddr=0, iAddr; + struct tok_info *ti=0; + static struct tok_info *badti=0; /* if fail after kmalloc, reuse */ + + static unsigned char Shared_Ram_Base = IBMTR_SHARED_RAM_BASE; + + /* this is the major adapter probe loop. For each call to tok_probe, + we try each remaining entry in TokBaseAddrs[] as a possible + adapter. Once an entry is rejected or assigned, we zero it to + avoid duplicate use or worthless trial for the tok probe call*/ + + for (iAddr=0; + iAddr < (sizeof(TokBaseAddrs)/sizeof(short))&&PIOaddr==0; + iAddr++) { char *tchanid, *cd_chanid, ctemp; + PIOaddr=TokBaseAddrs[iAddr]; /* address to try */ + TokBaseAddrs[iAddr] = 0; /* (and marked already used */ + if (PIOaddr == 0) continue; /* already tried this addr */ + if ( check_region(PIOaddr,4) ) { /* Make sure PIO address not + already assigned elsewhere before + we muck with I/O addresses */ + if (ibmtr_debug_trace & TRC_INIT) + DPRINTK("check_region(%4hx,4) failed.\n",PIOaddr); + PIOaddr = 0; continue; /* clear to flag fail and try next */ + } + /* Query the adapter PIO base port which will return + indication of where MMIO was placed (per tech ref + this assignment is done by BIOS - what is rational for + where it is?). We also have a coded interrupt address.*/ + + segment = inb(PIOaddr); + if (segment < 0x40 || segment > 0xe0) { /* out of range values + so we will assume non-existant IO dev */ + PIOaddr = 0; continue; /* clear to flag fail and try next */ + } + + /* Compute the linear base address of the MMIO area + as LINUX doesn't care about segments */ + t_mmio=(char *) (((segment & 0xfc) << 11) + 0x80000); + intr = segment & 0x03; /* low bits is coded interrupt # */ + if (ibmtr_debug_trace & TRC_INIT) + DPRINTK("PIOaddr: %4hx seg/intr: %2x mmio base: %p intr: %d\n", + PIOaddr, (int) segment,t_mmio,(int) intr); + /* Now we will compare expected 'channelid' strings with + what we is there to learn of ISA/MCA or not TR card */ + /* !!!WARNING:!!!! It seems pretty silly to blunder ahead + w/o verification that the mmio address we have found + is valid storage -- perhaps this is tolerable for current + hardware state??? */ + cd_chanid = (char *)(CHANNEL_ID + t_mmio); /* for efficiency */ + tchanid=pcchannelid; cardpresent=TR_ISA; /* try ISA ? */ + /* suboptimize knowing first byte different */ + ctemp = (* cd_chanid) & 0x0f; + if ( ctemp != *tchanid) { /* NOT ISA card, try MCA */ + tchanid=mcchannelid; cardpresent=TR_MCA; + if ( ctemp != *tchanid) /* Neither ISA nor MCA */ + cardpresent=NOTOK; + } + if (cardpresent != NOTOK) { /* know presumed type, try rest of ID */ + for (i=2,j=1; i<=46; i=i+2,j++) { + if ( (cd_chanid[i] & 0x0f) != tchanid[j]) { + cardpresent=NOTOK; /* match failed, not TR card */ + break; + } + } + } + + /* If we have an ISA board check for the ISA P&P version, as it has + different IRQ settings */ + if (cardpresent == TR_ISA && (*(AIPFID + t_mmio)==0x0e)) + cardpresent=TR_ISAPNP; + + if (cardpresent == NOTOK) { /* "channel_id" did not match, report */ + if (ibmtr_debug_trace & TRC_INIT) { + DPRINTK("Channel ID string not found for PIOaddr: %4hx\n", + PIOaddr); + DPRINTK("Expected for ISA: "); PrtChanID(pcchannelid,1); + DPRINTK(" found: "); PrtChanID(cd_chanid,2); + DPRINTK("Expected for MCA: "); PrtChanID(mcchannelid,1); + } + PIOaddr = 0; /* all to know not found yet */ + continue; + } + + /* !!!! we could tighten validation by checking the HW Address + against the 1-s complement.. Move the get HW logic to here */ + + } + + /* The search loop has either completed with a presumed TR adapter + or none found. Check situation ... march on if possible */ + + if (PIOaddr == 0) { /* failed to find a valid TR adapter */ + if (ibmtr_debug_trace & TRC_INIT) + DPRINTK("Unable to assign adapter to device.\n"); + return ENODEV; + } + + /*?? Now, allocate some of the pl0 buffers for this driver.. */ + /*?? Now, allocate some of the PIO PORTs for this driver.. */ + request_region(PIOaddr,4,"ibmtr"); /* record PIOaddr range as busy */ + + if (!badti) + ti = (struct tok_info *)kmalloc(sizeof(struct tok_info), GFP_KERNEL); + else { ti = badti; badti = NULL; }/*?? dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); */ + memset(ti,0,sizeof(struct tok_info)); + + ti->mmio= t_mmio; + + dev->priv = ti; /* this seems like the logical use of the + field ... lets try some empirical tests + using the token-info structure -- that + should fit with out future hope of multiple + adapter support as well /dwm */ + + switch (cardpresent) { + case TR_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; + ti->global_int_enable=GLOBAL_INT_ENABLE+((irq==9) ? 2 : irq); + ti->sram=NULL; + DPRINTK("ti->global_int_enable: %04X\n",ti->global_int_enable); + break; + case TR_MCA: + if (intr==0) irq=9; + if (intr==1) irq=3; + if (intr==2) irq=10; + if (intr==3) irq=11; + ti->global_int_enable=0; + ti->sram=(unsigned char *)((inb(PIOaddr+ADAPTRESETREL) & 0xfe) + << 12); + break; + case TR_ISAPNP: + if (intr==0) irq=9; + if (intr==1) irq=3; + if (intr==2) irq=10; + if (intr==3) irq=11; + while(!(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)); + ti->sram=(unsigned char *)((unsigned long)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN) <<12); + ti->global_int_enable=PIOaddr+ADAPTINTREL; + break; + + } + + if (ibmtr_debug_trace & TRC_INIT) { /* just report int */ + DPRINTK("irq=%d",irq); + if (ibmtr_debug_trace & TRC_INITV) { /* full chat in verbose only */ + DPRINTK(", ti->mmio=%p",ti->mmio); + printk(", segment=%02X",segment); + } + printk(".\n"); + } + + 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("%1X",ti->hw_address[j]=temp); + if(j&1) + dev->dev_addr[(j/2)]=ti->hw_address[j]+(ti->hw_address[j-1]<<4); + ++j; + } + printk("\n"); + + /* get Adapter type: 'F' = Adapter/A, 'E' = 16/4 Adapter II,...*/ + ti->adapter_type = *(char *)(ti->mmio + AIPADAPTYPE); + + /* get Data Rate: F=4Mb, E=16Mb, D=4Mb & 16Mb ?? */ + ti->data_rate = *(char *)(ti->mmio + AIPDATARATE); + + /* Get Early Token Release support?: F=no, E=4Mb, D=16Mb, C=4&16Mb */ + ti->token_release = *(char *)(ti->mmio + AIPEARLYTOKEN); + + /* How much shared RAM is on adapter ? */ + ti->avail_shared_ram = get_sram_size(ti); + + /* We need to set or do a bunch of work here based on previous results.. */ + /* Support paging? What sizes?: F=no, E=16k, D=32k, C=16 & 32k */ + ti->shared_ram_paging = *(char *)(ti->mmio + AIPSHRAMPAGE); + + /* Available DHB 4Mb size: F=2048, E=4096, D=4464 */ + ti->dhb_size4mb = *(char *) (ti->mmio + AIP4MBDHB); + + /* Available DHB 16Mb size: F=2048, E=4096, D=8192, C=16384, B=17960 */ + ti->dhb_size16mb = *(char *)(ti->mmio + AIP16MBDHB); + + DPRINTK("atype=%x, drate=%x, trel=%x, asram=%dK, srp=%x, dhb(4mb=%x, 16mb=%x)\n",ti->adapter_type, + ti->data_rate, ti->token_release, ti->avail_shared_ram/2, ti->shared_ram_paging, ti->dhb_size4mb, + ti->dhb_size16mb); + + /* We must figure out how much shared memory space this adapter + will occupy so that if there are two adapters we can fit both + in. Given a choice, we will limit this adapter to 32K. The + maximum space will will use for two adapters is 64K so if the + adapter we are working on demands 64K (it also doesn't support + paging), then only one adapter can be supported. */ + + /* determine how much of total RAM is mapped into PC space */ + ti->mapped_ram_size=1<<(((*(unsigned char *) + (ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2)+4); + ti->page_mask=0; + if (ti->shared_ram_paging == 0xf) { /* No paging in adapter */ + ti->mapped_ram_size = ti->avail_shared_ram; + } else { unsigned char pg_size; + + DPRINTK("shared ram page size: %dK\n",ti->mapped_ram_size/2); +#ifdef ENABLE_PAGING + switch(ti->shared_ram_paging) { + case 0xf: break; + case 0xe: ti->page_mask=(ti->mapped_ram_size==32) ? 0xc0 : 0; + pg_size=32; /* 16KB page size */ + break; + case 0xd: ti->page_mask=(ti->mapped_ram_size==64) ? 0x80 : 0; + pg_size=64; /* 32KB page size */ + break; + case 0xc: ti->page_mask=(ti->mapped_ram_size==32) ? 0xc0 : 0; + ti->page_mask=(ti->mapped_ram_size==64) ? 0x80 : 0; + DPRINTK("Dual size shared RAM page (code=0xC), don't support it!\n"); + /* nb/dwm: I did this because RRR (3,2) bits are documented as + R/O and I can't find how to select which page size */ + /* Also, the above conditional statement sequence is invalid */ + /* as page_mask will always be set by the second stmt */ + badti=ti; + break; + default: DPRINTK("Unknown shared ram paging info %01X\n",ti->shared_ram_paging); + badti=ti; /* bail out if bad code */ + break; + } + if(ti->page_mask) { + if(pg_size > ti->mapped_ram_size) { + DPRINTK("Page size (%d) > mapped ram window (%d), can't page.\n", + pg_size, ti->mapped_ram_size); + ti->page_mask = 0; /* reset paging */ + } else { + ti->mapped_ram_size=ti->avail_shared_ram; /****** ?????????? *******/ + DPRINTK("Shared RAM paging enabled. Page size : %uK\n",((ti->page_mask^ 0xff)+1)>>2); + } + } +#else +#endif + } + + if (cardpresent==TR_ISA) { /* finish figuring the shared RAM address */ + static unsigned char ram_bndry_mask[]={0xfe, 0xfc, 0xf8, 0xf0}; + unsigned char new_base, rrr_32, chk_base, rbm; + rrr_32 = (*(unsigned char *) + (ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2; + rbm = ram_bndry_mask[rrr_32]; + new_base = (Shared_Ram_Base + (~rbm)) & rbm; /* up to boundary */ + chk_base = new_base + (ti->mapped_ram_size>>3); + if (chk_base > (IBMTR_SHARED_RAM_BASE+IBMTR_SHARED_RAM_SIZE)) { + DPRINTK("Shared RAM for this adapter (%05x) exceeds driver" + " limit (%05x), adapter not started.\n", + chk_base<<12, (IBMTR_SHARED_RAM_BASE+ + IBMTR_SHARED_RAM_SIZE)<<12); + badti=ti; + } else { /* seems cool, record what we have figured out */ + ti->sram_base = new_base; + Shared_Ram_Base = new_base; + } + } + + /* dwm: irq and other final setup moved here so if we find other + unrecognized values OR shared ram conflicts, we can still + bail out in a rather benign fashion. */ + + if (badti) return ENODEV; + + DPRINTK("Using %dK shared RAM\n",ti->mapped_ram_size/2); + + if (request_irq (dev->irq = irq, &tok_interrupt,0,"IBM TR") != 0) { + DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",irq); + badti = ti; /* keep track of unused tok_info */ + return ENODEV; + } + irq2dev_map[irq]=dev; + + DPRINTK("%s",version); /* As we have passed card identification, + let the world know we're here! */ + dev->base_addr=PIOaddr; /* set the value for device */ + + dev->open=tok_open; + dev->stop=tok_close; + dev->hard_start_xmit=tok_send_packet; + dev->get_stats = NULL; + dev->get_stats = tok_get_stats; + dev->set_multicast_list = NULL; + tr_setup(dev); + tok_init_card((unsigned long)dev); + + return 0; /* Return 0 to indicate we have found a Token Ring card. */ +} + +/* query the adapter for the size of shared RAM */ + +unsigned char get_sram_size(struct tok_info *adapt_info) { + + unsigned char avail_sram_code; + static unsigned char size_code[]={ 0,16,32,64,127,128 }; + + /* Adapter gives + 'F' -- use RRR bits 3,2 + 'E' -- 8kb 'D' -- 16kb + 'C' -- 32kb 'A' -- 64KB + 'B' - 64KB less 512 bytes at top + (WARNING ... must zero top bytes in INIT */ + + avail_sram_code=0xf-*(adapt_info->mmio + AIPAVAILSHRAM); + if(avail_sram_code) + return size_code[avail_sram_code]; + else /* for code 'F', must compute size from RRR(3,2) bits */ + + return 1<<(((*(unsigned char *) + (adapt_info->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2)+4); +} + +static int tok_open(struct device *dev) { + + struct tok_info *ti=(struct tok_info *)dev->priv; + + if(ti->open_status==CLOSED) { + tok_init_card((unsigned long)dev); + } + + if(ti->open_status==IN_PROGRESS) { + sleep_on(&ti->wait_for_reset); + } + + if(ti->open_status==SUCCES) { + dev->tbusy=0; + dev->interrupt=0; + dev->start=1; + /* NEED to see smem size *AND* reset high 512 bytes if + needed */ + return 0; + } + else + return -EAGAIN; + +} + +static int tok_close(struct device *dev) { + + struct tok_info *ti=(struct tok_info *) dev->priv; + + 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; + + ti->open_status=CLOSED; + + sleep_on(&ti->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 irq, struct pt_regs *regs) +{ + + unsigned char status; + struct tok_info *ti; +/* 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 + ti=(struct tok_info *) dev->priv; + + switch (ti->do_tok_int) { + case NOT_FIRST: + + /* Begin the regular interrupt handler HERE inline to avoid + the extra levels of logic and call depth for the + original solution. */ + + + dev->interrupt=1; + + /* Disable interrupts till processing is finished */ + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE); + + /* Reset interrupt for ISA boards */ + if(ti->global_int_enable) + outb(0,ti->global_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++) + printk("%02X ",*check_reason); + printk("\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; + dev->interrupt=0; + } + + else if((*(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN)) & (TCR_INT + ERR_INT + ACCESS_INT)) { + + DPRINTK("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; + dev->interrupt=0; + } + + 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: { + struct srb_open_response *open_response=(struct srb_open_response *)(ti->init_srb); + + 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); + ti->current_skb=NULL; + + if(open_response->ret_code==7) { + if(!ti->auto_ringspeedsave && (open_response->error_code==0x24)) { + DPRINTK("open failed: Adapter speed must match ring speed if Automatic Ring Speed Save is disabled\n"); + ti->open_status=FAILURE; + wake_up(&ti->wait_for_reset); + } + else if(open_response->error_code==0x24) + DPRINTK("retrying open to adjust to ring speed\n"); + + else if((open_response->error_code==0x2d) && ti->auto_ringspeedsave) + DPRINTK("No signal detected for Auto Speed Detection\n"); + else DPRINTK("Unrecoverable error: error code = %02X\n",open_response->error_code); + } + else if(!open_response->ret_code) { + DPRINTK("board opened...\n"); + *(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); + open_sap(EXTENDED_SAP,dev); +/* YdW probably hates me */ + goto skip_reset; + } + else { + DPRINTK("open failed: ret_code = %02X, retrying\n",open_response->ret_code); + } + if(ti->open_status!=FAILURE) { + tr_timer.expires=TR_RETRY_INTERVAL; + tr_timer.data=(unsigned long)dev; + tr_timer.next=tr_timer.prev=NULL; + add_timer(&tr_timer); + } + } + break; + + case DIR_CLOSE_ADAPTER: + wake_up(&ti->wait_for_tok_int); + break; + case DLC_OPEN_SAP: { + struct dlc_open_sap *open_sap=(struct dlc_open_sap *)ti->srb; + if(open_sap->ret_code) { + DPRINTK("open_sap failed: ret_code = %02X,retrying\n",open_sap->ret_code); + tr_timer.expires=TR_RETRY_INTERVAL; + tr_timer.data=(unsigned long)dev; + tr_timer.next=tr_timer.prev=NULL; + add_timer(&tr_timer); + } + else { + ti->exsap_station_id=open_sap->station_id; + ti->open_status=SUCCES; /* TR adapter is now available */ + wake_up(&ti->wait_for_reset); + } + } + 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 { + DPRINTK("Line errors %02X, Internal errors %02X, Burst errors %02X\n", + read_log->line_errors,read_log->internal_errors,read_log->burst_errors); + DPRINTK("A/C errors %02X, Abort delimiters %02X, Lost frames %02X\n", + read_log->A_C_errors,read_log->abort_delimiters,read_log->lost_frames); + DPRINTK("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); +skip_reset: + } + + 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 try to reopen the adapter.\n"); + tr_timer.expires=TR_RETRY_INTERVAL; + tr_timer.data=(unsigned long)dev; + tr_timer.next=tr_timer.prev=NULL; + add_timer(&tr_timer); + } else if (ring_status & (HARD_ERROR + XMIT_BEACON + + AUTO_REMOVAL + REMOVE_RECV + RING_RECOVER)) + 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)); + else + ti->tr_stats.tx_packets++; + 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; + + break; + case FIRST_INT: + initial_tok_int(dev); + break; + default: + DPRINTK("Unexpected interrupt from tr adapter\n"); + } + +} + +static void initial_tok_int(struct device *dev) { + + int i; + unsigned char *encoded_addr; + struct tok_info *ti; + + ti=(struct tok_info *) dev->priv; + + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE); + + /* Reset interrupt for ISA boards */ + if(ti->global_int_enable) + outb(0,ti->global_int_enable); + + ti->do_tok_int=NOT_FIRST; + + DPRINTK("Initial tok int received\n"); + + if(!ti->sram) { /* we assign the address for ISA devices */ + /* set RRR even to D000 for shared ram address */ + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)= + ti->sram_base; + ti->sram=(char *)(ti->sram_base << 12); + } + ti->init_srb=ti->sram+ntohs(*(unsigned short *)(ti->mmio+ ACA_OFFSET + WRBR_EVEN)); + SET_PAGE(ntohs(*(unsigned short *)(ti->mmio+ ACA_OFFSET + WRBR_EVEN))); + +#if 1 + DPRINTK("init_srb(%p):",ti->init_srb); + for(i=0;i<17;i++) + printk("%02X ",*(ti->init_srb+i)); + printk("\n"); +#endif + + + DPRINTK("srb_init_response->encoded_address: %04X\n",((struct srb_init_response *)ti->init_srb)->encoded_address); + DPRINTK("ntohs(srb_init_response->encoded_address): %04X\n",ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address)); + encoded_addr=(unsigned char *)(ti->sram + ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address)); + + DPRINTK("encoded addr (%04X,%04X,%p): ", + ((struct srb_init_response *)ti->init_srb)->encoded_address, + ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address), + encoded_addr); + ti->auto_ringspeedsave=((struct srb_init_response *)ti->init_srb)->init_status_2 & 0x4 ? TRUE : FALSE; + + for(i=0;i<TR_ALEN;i++) + printk("%02X%s",dev->dev_addr[i]=encoded_addr[i],(i==TR_ALEN-1) ? "" : ":" ); + printk("\n"); + + tok_open_adapter((unsigned long)dev); + +} + +static void tok_init_card(unsigned long dev_addr) { + + struct tok_info *ti; + short PIOaddr; + int i; + struct device *dev=(struct device *)dev_addr; + PIOaddr = dev->base_addr; + ti=(struct tok_info *) dev->priv; + + /* Special processing for first interrupt after reset */ + ti->do_tok_int=FIRST_INT; + + /* Reset adapter */ + + dev->tbusy=1; /* nothing can be done before reset and open completed */ + +#ifdef ENABLE_PAGING + if(ti->page_mask) { + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN)=SRPR_ENABLE_PAGING; + } +#endif + + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=~(INT_ENABLE); + DPRINTK("resetting card\n"); + outb(0,PIOaddr+ADAPTRESET); + for(i=jiffies+5;jiffies<=i;); /* wait 50ms */ + outb(0,PIOaddr+ADAPTRESETREL); + DPRINTK("card reset\n"); + + ti->open_status=IN_PROGRESS; + + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE; + +} + +static void open_sap(unsigned char type,struct device *dev) { + + struct tok_info *ti=(struct tok_info *) dev->priv; + struct dlc_open_sap *open_sap=(struct dlc_open_sap *)ti->srb; + + SET_PAGE(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; + +} + +void tok_open_adapter(unsigned long dev_addr) { + + struct device *dev=(struct device *)dev_addr; + struct dir_open_adapter *open_adapter; + struct tok_info *ti; + ti=(struct tok_info *) dev->priv; + + DPRINTK("now opening the board...\n"); + + *(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); + + 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; + + ti->srb=ti->init_srb; /* We use this one in the interrupt handler */ + + *(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; + +} + +static void tr_tx(struct device *dev) { + + struct tok_info *ti=(struct tok_info *) dev->priv; + 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 hdr_len; + unsigned char *dhb; + +/* */ + DPRINTD("ti=%p asb=(%p,%p) arb=(%p,%p) srb=(%p,%p)\n", + ti , ti->asb, xmit_resp, ti->arb, xmit_req, ti->srb, xmit); +/* */ + +#if 0 +DPRINTK("transmitting...\n"); +#endif + + if(xmit_resp->ret_code!=0xff) DPRINTK("ASB not free !!!\n"); + + /* in providing the transmit interrupts, + is telling us it is ready for data and + providing a shared memory address for us + to stuff with data. Here we compute the + effective address where we will place data.*/ + 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,(int)0x0ff,TR_ALEN); + memset(dhb+2+TR_ALEN,0,TR_ALEN); + *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + + ISRA_ODD)=RESP_IN_ASB; + return; + } + + /* the token ring packet is copied from sk_buff to the adapter + buffer identified in the command data received with the + interrupt. The sk_buff area was set up with a maximum + sized route information field so here we must compress + out the extra (all) rif fields. */ + /* nb/dwm .... I re-arranged code here to avoid copy of extra + bytes, ended up with fewer statements as well */ + + /* TR arch. identifies if RIF present by high bit of source + address. So here we check if RIF present */ + if(!(((struct trh_hdr *)(&ti->current_skb->data))->saddr[0] & 0x80)) { + hdr_len=sizeof(struct trh_hdr)-18; +#if 0 +DPRINTK(("hdr_length: %d, frame length: %ld\n",hdr_len, + ti->current_skb->len-18)); +#endif + } /* TR packet includes RIF data ... preserve it */ + else { + hdr_len=((ntohs(((struct trh_hdr *)(&ti->current_skb->data))->rcf) + & TR_RCF_LEN_MASK)>>8)+sizeof(struct trh_hdr)-18; +#if 0 +/* rework the following if activated, hdr_len <> rif_len */ +DPRINTK("rcf: %02X rif_len: %d\n",((struct trh_hdr *)&ti->current_skb->data)->rcf,wrk_len); +DPRINTK("hdr_length: %d, frame length: %ld\n",sizeof(struct trh_hdr)-18+hdr_len, + ti->current_skb->len-18+hdr_len); +#endif + } + + /* header length including rif is computed above, now move the data + and set fields appropriately. */ + + memcpy(dhb,ti->current_skb->data,hdr_len); + dhb+=hdr_len; + xmit_resp->hdr_length= hdr_len; + xmit_resp->frame_length=htons(ti->current_skb->len + -sizeof(struct trh_hdr)+hdr_len); + + /* now copy the actual packet data next to hdr */ + 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 tok_info *ti=(struct tok_info *) dev->priv; + + 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; + ti->tr_stats.rx_dropped++; + *(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"); + ti->tr_stats.rx_dropped++; + 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; + + ti->tr_stats.rx_packets++; + + skb->protocol=tr_type_trans(skb,dev); + netif_rx(skb); + + return; +} + +static int tok_send_packet(struct sk_buff *skb, struct device *dev) { + + struct tok_info *ti=(struct tok_info *) dev->priv; + +#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... */ + return 1; + } + + /* 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; +} + +/* tok_get_stats(): Basically a scaffold routine which will return + the address of the tr_statistics structure associated with + this device -- the tr.... structure is a ethnet look-alike + so at least for this iteration may suffice. */ + +static struct enet_statistics * tok_get_stats(struct device *dev) { + + struct tok_info *toki; + toki=(struct tok_info *) dev->priv; + return (struct enet_statistics *) &toki->tr_stats; +} |