openblt

a hobby OS from the late 90s
git clone http://frotz.net/git/openblt.git
Log | Files | Refs | LICENSE

ne2000.c (29918B)


      1 /* $Id: //depot/blt/srv/ne2000/ne2000.c#2 $
      2 **
      3 ** Copyright 1998 Brian J. Swetland
      4 ** All rights reserved.
      5 **
      6 ** Redistribution and use in source and binary forms, with or without
      7 ** modification, are permitted provided that the following conditions
      8 ** are met:
      9 ** 1. Redistributions of source code must retain the above copyright
     10 **    notice, this list of conditions, and the following disclaimer.
     11 ** 2. Redistributions in binary form must reproduce the above copyright
     12 **    notice, this list of conditions, and the following disclaimer in the
     13 **    documentation and/or other materials provided with the distribution.
     14 ** 3. The name of the author may not be used to endorse or promote products
     15 **    derived from this software without specific prior written permission.
     16 **
     17 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 */
     28 /* This file contains a network driver core for the NE2000 card.
     29 // Use at your own risk!    (C) Copyright 1997,
     30 // Douglas Armstrong	site@xnet.com	drarmstr@uiuc.edu	10/11/97
     31 // ... 
     32 // derived from National Semiconductor datasheets and application notes
     33 // as well as the Linux driver by Donald Becker */
     34 
     35 /* History Log:
     36 10-25-97	Setup under CVS					drarmstr
     37 10-27-97	Added alloc_buffer() and free_buffer()		drarmstr
     38 12-08-97	Finished scatter gather				drarmstr
     39 03-03-98	Minor bug / Output cleanup			drarmstr
     40 03-04-98	snic->tx_buf[]  packet_buf -> packet_buf*	drarmstr
     41 03-04-98	added passback pointer to notify and snic	drarmstr
     42 */
     43 
     44 #ifndef NULL
     45 #define NULL	0
     46 #endif	/* NULL */
     47 #include <i386/io.h>
     48 #include "ne2000.h"
     49 #include "ne2k.h"
     50 #include "err.h"
     51 extern void kprintf(char *, ...);	/* Make sure your OS has these...*/
     52 extern void idle();
     53 extern unsigned long ticks;	
     54 			/* replace with a better timeout method, like wait() */
     55 
     56 #ifdef XXX
     57 static char *BLARGH = 0xB8000 + 160*23 + 152;
     58 #define t(a) { BLARGH[0] = #a[0]; }
     59 #define T(a) { BLARGH[2] = #a[0]; }
     60 #else
     61 #define t(a) {}
     62 #define T(a) {}
     63 #endif
     64 
     65 /* we may have to change inb and outb to inb_p and outb_p.
     66 // Note, none of this code is gauranteed to be reentrant.
     67 
     68 // Note, don't create two nics to the same card and then try to use both 
     69 // of them.  The results will be unpredictable.
     70 
     71 // This violates IO resource manegment if your OS handles that, but it 
     72 // works for now.  Make sure that the driver has privlige access to the 
     73 // mapped I/O space and the IRQ.*/
     74 static unsigned int default_ports[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0 };
     75 
     76 /* internal procedure, don't call this directly.*/
     77 int nic_probe(int addr) {
     78 	uint regd;
     79 	uint state=inb(addr);		/* save command state */
     80 
     81 	if(inb(addr==0xff)) return ERRNOTFOUND;
     82 
     83 	outb(NIC_DMA_DISABLE | NIC_PAGE1 | NIC_STOP, addr);
     84 	regd=inb(addr + 0x0d);
     85 	outb(0xff, addr + 0x0d);
     86 	outb(NIC_DMA_DISABLE | NIC_PAGE0, addr);
     87 	inb(addr + FAE_TALLY);	/* reading CNTR0 resets it.*/
     88 	if(inb(addr + FAE_TALLY)) {	/* counter didn't clear so probe fails*/
     89 		outb(state,addr);	/* restore command state*/
     90 		outb(regd,addr + 0x0d);
     91 		return ERRNOTFOUND; }
     92 
     93 	return addr;	/* network card detected at io addr;*/
     94 }
     95 
     96 /* Detects for presence of NE2000 card.  Will check given io addr that 
     97 // is passed to it as well as the default_ports array.  Returns ERRNOTFOUND 
     98 // if no card is found, i/o address otherwise.  This does conduct an ISA
     99 // probe, so it's not always a good idea to run it.  If you already have 
    100 // the information, then you can just use that with nic_init().*/
    101 int nic_detect(int given) {
    102 	int found; int f;
    103 	kprintf("NE2000: detecting card...");
    104 	if(given) if((found=nic_probe(given))!=ERRNOTFOUND) {
    105 		kprintf("found at given:0x%x\n",given);
    106 		return found; }
    107 
    108 	/* probe for PCI clones here....  or not...*/
    109 
    110 	for(f=0;default_ports[f]!='\0';f++) {
    111 		// perform resource manegment here
    112 		if((found=nic_probe(default_ports[f]))!=ERRNOTFOUND) {
    113 			kprintf("found at default:0x%x\n",default_ports[f]);
    114 			return found; }
    115 	}
    116 	kprintf("none found.\n");
    117 	return ERRNOTFOUND;
    118 }
    119 
    120 /* This initializes the NE2000 card.  If it turns out the card is not
    121 // really a NE2000 after all then it will return ERRNOTFOUND, else NOERR
    122 // It also dumps the prom into buffer prom for the upper layers..
    123 // Pass it a nic with a null iobase and it will initialize the structure for
    124 // you, otherwise it will just reinitialize it. */
    125 int nic_init(snic* nic, int addr, unsigned char *prom, unsigned char *manual) {
    126 	uint f;
    127 	kprintf("NE2000: reseting NIC card...");
    128 	if(!nic->iobase) {
    129 		nic->iobase=addr;
    130 		nic_stat_clear(&nic->stat);
    131 		nic->pstart=0; nic->pstop=0; nic->wordlength=0; 
    132 		nic->current_page=0;
    133 		nic->notify=NULL;
    134 		for(f=0;f<MAX_TX;f++) {
    135 			nic->tx_packet[f]= NULL;
    136 /*			nic->tx_packet[f].count=0;
    137 			nic->tx_packet[f].buf=NULL;
    138 			nic->tx_packet[f].page=0; */
    139 		}
    140 		nic->last_tx=NULL;
    141 		nic->busy=0;
    142 		nic->sending=0;
    143 	} else {
    144 		if(!nic->iobase || nic->iobase!=addr) return ERR;
    145 	}
    146 	
    147 
    148 	outb(inb(addr + NE_RESET), addr + NE_RESET);	/* reset the NE2000*/
    149 	while(!(inb_p(addr+INTERRUPTSTATUS) & ISR_RST)) {
    150 		/* TODO insert timeout code here.*/
    151 	}
    152 	kprintf("done.\n");
    153 	outb_p(0xff,addr + INTERRUPTSTATUS);	/* clear all pending ints*/
    154 
    155 	// Initialize registers
    156 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr);	/* enter page 0*/
    157 	outb_p(DCR_DEFAULT, addr + DATACONFIGURATION);
    158 	outb_p(0x00, addr + REMOTEBYTECOUNT0);
    159 	outb_p(0x00, addr + REMOTEBYTECOUNT1);
    160 	outb_p(0x00, addr + INTERRUPTMASK);	/* mask off all irq's*/
    161 	outb_p(0xff, addr + INTERRUPTSTATUS);	/* clear pending ints*/
    162 	outb_p(RCR_MON, RECEIVECONFIGURATION);	/* enter monitor mode*/
    163 	outb_p(TCR_INTERNAL_LOOPBACK, TRANSMITCONFIGURATION); /* internal loopback*/
    164 	
    165 	nic->wordlength=nic_dump_prom(nic,prom);
    166 	if(prom[14]!=0x57 || prom[15]!=0x57) {
    167 		kprintf("NE2000: PROM signature does not match NE2000 0x57.\n");
    168 		return ERRNOTFOUND;
    169 	}
    170 	kprintf("NE2000: PROM signature matches NE2000 0x57.\n");
    171 
    172 	/* if the wordlength for the NE2000 card is 2 bytes, then
    173 	// we have to setup the DP8390 chipset to be the same or 
    174 	// else all hell will break loose.*/
    175 	if(nic->wordlength==2) {
    176 		outb_p(DCR_DEFAULT_WORD, addr + DATACONFIGURATION);
    177 	}
    178 	nic->pstart=(nic->wordlength==2) ? PSTARTW : PSTART;
    179 	nic->pstop=(nic->wordlength==2) ? PSTOPW : PSTOP;
    180 
    181 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr);
    182 	outb_p(nic->pstart, addr + TRANSMITPAGE);	/* setup local buffer*/
    183 	outb_p(nic->pstart + TXPAGES, addr + PAGESTART);
    184 	outb_p(nic->pstop - 1, addr + BOUNDARY);
    185 	outb_p(nic->pstop, addr + PAGESTOP);
    186 	outb_p(0x00, addr + INTERRUPTMASK);	/* mask off all irq's*/
    187 	outb_p(0xff, addr + INTERRUPTSTATUS);	/* clear pending ints*/
    188 	nic->current_page=nic->pstart + TXPAGES;
    189 	
    190 	/* put physical address in the registers */
    191 	outb_p(NIC_DMA_DISABLE | NIC_PAGE1 | NIC_STOP, addr);  /* switch to page 1 */
    192 	if(manual) for(f=0;f<6;f++) outb_p(manual[f], addr + PHYSICAL + f);
    193 	else for(f=0;f<LEN_ADDR;f++) outb_p(prom[f], addr + PHYSICAL + f);
    194 	kprintf("NE2000: Physical Address- ");
    195 	kprintf("%X:%X:%X:%X:%X:%X\n",
    196 		inb(addr+PAR0),inb(addr+PAR1),inb(addr+PAR2),
    197 		inb(addr+PAR3),inb(addr+PAR4),inb(addr+PAR5));
    198 
    199 	/* setup multicast filter to accept all packets*/
    200 	for(f=0;f<8;f++) outb_p(0xFF, addr + MULTICAST + f);
    201 
    202 	outb_p(nic->pstart+TXPAGES, addr + CURRENT);
    203 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr); /* switch to page 0 */
    204 
    205 	return NOERR;
    206 }
    207 
    208 /* This registers the function that will be called when a new packet 
    209 // comes in. */
    210 void nic_register_notify(snic *nic,
    211 			void (*newnotify)(void*,packet_data*), void *passback){
    212 	nic->kore= passback;
    213 	nic->notify= newnotify;
    214 }
    215 
    216 /* start the NIC so it can actually recieve or transmit packets */
    217 void nic_start(snic *nic, int promiscuous) {
    218 	int iobase;
    219 	kprintf("NE2000: Starting NIC.\n");
    220 	if(!nic || !nic->iobase) {
    221 		kprintf("NE2000: can't start a non-initialized card.\n");
    222 		return; }
    223 	iobase=nic->iobase;
    224 	outb(0xff, iobase + INTERRUPTSTATUS);
    225 	outb(IMR_DEFAULT, iobase + INTERRUPTMASK);
    226 	outb(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    227 	outb(TCR_DEFAULT, iobase + TRANSMITCONFIGURATION);
    228 	if(promiscuous)
    229 		outb(RCR_PRO | RCR_AM, iobase + RECEIVECONFIGURATION);
    230 	else
    231 		outb(RCR_DEFAULT, iobase + RECEIVECONFIGURATION);
    232 
    233 	/* The following is debugging code! */
    234 #ifdef SHIT
    235 	kprintf("NE2000: Trying to fire off an IRQ.\n");
    236 	outb_p(0x50,iobase+INTERRUPTMASK);
    237 	outb_p(0x00,iobase+REMOTEBYTECOUNT0);
    238 	outb_p(0x00,iobase+REMOTEBYTECOUNT1); 
    239 	outb_p(NIC_REM_READ | NIC_START,iobase);   /* this should fire off */
    240 	outb_p(IMR_DEFAULT,iobase+INTERRUPTMASK);  /* an interrupt... */
    241 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    242 	outb_p(0xff,iobase+INTERRUPTSTATUS); 
    243 #endif
    244             /* End debugging code */
    245 }
    246 
    247 /* stops the NIC */
    248 void nic_stop(snic *nic) {
    249 	unsigned char tmp_buffer[16];	
    250 	if(!nic || !nic->iobase) return;    /* make sure card was initialized */
    251 	nic_init(nic,nic->iobase,tmp_buffer,NULL);
    252 }
    253 
    254 void nic_isr(snic *nic) {
    255 	uint isr;	/* Illinois Sreet Residence Hall */
    256 	uint overload;
    257 	if(!nic || !nic->iobase) return;    /* make sure card was initialized */
    258 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0, nic->iobase);
    259 	overload=MAX_LOAD+1;
    260 	while((isr=inb_p(nic->iobase+INTERRUPTSTATUS))) {
    261 		if((--overload)<=0) break;
    262 		if(isr & ISR_OVW)			nic_overrun(nic);
    263 		else if(isr & (ISR_PRX | ISR_RXE))	nic_rx(nic);
    264 		if(isr & ISR_PTX)			nic_tx(nic);
    265 		else if(isr & ISR_TXE)			nic_tx_err(nic);
    266 		if(isr & ISR_CNT)			nic_counters(nic);
    267 		if(isr & ISR_RDC) outb_p(ISR_RDC, nic->iobase+ INTERRUPTSTATUS);
    268 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, nic->iobase);
    269 	}
    270 	if(isr) {
    271 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, nic->iobase);
    272 		if(!overload) {
    273 			kprintf("NE2000: Too many requests in ISR.  ISR:%X  MaxLoad:%X\n",
    274 				isr, MAX_LOAD);
    275 			outb_p(ISR_ALL, nic->iobase + INTERRUPTSTATUS); // clear
    276 		} else {
    277 			kprintf("NE2000: Unhandeled interrupt, ISR:%X\n",isr);
    278 			outb_p(0xff, nic->iobase + INTERRUPTSTATUS);
    279 								// Ack it anyway
    280 		}
    281 	}
    282 }
    283 
    284 /* You should call this before you just read the stats directlly from the
    285 // snic struct.  This procedure updates the counters */
    286 nic_stat nic_get_stats(snic *nic) { 
    287 	nic->stat.errors.frame_alignment+=inb_p(nic->iobase + FAE_TALLY);
    288 	nic->stat.errors.crc+=inb_p(nic->iobase + CRC_TALLY);
    289 	nic->stat.errors.missed_packets+=inb_p(nic->iobase + MISS_PKT_TALLY);
    290 	return nic->stat;
    291 }
    292 
    293 void nic_stat_clear(nic_stat *that) {
    294 	that->rx_packets=0;
    295 	that->tx_buffered=0;		that->tx_packets=0;
    296 	that->errors.frame_alignment=0;	that->errors.crc=0;
    297 	that->errors.missed_packets=0;	that->errors.rx=0;
    298 	that->errors.rx_size=0;		that->errors.rx_dropped=0;
    299 	that->errors.rx_fifo=0;		that->errors.rx_overruns=0;
    300 	that->errors.tx_collisions=0;
    301 }
    302 
    303 /* Since this could be called by more than one other device, it should
    304 // be properly protected for reentrancy.  We should put in a proper
    305 // semaphore or something, look into this.
    306 // NOTE: It is your responsibility to clean up the packet_buffer when you 
    307 // are done calling this. */
    308 /* TODO- Have it check how lonk a transmission is taking and attempt to 
    309     restart the card if it's too long. */
    310 int nic_send_packet(snic *nic, packet_buffer *buffer) {
    311 	uint timeout;	uint f;	int iobase;
    312 /*	kprintf("nic_send_packet()\n"); */
    313 	if(!buffer) return ERRARG;
    314 	if(!buffer->count || !buffer->buf) return ERRARG;
    315 	if(!nic || !nic->iobase) return ERR;
    316 	iobase=nic->iobase;
    317 
    318 	t(A);
    319         
    320 	buffer->len=0;
    321 	for(f=0;f<buffer->count;f++) buffer->len+=buffer->buf[f].len;
    322 	if(buffer->len>MAX_LENGTH) return ERRTOOBIG;
    323 
    324         t(B);
    325         
    326 	/* the following wait for anyother tasks that are calling 
    327 	// nic_send_packet() right now.  Note that this doesn't use
    328 	// an atomic semaphore, so something MAY leak through. */
    329 /*	timeout=ticks+10;	wait 10 ticks */
    330 	timeout=ticks+100;
    331 	while(nic->busy && ticks<=timeout) idle();
    332 	/* Replace this with a proper timeout thing that doesn't use the
    333 	// ticks method which will be diffrent on each OS. */
    334 	t(C);
    335         if(nic->busy) {
    336 		kprintf("NE2000: ERROR: Card stalled, timeout.\n");
    337 		return ERRTIMEOUT;
    338 	}
    339 	nic->busy=1;	/* mark as busy, replace with semaphore */
    340 
    341         t(D);
    342         
    343 	outb_p(0x00, iobase + INTERRUPTMASK);	/* mask ints for now */
    344         t(E);
    345         
    346 	timeout=ticks+TIMEOUT_TX;
    347 	while(idle(), ticks<=timeout) for(f=0;f<MAX_TX;f++) {
    348 		if(!nic->tx_packet[f]) {
    349                     t(F);
    350                     
    351 /*			nic->tx_packet[f]=*buffer;*/
    352 			nic->tx_packet[f]=buffer;
    353 			nic->tx_packet[f]->page=nic->pstart + (f * MAX_PAGESPERPACKET);
    354 								/*output page */
    355 
    356 /*			kprintf("NE2000: sending packet with count:%x on page:%X with buffer:%x\n",
    357 				buffer->count,buffer->page,buffer->buf); */
    358                         t(>);
    359                         
    360                         nic_block_output(nic,nic->tx_packet[f]);
    361 
    362                         t(<);
    363                         
    364 			if(!nic->sending) {
    365 				nic->send=f;	nic->sending=1;
    366 				/* now let's actually trigger the transmitter */
    367                                 t(I);
    368                                 
    369                                 if(nic_send(nic,f)<0) {
    370 					nic->sending=0;
    371 					break; }
    372                                 
    373 				/* note, the nic_tx() interrupt will mark this
    374 				// tx_packet buffer as free again once it
    375 				// confirms that the packet was sent. */
    376 			}
    377                         t(J);
    378                                 
    379 			nic->stat.tx_buffered++;
    380 			outb_p(IMR_DEFAULT, iobase + INTERRUPTMASK); /* unmask */
    381 			nic->busy=0;	/* V() */
    382                         t(K);
    383                         
    384 			return NOERR;
    385 		}
    386 	}
    387 
    388         t(L);
    389         
    390 	outb_p(IMR_DEFAULT, iobase + INTERRUPTMASK);	/* unmask */
    391 	nic->busy=0;	/* V() */
    392 	kprintf("NE2000: ERROR: Transmitter stalled, card timeout.\n");
    393 	if(f==MAX_TX) {
    394 		kprintf("NE2000: ERROR: There are no transmit buffers available.  TX stalled.\n");
    395 		return ERRTIMEOUT;
    396 	}
    397 	return ERR;
    398 }
    399 
    400 /* dumps the prom into a 16 byte buffer and returns the wordlength of
    401 // the card.
    402 // You should be able to make this procedure a wrapper of nic_block_input(). */
    403 int nic_dump_prom(snic *nic, unsigned char *prom) {
    404 	uint f;
    405 	int iobase=nic->iobase;
    406 	char wordlength=2;		/* default wordlength of 2 */
    407 	unsigned char dump[32];
    408 	outb_p(32, iobase + REMOTEBYTECOUNT0);	  /* read 32 bytes from DMA->IO */
    409 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);  /*  this is for the PROM dump */
    410 	outb_p(0x00, iobase + REMOTESTARTADDRESS0); /* configure DMA for 0x0000 */
    411 	outb_p(0x00, iobase + REMOTESTARTADDRESS1);
    412 	outb_p(NIC_REM_READ | NIC_START, iobase);
    413 	for(f=0;f<32;f+=2) {
    414 		dump[f]=inb_p(iobase + NE_DATA);
    415 		dump[f+1]=inb_p(iobase + NE_DATA);
    416 		if(dump[f]!=dump[f+1]) wordlength=1;
    417 	}
    418 	/* if wordlength is 2 bytes, then collapse prom to 16 bytes */
    419 	for(f=0;f<LEN_PROM;f++) prom[f]=dump[f+((wordlength==2)?f:0)];
    420 /*	kprintf("NE2000: prom dump - ");
    421 	for(f=0;f<LEN_PROM;f++) kprintf("%X",prom[f]);
    422 	kprintf("\n");	*/
    423 
    424 	return wordlength;
    425 }
    426 
    427 void nic_overrun(snic *nic) {
    428 	uint tx_status;	int iobase=nic->iobase;
    429 	long starttime;	uint resend=0;
    430 	kprintf("NE2000: Receive packet ring overrun!\n");
    431 	if(!nic || !nic->iobase) return;
    432 	tx_status=inb_p(iobase) & NIC_TRANSMIT;
    433 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, iobase);
    434 	nic->stat.errors.rx_overruns++;
    435 
    436 	starttime=ticks;
    437 kprintf("BEFORE\n");
    438 	while(ticks-starttime<=10/*ticks to wait*/) idle();
    439 	/* Arrgh!  TODO: Replace this whole crappy code with a decent
    440 	// wait method.  We need to wait at least 1.6ms as per National
    441 	// Semiconductor datasheets, but we should probablly wait a 
    442 	// little more to be safe. */
    443 kprintf("AFTER\n");
    444 
    445 	outb_p(0x00, iobase + REMOTEBYTECOUNT0);
    446 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);
    447 	if(tx_status) {
    448 		uint tx_completed=inb_p(iobase + INTERRUPTSTATUS) & 
    449 			(ISR_PTX | ISR_TXE);
    450 		if(!tx_completed) resend=1;
    451 	}
    452 
    453 	outb_p(TCR_INTERNAL_LOOPBACK, iobase + TRANSMITCONFIGURATION);
    454 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    455 	nic_rx(nic);	/* cleanup RX ring */
    456 	outb_p(ISR_OVW, iobase + INTERRUPTSTATUS);	/* ACK INT */
    457 
    458 	outb_p(TCR_DEFAULT, iobase + TRANSMITCONFIGURATION);
    459 	if(resend)
    460 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START | NIC_TRANSMIT,
    461 			iobase);
    462 }
    463 
    464 /* This is the procedure that markst the transmit buffer as available again */
    465 void nic_tx(snic *nic) {
    466 	uint f;			int iobase=nic->iobase;
    467 	uint status;
    468 /*	kprintf("nic_tx()\n"); */
    469 	if(!nic || !nic->iobase) return;
    470 	status=inb(iobase + TRANSMITSTATUS);
    471 
    472 	if(!nic->tx_packet[nic->send]) {
    473 		kprintf("NE2000: ERROR: Invalid transmison packet buffer.\n");
    474 		return;
    475 	}
    476 	if(!nic->sending) kprintf("NE2000: ERROR: Invalid nic->sending value.\n");
    477 
    478 	free_buffer(nic->tx_packet[nic->send]);
    479 	nic->tx_packet[nic->send]= NULL;
    480 /*	nic->tx_packet[nic->send].count=0;	 mark buffer as available */
    481 /*	nic->tx_packet[nic->send].len=0;
    482 	nic->tx_packet[nic->send].page=0; */
    483 
    484 	for(f=0;f<MAX_TX;f++) {
    485 		if(nic->tx_packet[f]) {
    486 			kprintf("NE2000: DEBUG: transmitting secondary buffer:%X\n",f);
    487 			nic->stat.tx_buffered++;
    488 			nic->send=f;	nic->sending=1;
    489 			nic_send(nic,f);	/* send a back-to-back buffer */
    490 			break;
    491 		}
    492 	}
    493 	if(f==MAX_TX) nic->sending=0;
    494 
    495 	if(status & TSR_COL) nic->stat.errors.tx_collisions++;
    496 	if(status & TSR_PTX) nic->stat.tx_packets++;
    497 	else {
    498 		if(status & TSR_ABT) {
    499 			nic->stat.errors.tx_aborts++;
    500 			nic->stat.errors.tx_collisions+=16; }
    501 		if(status & TSR_CRS) nic->stat.errors.tx_carrier++;
    502 		if(status & TSR_FU) nic->stat.errors.tx_fifo++;
    503 		if(status & TSR_CDH) nic->stat.errors.tx_heartbeat++;
    504 		if(status & TSR_OWC) nic->stat.errors.tx_window++;
    505 	}
    506 
    507 	outb_p(ISR_PTX, iobase + INTERRUPTSTATUS);	/* ack int */
    508 }
    509 
    510 void nic_tx_err(snic *nic) {
    511 	unsigned char tsr;	int iobase=nic->iobase;
    512 	if(!nic || !nic->iobase) return;
    513 	tsr=inb_p(nic->iobase);
    514 	kprintf("NE2000: ERROR: TX error: ");
    515 	if(tsr & TSR_ABT) kprintf("Too many collisions.\n");
    516 	if(tsr & TSR_ND) kprintf("Not deffered.\n");
    517 	if(tsr & TSR_CRS) kprintf("Carrier lost.\n");
    518 	if(tsr & TSR_FU) kprintf("FIFO underrun.\n");
    519 	if(tsr & TSR_CDH) kprintf("Heart attack!\n");
    520 
    521 	outb_p(ISR_TXE, iobase + INTERRUPTSTATUS);
    522 	if(tsr & (TSR_ABT | TSR_FU)) {
    523 		kprintf("NE2000: DEBUG: Attempting to retransmit packet.\n");
    524 		nic_tx(nic);
    525 	}
    526 }
    527 
    528 void nic_rx(snic *nic) {
    529 	uint packets=0;	uint frame;	uint rx_page;	uint rx_offset;
    530 	uint len;	uint next_pkt;	uint numpages;
    531 	int iobase=nic->iobase;
    532 	buffer_header header;
    533 	if(!nic || !nic->iobase) return;
    534 	while(packets<MAX_RX) {
    535 		outb_p(NIC_DMA_DISABLE | NIC_PAGE1, iobase); /*curr is on page 1 */
    536 		rx_page=inb_p(iobase + CURRENT);	/* get current page */
    537 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0, iobase);
    538 		frame=inb(iobase + BOUNDARY)+1;
    539 			/* we add one becuase boundary is a page behind
    540 			// as pre NS notes to help in overflow problems */
    541 		if(frame>=nic->pstop) frame=nic->pstart+TXPAGES;
    542 							/* circual buffer */
    543 
    544 		if(frame != nic->current_page) {
    545 			kprintf("NE2000: ERROR: mismatched read page pointers!\n");
    546 			kprintf("NE2000: NIC-Boundary:%x  dev-current_page:%x\n",
    547 				frame, nic->current_page); }
    548 
    549 /*		kprintf("Boundary-%x  Current-%x\n",frame-1,rx_page); */
    550 		if(frame==rx_page) break;	/* all frames read */
    551 
    552 		rx_offset=frame << 8;  /* current ptr in bytes(not pages) */
    553 
    554 		nic_get_header(nic,frame,&header);
    555 		len=header.count - sizeof(buffer_header);
    556 							/* length of packet */
    557 		next_pkt=frame + 1 + ((len+4)>>8); /* next packet frame */
    558 
    559 		numpages=nic->pstop-(nic->pstart+TXPAGES);
    560 		if(	   (header.next!=next_pkt)
    561 			&& (header.next!=next_pkt + 1)
    562 			&& (header.next!=next_pkt - numpages)
    563 			&& (header.next != next_pkt +1 - numpages)){
    564 				kprintf("NE2000: ERROR: Index mismatch.   header.next:%X  next_pkt:%X frame:%X\n",
    565 					header.next,next_pkt,frame);
    566 				nic->current_page=frame;
    567 				outb(nic->current_page-1, iobase + BOUNDARY);
    568 				nic->stat.errors.rx++;
    569 				continue;
    570 		}
    571 
    572 		if(len<60 || len>1518) {
    573 			kprintf("NE2000: invalid packet size:%d\n",len);
    574 			nic->stat.errors.rx_size++;
    575 		} else if((header.status & 0x0f) == RSR_PRX) {
    576 			/* We have a good packet, so let's recieve it! */
    577 
    578 			packet_data *newpacket=alloc_buffer_data(len);
    579 			if(!newpacket) {
    580 				kprintf("NE2000: ERROR: out of memory!\n");
    581 				nic->stat.errors.rx_dropped++;
    582                                 nic_block_input(nic,NULL,len,rx_offset+
    583                                                 sizeof(buffer_header));
    584 			} else {
    585                             nic_block_input(nic,newpacket->ptr,newpacket->len,
    586                                             rx_offset+sizeof(buffer_header));
    587 								/* read it */
    588                             
    589                             if(nic->notify) nic->notify(nic->kore,newpacket);
    590                             else free_buffer_data(newpacket);
    591                                 /* NOTE: you are responsible for deleting this buffer. */
    592 
    593                             nic->stat.rx_packets++;
    594                         }
    595 		} else {
    596 			kprintf("NE2000: ERROR: bad packet.  header-> status:%X next:%X len:%x.\n",
    597 				header.status,header.next,header.count);
    598 			if(header.status & RSR_FO) nic->stat.errors.rx_fifo++;
    599 		}
    600 /*		kprintf("frame:%x  header.next:%x  next_pkt:%x\n",
    601 			frame,header.next,next_pkt); */
    602 		next_pkt=header.next;
    603 
    604 		if(next_pkt >= nic->pstop) {
    605 			kprintf("NE2000: ERROR: next frame beyond local buffer!  next:%x.\n",
    606 				next_pkt);
    607 			next_pkt=nic->pstart+TXPAGES;
    608 		}
    609 
    610 		nic->current_page=next_pkt;
    611 		outb_p(next_pkt-1, iobase + BOUNDARY);
    612 	}
    613 	outb_p(ISR_PRX | ISR_RXE, iobase + INTERRUPTSTATUS);	/* ack int */
    614 }
    615 
    616 void nic_counters(snic *nic) {
    617 	nic->stat.errors.frame_alignment+=inb_p(nic->iobase+FAE_TALLY);
    618 	nic->stat.errors.crc+=inb_p(nic->iobase+CRC_TALLY);
    619 	nic->stat.errors.missed_packets+=inb_p(nic->iobase+MISS_PKT_TALLY);
    620 	/* reading the counters on the DC8390 clears them. */
    621 	outb_p(ISR_CNT, nic->iobase + INTERRUPTSTATUS); /* ackkowledge int */
    622 }
    623 
    624 /* You should be able to make this procedure a wrapper of nic_block_input */
    625 void nic_get_header(snic *nic, uint page, buffer_header *header) {
    626 	int iobase=nic->iobase;	uint f;
    627 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    628 	outb_p(sizeof(buffer_header), iobase + REMOTEBYTECOUNT0);
    629 	outb_p(0, iobase + REMOTEBYTECOUNT1);		/* read the header */
    630 	outb_p(0, iobase + REMOTESTARTADDRESS0); 	/* page boundary */
    631 	outb_p(page, iobase + REMOTESTARTADDRESS1); 	/* from this page */
    632 	outb_p(NIC_REM_READ | NIC_START, iobase);	/* start reading */
    633 
    634 	if(nic->wordlength==2) for(f=0;f<(sizeof(buffer_header)>>1);f++)
    635 		((unsigned short *)header)[f]=inw(iobase+NE_DATA);
    636 	else for(f=0;f<sizeof(buffer_header);f++)
    637 		((unsigned char *)header)[f]=inb(iobase+NE_DATA);
    638 					/* Do these need to be *_p variants??? */
    639 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    640 }
    641 
    642 int nic_send(snic *nic, uint buf) {
    643 	uint len= (nic->tx_packet[buf]->len<MIN_LENGTH) ?
    644 		MIN_LENGTH : nic->tx_packet[buf]->len;
    645 /*	kprintf("nic_send()\n"); */
    646 	if(!nic->tx_packet[buf]) return ERR; /* this is bad */
    647 	outb_p(NIC_DMA_DISABLE |  NIC_PAGE0, nic->iobase);
    648         if(inb_p(nic->iobase + STATUS) & NIC_TRANSMIT) {
    649 		kprintf("NE2000: ERROR: Transmitor busy.\n");
    650 		free_buffer(nic->tx_packet[buf]);
    651 		nic->tx_packet[buf]= NULL;
    652 /*                nic->tx_packet[buf].count=0;  mark as free again */
    653                 return ERRTIMEOUT; }
    654         outb_p(len & 0xff,nic->iobase+TRANSMITBYTECOUNT0);
    655         outb_p(len >> 8,nic->iobase+TRANSMITBYTECOUNT1);
    656         outb_p(nic->tx_packet[buf]->page,nic->iobase+TRANSMITPAGE);
    657         outb_p(NIC_DMA_DISABLE | NIC_TRANSMIT | NIC_START,nic->iobase);
    658 	return NOERR;
    659 }
    660 
    661 void nic_block_input(snic *nic, unsigned char *buf, uint len, uint offset) {
    662 	int iobase=nic->iobase;	uint f;
    663 	uint xfers=len;
    664 	uint timeout=TIMEOUT_DMAMATCH;	uint addr;
    665 /*	kprintf("NE2000: RX: Length:%x  Offset:%x  ",len,offset); */
    666 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    667 	outb_p(len & 0xff, iobase + REMOTEBYTECOUNT0);
    668 	outb_p(len >> 8, iobase + REMOTEBYTECOUNT1);
    669 	outb_p(offset & 0xff, iobase + REMOTESTARTADDRESS0);
    670 	outb_p(offset >> 8, iobase + REMOTESTARTADDRESS1);
    671 	outb_p(NIC_REM_READ | NIC_START, iobase);
    672 
    673             /* allow for no buffer */
    674         if(buf){
    675             if(nic->wordlength==2) {
    676 		for(f=0;f<(len>>1);f++)
    677                     ((unsigned short *)buf)[f]=inw(iobase+NE_DATA);
    678 		if(len&0x01) {
    679                     ((unsigned char *)buf)[len-1]=inb(iobase+NE_DATA);
    680                     xfers++;
    681 		}
    682             } else {
    683                 for(f=0;f<len;f++)
    684                     ((unsigned char *)buf)[f]=inb(iobase+NE_DATA);
    685             }
    686         } else {
    687             if(nic->wordlength==2) {
    688 		for(f=0;f<(len>>1);f++)
    689                     inw(iobase+NE_DATA);
    690 		if(len&0x01) {
    691                     inb(iobase+NE_DATA);
    692                     xfers++;
    693 		}
    694             } else {
    695                 for(f=0;f<len;f++)
    696                     inb(iobase+NE_DATA);
    697             }
    698         }
    699 					/* Do these need to be *_p variants??? */
    700 
    701 /*	for(f=0;f<15;f++) kprintf("%X",buf[f]); kprintf("\n"); */
    702 	/* TODO: make this timeout a constant */
    703 	for(f=0;f<timeout;f++) {
    704 		uint high=inb_p(iobase + REMOTESTARTADDRESS1);
    705 		uint low=inb_p(iobase + REMOTESTARTADDRESS0);
    706 		addr=(high<<8)+low;
    707 		if(((offset+xfers)&0xff)==low)	break;
    708 	}
    709             /*
    710 	if(f>=timeout)
    711 		kprintf("NE2000: Remote DMA address invalid.  expected:%x - actual:%x\n",
    712 			offset+xfers, addr); HUH WHAT? BJS*/
    713 	
    714 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    715 }
    716 
    717 /* Now supports scatter gather */
    718 void nic_block_output(snic *nic, packet_buffer *pkt) {
    719 	int iobase=nic->iobase;
    720 	int timeout=TIMEOUT_DMAMATCH;	int f;	uint addr;	int tmplen;
    721 	int bufidx, buflen; /* current packet_data in packet_buffer */
    722 	void *bufptr;
    723 	char skip=0; /* Ask me about this if you have questions */
    724 	uint deleteme=0;	/* delete when done debugging */
    725 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    726 
    727 	T(A);
    728         
    729 	for(f=0;f<pkt->count;f++) deleteme+=pkt->buf[f].len;
    730         T(B);
    731         
    732 	if(pkt->len != deleteme) return;
    733 
    734 	if(pkt->len > MAX_LENGTH) return;
    735 		/* we should not have gotten this far...  Paranoia check */
    736 
    737 	/* this next part is to supposedly fix a "read-before-write" bug... */
    738 	outb_p(0x42, iobase + REMOTEBYTECOUNT0);
    739 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);
    740 	outb_p(0x42, iobase + REMOTESTARTADDRESS0);
    741 	outb_p(0x00, iobase + REMOTESTARTADDRESS1);
    742 	outb_p(NIC_REM_READ | NIC_START, iobase);
    743 	SLOW_DOWN_IO;	SLOW_DOWN_IO;	SLOW_DOWN_IO;
    744 
    745 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* ack remote DMA int */
    746         T(C);
    747         
    748 	tmplen=(pkt->len < MIN_LENGTH) ? MIN_LENGTH : pkt->len;
    749 	outb_p(tmplen & 0xff, iobase + REMOTEBYTECOUNT0);
    750 	outb_p(tmplen >> 8, iobase + REMOTEBYTECOUNT1);
    751 	outb_p(0x00, iobase + REMOTESTARTADDRESS0);
    752 	outb_p(pkt->page, iobase + REMOTESTARTADDRESS1);
    753 	outb_p(NIC_REM_WRITE | NIC_START, iobase);
    754 
    755         T(D);
    756         
    757 	/* go through each buffer and transfer it's contents */
    758 	for(bufidx=pkt->count-1; bufidx >= 0; bufidx--) {
    759 	  char odd;
    760 	  buflen=pkt->buf[bufidx].len; /* only do derefrence once */
    761 	  bufptr=pkt->buf[bufidx].ptr; /* for performance :) */
    762 
    763 	  if(nic->wordlength==2) {
    764 
    765 	    odd=(buflen & 0x01);
    766 	    buflen>>=1;		/* half the transfers */
    767 
    768 	    if(skip) ((uint)bufptr)--;   /* korewa baka desu */
    769 
    770 	    for(f=skip;f<buflen;f++) { /* do we skip? */
    771 	      outw(((unsigned short *)bufptr)[f],iobase+NE_DATA);
    772 	      tmplen -= 2;
    773 	    }
    774 
    775 	    /* output 16-bits of the last odd byte */
    776 	    skip=0;		/* assume we don't skip */
    777 
    778 	    if(odd) {
    779 	      short tmp=((unsigned char *)bufptr)[buflen<<1];
    780 				/* get the byte from the next segment */
    781 	      if((bufidx-1)>=0) {
    782 		tmp |= ((unsigned char *) pkt->buf[bufidx-1].ptr)[0]  << 8;
    783 	      }
    784 	      outw(tmp,iobase + NE_DATA);
    785 	      tmplen -= 2;
    786 	      skip=1;
    787 	    }
    788 	  } else 
    789 	    for(f=0;f<buflen;f++) {
    790 	      outb(((unsigned char *)bufptr)[f],iobase+NE_DATA);
    791 	      tmplen--;
    792 	    }
    793 	}
    794         T(E);
    795         
    796 	/* If it's too short, pad with 0s */
    797 	if(tmplen > 0) {
    798             T(F);
    799 /*	kprintf("Padding runt\n"); */
    800 		if(nic->wordlength==2) {
    801                     for(f=0;f<tmplen/2;f++){
    802 		      outw(0x00,iobase + NE_DATA);
    803 		      tmplen -= 2;
    804 		    }
    805 		}
    806 	}
    807 	if (tmplen > 0) {
    808 	  for(f=0;f<tmplen;f++) {
    809 	    outb(0x00,iobase + NE_DATA);
    810 	    tmplen--;
    811 	  }
    812 	}
    813 
    814 
    815         T(G);
    816 #ifdef SHIT       
    817 	if(nic->wordlength==2) {
    818 		for(w=0;w<(len>>1);w++)
    819 			outw(((unsigned short *)buf)[w],iobase+NE_DATA);
    820 		if(len & 0x01) {
    821 			short tmp=buf[len-1]; //so we can output a whole 16-bits
    822 			// if buf were on the end of something, we would die.
    823 			outw(tmp,iobase+NE_DATA);
    824 		}
    825 	} else for(f=0;f<len;f++)
    826 		outb(((unsigned char *)buf)[f],iobase+NE_DATA);
    827 
    828 #endif
    829 	for(f=0;f<timeout;f++) {
    830 		uint high=inb_p(iobase + REMOTESTARTADDRESS1);
    831 		uint low=inb_p(iobase + REMOTESTARTADDRESS0);
    832 		addr=(high<<8)+low;
    833 		if(( (((pkt->page<<8)+(nic->wordlength==2 && (pkt->len&0x01))?
    834 /*				pkt->len+1:pkt->len+2))&0xff)==low)*/
    835 				pkt->len+0:pkt->len+1))&0xff)==low)
    836 			break;
    837 		if( (pkt->len < MIN_LENGTH) && ( (((pkt->page<<8)+MIN_LENGTH)
    838 				& 0xff) == low) )
    839 			break;
    840 	}
    841         T(H);
    842 #ifdef SHIT        
    843 	if(f>=timeout)
    844 		kprintf("NE2000: Remote DMA address invalid.  expected:%x - actual:%x\n",
    845 			( (pkt->len >= MIN_LENGTH) ?
    846 				((pkt->page<<8)+
    847 					(nic->wordlength==2&&(pkt->len&0x01))?
    848 					pkt->len+0:pkt->len+1)
    849 /*					pkt->len+1:pkt->len+2)*/
    850 				: ((pkt->page<<8)+MIN_LENGTH) ),
    851 			addr);
    852 #endif
    853 /* TODO Check ISR_RDC */
    854 
    855         T(I);
    856         
    857 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    858 }