openblt

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

ne2000.c (22729B)


      1 /* $Id: //depot/blt/netboot/ne2000.c#3 $
      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 the shared code base for the 1997 SigOps NE2000
     29 // network driver.  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 /* Change Log:
     36 10-25-97	Setup under CVS					drarmstr
     37 10-27-97	Added alloc_buffer() and free_buffer()		drarmstr
     38 */
     39 
     40 #ifndef NULL
     41 #define NULL	0
     42 #endif	/* NULL */
     43 #include "io.h"
     44 #include "ne2000.h"
     45 #include "ne2k.h"
     46 #include "err.h"
     47 
     48 extern void idle();
     49 extern long ticks;	/* replace with a better timeout method, like wait() */
     50 
     51 /* we may have to change inb and outb to inb_p and outb_p.
     52 // Note, none of this code is gauranteed to be reentrant.
     53 
     54 // Note, don't create to nics to the same card and then try to use both 
     55 // of them.  The results will be unpredictable.
     56 
     57 // This violates IO resource manegment if your OS handles that, but it 
     58 // works for now.  Make sure that the driver has privlige access to the 
     59 // mapped I/O space and the IRQ.*/
     60 static unsigned int default_ports[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0 };
     61 
     62 /* internal procedure, don't call this directly.*/
     63 int nic_probe(int addr) {
     64 	uint regd;
     65 	uint state=inb(addr);		/* save command state */
     66 
     67 	if(inb(addr==0xff)) return ERRNOTFOUND;
     68 
     69 	outb(NIC_DMA_DISABLE | NIC_PAGE1 | NIC_STOP, addr);
     70 	regd=inb(addr + 0x0d);
     71 	outb(0xff, addr + 0x0d);
     72 	outb(NIC_DMA_DISABLE | NIC_PAGE0, addr);
     73 	inb(addr + FAE_TALLY);	/* reading CNTR0 resets it.*/
     74 	if(inb(addr + FAE_TALLY)) {	/* counter didn't clear so probe fails*/
     75 		outb(state,addr);	/* restore command state*/
     76 		outb(regd,addr + 0x0d);
     77 		return ERRNOTFOUND; }
     78 
     79 	return addr;	/* network card detected at io addr;*/
     80 }
     81 
     82 /* Detects for presence of NE2000 card.  Will check given io addr that 
     83 // is passed to it as well as the default_ports array.  Returns ERRNOTFOUND 
     84 // if no card is found, i/o address otherwise.  This does conduct an ISA
     85 // probe, so it's not always a good idea to run it.  If you already have 
     86 // the information, then you can just use that with nic_init().*/
     87 int nic_detect(int given) {
     88 	int found;
     89 	if((found=nic_probe(given))!=ERRNOTFOUND) { return found; }
     90 	return ERRNOTFOUND;
     91 }
     92 
     93 /* This initializes the NE2000 card.  If it turns out the card is not
     94 // really a NE2000 after all then it will return ERRNOTFOUND, else NOERR
     95 // It also dumps the prom into buffer prom for the upper layers..
     96 // Pass it a nic with a null iobase and it will initialize the structure for
     97 // you, otherwise it will just reinitialize it. */
     98 int nic_init(snic* nic, int addr, unsigned char *prom, unsigned char *manual) {
     99 	uint f;
    100 	if(!nic->iobase) {
    101 		nic->iobase=addr;
    102 		nic_stat_clear(&nic->stat);
    103 		nic->pstart=0; nic->pstop=0; nic->wordlength=0; 
    104 		nic->current_page=0;
    105 		nic->notify=NULL;
    106 		for(f=0;f<MAX_TX;f++) nic->tx_packet[f].len=0;
    107 		nic->last_tx=NULL;
    108 		nic->busy=0;
    109 	} else {
    110 		if(!nic->iobase || nic->iobase!=addr) return ERR;
    111 	}	
    112 
    113 	outb(inb(addr + NE_RESET), addr + NE_RESET);	/* reset the NE2000*/
    114 	while(!(inb_p(addr+INTERRUPTSTATUS) & ISR_RST)) {
    115 		/* TODO insert timeout code here.*/
    116 	}
    117 
    118 	outb_p(0xff,addr + INTERRUPTSTATUS);	/* clear all pending ints*/
    119 
    120 	// Initialize registers
    121 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr);	/* enter page 0*/
    122 	outb_p(DCR_DEFAULT, addr + DATACONFIGURATION);
    123 	outb_p(0x00, addr + REMOTEBYTECOUNT0);
    124 	outb_p(0x00, addr + REMOTEBYTECOUNT1);
    125 	outb_p(0x00, addr + INTERRUPTMASK);	/* mask off all irq's*/
    126 	outb_p(0xff, addr + INTERRUPTSTATUS);	/* clear pending ints*/
    127 	outb_p(RCR_MON, RECEIVECONFIGURATION);	/* enter monitor mode*/
    128 	outb_p(TCR_INTERNAL_LOOPBACK, TRANSMITCONFIGURATION); /* internal loopback*/
    129 	
    130 	nic->wordlength=nic_dump_prom(nic,prom);
    131 	if(prom[14]!=0x57 || prom[15]!=0x57) {
    132 		return ERRNOTFOUND;
    133 	}
    134 
    135 	/* if the wordlength for the NE2000 card is 2 bytes, then
    136 	// we have to setup the DP8390 chipset to be the same or 
    137 	// else all hell will break loose.*/
    138 	if(nic->wordlength==2) {
    139 		outb_p(DCR_DEFAULT_WORD, addr + DATACONFIGURATION);
    140 	}
    141 	nic->pstart=(nic->wordlength==2) ? PSTARTW : PSTART;
    142 	nic->pstop=(nic->wordlength==2) ? PSTOPW : PSTOP;
    143 
    144 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr);
    145 	outb_p(nic->pstart, addr + TRANSMITPAGE);	/* setup local buffer*/
    146 	outb_p(nic->pstart + TXPAGES, addr + PAGESTART);
    147 	outb_p(nic->pstop - 1, addr + BOUNDARY);
    148 	outb_p(nic->pstop, addr + PAGESTOP);
    149 	outb_p(0x00, addr + INTERRUPTMASK);	/* mask off all irq's*/
    150 	outb_p(0xff, addr + INTERRUPTSTATUS);	/* clear pending ints*/
    151 	nic->current_page=nic->pstart + TXPAGES;
    152 	
    153 	/* put physical address in the registers */
    154 	outb_p(NIC_DMA_DISABLE | NIC_PAGE1 | NIC_STOP, addr);  /* switch to page 1 */
    155 	if(manual) for(f=0;f<6;f++) outb_p(manual[f], addr + PHYSICAL + f);
    156 	else for(f=0;f<LEN_ADDR;f++) outb_p(prom[f], addr + PHYSICAL + f);
    157 
    158 	/* setup multicast filter to accept all packets*/
    159 	for(f=0;f<8;f++) outb_p(0xFF, addr + MULTICAST + f);
    160 
    161 	outb_p(nic->pstart+TXPAGES, addr + CURRENT);
    162 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, addr); /* switch to page 0 */
    163 
    164 	return NOERR;
    165 }
    166 
    167 /* This registers the function that will be called when a new packet 
    168 // comes in. */
    169 void nic_register_notify(snic *nic, void (*newnotify)(packet_buffer *newpacket)){
    170 	nic->notify=newnotify;
    171 }
    172 
    173 /* start the NIC so it can actually recieve or transmit packets */
    174 void nic_start(snic *nic, int promiscuous) {
    175 	int iobase;
    176 	if(!nic || !nic->iobase) {
    177 		return; }
    178 	iobase=nic->iobase;
    179 	outb(0xff, iobase + INTERRUPTSTATUS);
    180 	outb(IMR_DEFAULT, iobase + INTERRUPTMASK);
    181 	outb(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    182 	outb(TCR_DEFAULT, iobase + TRANSMITCONFIGURATION);
    183 	if(promiscuous)
    184 		outb(RCR_PRO | RCR_AM, iobase + RECEIVECONFIGURATION);
    185 	else
    186 		outb(RCR_DEFAULT, iobase + RECEIVECONFIGURATION);
    187 }
    188 
    189 /* stops the NIC */
    190 void nic_stop(snic *nic) {
    191 	unsigned char tmp_buffer[16];	
    192 	if(!nic || !nic->iobase) return;    /* make sure card was initialized */
    193 	nic_init(nic,nic->iobase,tmp_buffer,NULL);
    194 }
    195 
    196 void nic_isr(snic *nic) {
    197 	uint isr;	/* Illinois Sreet Residence Hall */
    198 	uint overload;
    199 	if(!nic || !nic->iobase) return;    /* make sure card was initialized */
    200 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0, nic->iobase);
    201 	overload=MAX_LOAD+1;
    202 	while((isr=inb_p(nic->iobase+INTERRUPTSTATUS))) {
    203 		if((--overload)<=0) break;
    204 		if(isr & ISR_OVW)			nic_overrun(nic);
    205 		else if(isr & (ISR_PRX | ISR_RXE))	nic_rx(nic);
    206 		if(isr & ISR_PTX)			nic_tx(nic);
    207 		else if(isr & ISR_TXE)			nic_tx_err(nic);
    208 //		if(isr & ISR_CNT)			nic_counters(nic);
    209 		if(isr & ISR_RDC) outb_p(ISR_RDC, nic->iobase+ INTERRUPTSTATUS);
    210 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, nic->iobase);
    211 	}
    212 	if(isr) {
    213 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, nic->iobase);
    214 		if(!overload) {
    215 				
    216 			outb_p(ISR_ALL, nic->iobase + INTERRUPTSTATUS); // clear
    217 		} else {
    218 			outb_p(0xff, nic->iobase + INTERRUPTSTATUS);
    219 								// Ack it anyway
    220 		}
    221 	}
    222 }
    223 
    224 /* You should call this before you just read the stats directlly from the
    225 // snic struct.  This procedure updates the counters */
    226 nic_stat nic_get_stats(snic *nic) { 
    227 	nic->stat.errors.frame_alignment+=inb_p(nic->iobase + FAE_TALLY);
    228 	nic->stat.errors.crc+=inb_p(nic->iobase + CRC_TALLY);
    229 	nic->stat.errors.missed_packets+=inb_p(nic->iobase + MISS_PKT_TALLY);
    230 	return nic->stat;
    231 }
    232 
    233 void nic_stat_clear(nic_stat *that) {
    234 	that->rx_packets=0;
    235 	that->tx_buffered=0;		that->tx_packets=0;
    236 	that->errors.frame_alignment=0;	that->errors.crc=0;
    237 	that->errors.missed_packets=0;	that->errors.rx=0;
    238 	that->errors.rx_size=0;		that->errors.rx_dropped=0;
    239 	that->errors.rx_fifo=0;		that->errors.rx_overruns=0;
    240 	that->errors.tx_collisions=0;
    241 }
    242 
    243 /* Since this could be called by more than one other device, it should
    244 // be properly protected for reentrancy.  We should put in a proper
    245 // semaphore or something, look into this.
    246 // NOTE: It is your responsibility to clean up the packet_buffer when you 
    247 // are done calling this.  Also note that the buffer.ptr may change if
    248 // you gave us a packet that was too small and we had to pad the data. 
    249 // UPDATE: we now never change buffer.ptr, but create a whole new 
    250 // packet_buffer instead.  We take care of deleting this, so you don't
    251 // have to worry about it.
    252 // This is done so we can avoid using dynamic memory allocation ourselves. */
    253 int nic_send_packet(snic *nic, packet_buffer* buffer) {
    254 	uint timeout;	uint f;	int iobase=nic->iobase;
    255 	packet_buffer *pad=NULL;
    256 	if(!buffer->len || !buffer->ptr) return ERR;	
    257 	if(!nic || !nic->iobase) return ERR;
    258 	if(buffer->len>MAX_LENGTH) return ERRFORMAT;
    259 	/* the following wait for anyother tasks that are calling 
    260 	// nic_send_packet() right now.  Note that this doesn't use
    261 	// an atomic semaphore, so something MAY leak through. */
    262 	timeout=ticks+10;	// wait 10 ticks
    263 	while(nic->busy && ticks<=timeout) idle();
    264 	/* Replace this with a proper timeout thing that doesn't use the
    265 	// ticks method which will be diffrent on each OS. */
    266 	if(nic->busy) {
    267 		return ERRTIMEOUT;
    268 	}
    269 	nic->busy=1;	/* mark as busy, replace with semaphore */
    270 
    271 /* XXX should not have to copy the data --- modify nic_block_output to
    272    optionally add padding 0's */
    273 	if(buffer->len<MIN_LENGTH) {
    274 /*		unsigned char *newbuffer=(unsigned char*)kalloc(MIN_LENGTH);	
    275 		if(!newbuffer) return ERRNOMEM; */
    276 		pad=alloc_buffer(MIN_LENGTH);
    277 		if(!pad) {
    278 			nic->busy=0;	/* V() */
    279 			return ERRNOMEM; }
    280 		for(f=0;f<buffer->len;f++) pad->ptr[f]=buffer->ptr[f];
    281 		for(;f<MIN_LENGTH;f++) pad->ptr[f]='\0';
    282 		/* pad the data to the minimum size. */
    283 		buffer=pad;
    284 	}
    285 
    286 	outb_p(0x00, iobase + INTERRUPTMASK);	/* mask ints for now */
    287 	for(f=0;f<MAX_TX;f++) {
    288 		if(nic->tx_packet[f].len==0) {
    289 			nic->tx_packet[f]=*buffer;
    290 			nic->tx_packet[f].page=nic->pstart + (f * MAX_PAGESPERPACKET);
    291 								/*output page */
    292 
    293 			nic_block_output(nic,nic->tx_packet[f].ptr,
    294 				nic->tx_packet[f].len,
    295 				nic->tx_packet[f].page );
    296 
    297 			nic->send=f;
    298 			/* now let's actually trigger the transmitter to send */
    299 			if(nic_send(nic,f)<0) break;
    300 			/* note, the nic_tx() interrupt will mark this
    301 			// tx_packet buffer as free again once it
    302 			// confirms that the packet was sent. */
    303 
    304 			nic->stat.tx_buffered++;
    305 			outb_p(IMR_DEFAULT, iobase + INTERRUPTMASK); /* unmask */
    306 			nic->busy=0;	/* V() */
    307 			if(pad) free_buffer(pad);
    308 			return NOERR;
    309 		}
    310 	}
    311 
    312 	outb_p(IMR_DEFAULT, iobase + INTERRUPTMASK);	/* unmask */
    313 	nic->busy=0;	/* V() */
    314 	if(pad) free_buffer(pad);
    315 	return ERR;
    316 	/* since we passed with nic->busy not busy then we should
    317 	// always have at least one buffer free. */
    318 }
    319 
    320 /* dumps the prom into a 16 byte buffer and returns the wordlength of
    321 // the card.
    322 // You should be able to make this procedure a wrapper of nic_block_input(). */
    323 int nic_dump_prom(snic *nic, unsigned char *prom) {
    324 	uint f;
    325 	int iobase=nic->iobase;
    326 	char wordlength=2;		/* default wordlength of 2 */
    327 	unsigned char dump[32];
    328 	outb_p(32, iobase + REMOTEBYTECOUNT0);	  /* read 32 bytes from DMA->IO */
    329 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);  /*  this is for the PROM dump */
    330 	outb_p(0x00, iobase + REMOTESTARTADDRESS0); /* configure DMA for 0x0000 */
    331 	outb_p(0x00, iobase + REMOTESTARTADDRESS1);
    332 	outb_p(NIC_REM_READ | NIC_START, iobase);
    333 	for(f=0;f<32;f+=2) {
    334 		dump[f]=inb_p(iobase + NE_DATA);
    335 		dump[f+1]=inb_p(iobase + NE_DATA);
    336 		if(dump[f]!=dump[f+1]) wordlength=1;
    337 	}
    338 	/* if wordlength is 2 bytes, then collapse prom to 16 bytes */
    339 	for(f=0;f<LEN_PROM;f++) prom[f]=dump[f+((wordlength==2)?f:0)];
    340 
    341 	return wordlength;
    342 }
    343 
    344 void nic_overrun(snic *nic) {
    345 	uint tx_status;	int iobase=nic->iobase;
    346 	long starttime;	uint resend=0;
    347 	if(!nic || !nic->iobase) return;
    348 	tx_status=inb_p(iobase) & NIC_TRANSMIT;
    349 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_STOP, iobase);
    350 	nic->stat.errors.rx_overruns++;
    351 
    352 	starttime=ticks;
    353 /*kprintf("BEFORE\n");*/
    354 	while(ticks-starttime<=10/*ticks to wait*/) idle();
    355 	/* Arrgh!  TODO: Replace this whole crappy code with a decent
    356 	// wait method.  We need to wait at least 1.6ms as per National
    357 	// Semiconductor datasheets, but we should probablly wait a 
    358 	// little more to be safe.
    359 //kprintf("AFTER\n"); */
    360 
    361 	outb_p(0x00, iobase + REMOTEBYTECOUNT0);
    362 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);
    363 	if(tx_status) {
    364 		uint tx_completed=inb_p(iobase + INTERRUPTSTATUS) & 
    365 			(ISR_PTX | ISR_TXE);
    366 		if(!tx_completed) resend=1;
    367 	}
    368 
    369 	outb_p(TCR_INTERNAL_LOOPBACK, iobase + TRANSMITCONFIGURATION);
    370 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    371 	nic_rx(nic);	/* cleanup RX ring */
    372 	outb_p(ISR_OVW, iobase + INTERRUPTSTATUS);	/* ACK INT */
    373 
    374 	outb_p(TCR_DEFAULT, iobase + TRANSMITCONFIGURATION);
    375 	if(resend)
    376 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START | NIC_TRANSMIT,
    377 			iobase);
    378 }
    379 
    380 /* This is the procedure that markst the transmit buffer as available again */
    381 void nic_tx(snic *nic) {
    382 	uint f;			int iobase=nic->iobase;
    383 	uint status;
    384 	if(!nic || !nic->iobase) return;
    385 	status=inb(iobase + TRANSMITSTATUS);
    386 
    387 	if(!nic->tx_packet[nic->send].len) {
    388 		return;
    389 	}
    390 	nic->tx_packet[nic->send].len=0;	/* mark buffer as available */
    391 
    392 	for(f=0;f<MAX_TX;f++) {
    393 		if(nic->tx_packet[f].len) {
    394 			nic->stat.tx_buffered++;
    395 			nic_send(nic,f);	/* send a back-to-back buffer */
    396 			break;
    397 		}
    398 	}
    399 
    400 	if(status & TSR_COL) nic->stat.errors.tx_collisions++;
    401 	if(status & TSR_PTX) nic->stat.tx_packets++;
    402 	else {
    403 		if(status & TSR_ABT) {
    404 			nic->stat.errors.tx_aborts++;
    405 			nic->stat.errors.tx_collisions+=16; }
    406 		if(status & TSR_CRS) nic->stat.errors.tx_carrier++;
    407 		if(status & TSR_FU) nic->stat.errors.tx_fifo++;
    408 		if(status & TSR_CDH) nic->stat.errors.tx_heartbeat++;
    409 		if(status & TSR_OWC) nic->stat.errors.tx_window++;
    410 	}
    411 
    412 	outb_p(ISR_PTX, iobase + INTERRUPTSTATUS);	/* ack int */
    413 }
    414 
    415 void nic_tx_err(snic *nic) {
    416 	unsigned char tsr;	int iobase=nic->iobase;
    417 	if(!nic || !nic->iobase) return;
    418 	tsr=inb_p(nic->iobase);
    419 /*	kprintf("NE2000: ERROR: TX error: ");
    420 	if(tsr & TSR_ABT) kprintf("Too many collisions.\n");
    421 	if(tsr & TSR_ND) kprintf("Not deffered.\n");
    422 	if(tsr & TSR_CRS) kprintf("Carrier lost.\n");
    423 	if(tsr & TSR_FU) kprintf("FIFO underrun.\n");
    424 	if(tsr & TSR_CDH) kprintf("Heart attack!\n");
    425         */
    426 	outb_p(ISR_TXE, iobase + INTERRUPTSTATUS);
    427 	if(tsr & (TSR_ABT | TSR_FU)) {
    428 		nic_tx(nic);
    429 	}
    430 }
    431 
    432 void nic_rx(snic *nic) {
    433 	uint packets=0;	uint frame;	uint rx_page;	uint rx_offset;
    434 	uint len;	uint next_pkt;	uint numpages;
    435 	int iobase=nic->iobase;
    436 	buffer_header header;
    437 	if(!nic || !nic->iobase) return;
    438 	while(packets<MAX_RX) {
    439 		outb_p(NIC_DMA_DISABLE | NIC_PAGE1, iobase); /*curr is on page 1 */
    440 		rx_page=inb_p(iobase + CURRENT);	/* get current page */
    441 		outb_p(NIC_DMA_DISABLE | NIC_PAGE0, iobase);
    442 		frame=inb(iobase + BOUNDARY)+1;
    443 			/* we add one becuase boundary is a page behind
    444 			// as pre NS notes to help in overflow problems */
    445 		if(frame>=nic->pstop) frame=nic->pstart+TXPAGES;
    446 							/* circual buffer */
    447 
    448 		if(frame==rx_page) break;	/* all frames read */
    449 
    450 		rx_offset=frame << 8;  /* current ptr in bytes(not pages) */
    451 
    452 		nic_get_header(nic,frame,&header);
    453 		len=header.count - sizeof(buffer_header);
    454 							/* length of packet */
    455 		next_pkt=frame + 1 + ((len+4)>>8); /* next packet frame */
    456 
    457 		numpages=nic->pstop-(nic->pstart+TXPAGES);
    458 		if(	   (header.next!=next_pkt)
    459 			&& (header.next!=next_pkt + 1)
    460 			&& (header.next!=next_pkt - numpages)
    461 			&& (header.next != next_pkt +1 - numpages)){
    462 /*				kprintf("NE2000: ERROR: Index mismatch.   header.next:%X  next_pkt:%X frame:%X\n",
    463 					header.next,next_pkt,frame);*/
    464 				nic->current_page=frame;
    465 				outb(nic->current_page-1, iobase + BOUNDARY);
    466 				nic->stat.errors.rx++;
    467 				continue;
    468 		}
    469 
    470 		if(len<60 || len>1518) {
    471 /*			kprintf("NE2000: invalid packet size:%d\n",len);*/
    472 			nic->stat.errors.rx_size++;
    473 		} else if((header.status & 0x0f) == RSR_PRX) {
    474 			/* We have a good packet, so let's recieve it! */
    475 
    476 			packet_buffer *newpacket=alloc_buffer(len);
    477 			if(!newpacket) {
    478 /*				kprintf("NE2000: ERROR: out of memory!\n");*/
    479 				nic->stat.errors.rx_dropped++;
    480 				break;
    481 			}
    482 
    483 			nic_block_input(nic,newpacket->ptr,newpacket->len,
    484 					rx_offset+sizeof(buffer_header));
    485 								/* read it */
    486 
    487 			if(nic->notify) nic->notify(newpacket);
    488 			/* NOTE: you are responsible for deleting this buffer. */
    489 
    490 			nic->stat.rx_packets++;
    491 
    492 		} else {
    493 /*			kprintf("NE2000: ERROR: bad packet.  header-> status:%X next:%X len:%x.\n",
    494 				header.status,header.next,header.count); */
    495 			if(header.status & RSR_FO) nic->stat.errors.rx_fifo++;
    496 		}
    497 /*		kprintf("frame:%x  header.next:%x  next_pkt:%x\n",
    498 //			frame,header.next,next_pkt); */
    499 		next_pkt=header.next;
    500 
    501 		if(next_pkt >= nic->pstop) {
    502 /*			kprintf("NE2000: ERROR: next frame beyond local buffer!  next:%x.\n",
    503 				next_pkt);*/
    504 			next_pkt=nic->pstart+TXPAGES;
    505 		}
    506 
    507 		nic->current_page=next_pkt;
    508 		outb_p(next_pkt-1, iobase + BOUNDARY);
    509 	}
    510 	outb_p(ISR_PRX | ISR_RXE, iobase + INTERRUPTSTATUS);	/* ack int */
    511 }
    512 
    513 /* You should be able to make this procedure a wrapper of nic_block_input */
    514 void nic_get_header(snic *nic, uint page, buffer_header *header) {
    515 	int iobase=nic->iobase;	uint f;
    516 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    517 	outb_p(sizeof(buffer_header), iobase + REMOTEBYTECOUNT0);
    518 	outb_p(0, iobase + REMOTEBYTECOUNT1);		/* read the header */
    519 	outb_p(0, iobase + REMOTESTARTADDRESS0); 	/* page boundary */
    520 	outb_p(page, iobase + REMOTESTARTADDRESS1); 	/* from this page */
    521 	outb_p(NIC_REM_READ | NIC_START, iobase);	/* start reading */
    522 
    523 	if(nic->wordlength==2) for(f=0;f<(sizeof(buffer_header)>>1);f++)
    524 		((unsigned short *)header)[f]=inw(iobase+NE_DATA);
    525 	else for(f=0;f<sizeof(buffer_header);f++)
    526 		((unsigned char *)header)[f]=inb(iobase+NE_DATA);
    527 					/* Do these need to be *_p variants??? */
    528 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    529 }
    530 
    531 int nic_send(snic *nic, uint buf) {
    532 	outb_p(NIC_DMA_DISABLE |  NIC_PAGE0, nic->iobase);
    533         if(inb_p(nic->iobase + STATUS) & NIC_TRANSMIT) {
    534 /*		kprintf("NE2000: ERROR: Transmitor busy.\n");*/
    535                 nic->tx_packet[buf].len=0; /* mark as free again */
    536                 return ERRTIMEOUT; }
    537         outb_p(nic->tx_packet[buf].len & 0xff,nic->iobase+TRANSMITBYTECOUNT0);
    538         outb_p(nic->tx_packet[buf].len >> 8,nic->iobase+TRANSMITBYTECOUNT1);
    539         outb_p(nic->tx_packet[buf].page,nic->iobase+TRANSMITPAGE);
    540         outb_p(NIC_DMA_DISABLE | NIC_TRANSMIT | NIC_START,nic->iobase);
    541 	return NOERR;
    542 }
    543 
    544 void nic_block_input(snic *nic, unsigned char *buf, uint len, uint offset) {
    545 	int iobase=nic->iobase;	uint f;
    546 	uint xfers=len;
    547 	uint timeout=TIMEOUT_DMAMATCH;	uint addr;
    548 /*	kprintf("NE2000: RX: Length:%x  Offset:%x  ",len,offset);*/
    549 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    550 	outb_p(len & 0xff, iobase + REMOTEBYTECOUNT0);
    551 	outb_p(len >> 8, iobase + REMOTEBYTECOUNT1);
    552 	outb_p(offset & 0xff, iobase + REMOTESTARTADDRESS0);
    553 	outb_p(offset >> 8, iobase + REMOTESTARTADDRESS1);
    554 	outb_p(NIC_REM_READ | NIC_START, iobase);
    555 
    556 	if(nic->wordlength==2) {
    557 		for(f=0;f<(len>>1);f++)
    558 			((unsigned short *)buf)[f]=inw(iobase+NE_DATA);
    559 		if(len&0x01) {
    560 			((unsigned char *)buf)[len-1]=inb(iobase+NE_DATA);
    561 			xfers++;
    562 		}
    563 	} else for(f=0;f<len;f++)
    564 		((unsigned char *)buf)[f]=inb(iobase+NE_DATA);
    565 					/* Do these need to be *_p variants??? */
    566 
    567 /*	for(f=0;f<15;f++) kprintf("%X",buf[f]); kprintf("\n");*/
    568 	/* TODO: make this timeout a constant */
    569 	for(f=0;f<timeout;f++) {
    570 		uint high=inb_p(iobase + REMOTESTARTADDRESS1);
    571 		uint low=inb_p(iobase + REMOTESTARTADDRESS0);
    572 		addr=(high<<8)+low;
    573 		if(((offset+xfers)&0xff)==low)	break;
    574 	}
    575 /*	if(f>=timeout)
    576 		kprintf("NE2000: Remote DMA address invalid.  expected:%x - actual:%x\n",
    577 			offset+xfers, addr);*/
    578 	
    579 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    580 }
    581 
    582 void nic_block_output(snic *nic, unsigned char *buf, uint len, uint page) {
    583 	int iobase=nic->iobase;
    584 	int timeout=TIMEOUT_DMAMATCH;	int f;	uint addr;	int w;
    585 	outb_p(NIC_DMA_DISABLE | NIC_PAGE0 | NIC_START, iobase);
    586 	
    587 	/* this next part is to supposedly fix a "read-before-write" bug... */
    588 	outb_p(0x42, iobase + REMOTEBYTECOUNT0);
    589 	outb_p(0x00, iobase + REMOTEBYTECOUNT1);
    590 	outb_p(0x42, iobase + REMOTESTARTADDRESS0);
    591 	outb_p(0x00, iobase + REMOTESTARTADDRESS1);
    592 	outb_p(NIC_REM_READ | NIC_START, iobase);
    593 	SLOW_DOWN_IO;	SLOW_DOWN_IO;	SLOW_DOWN_IO;
    594 
    595 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* ack remote DMA int */
    596 	
    597 	outb_p(len & 0xff, iobase + REMOTEBYTECOUNT0);
    598 	outb_p(len >> 8, iobase + REMOTEBYTECOUNT1);
    599 	outb_p(0x00, iobase + REMOTESTARTADDRESS0);
    600 	outb_p(page, iobase + REMOTESTARTADDRESS1);
    601 	outb_p(NIC_REM_WRITE | NIC_START, iobase);
    602 
    603 	if(nic->wordlength==2) {
    604 		for(w=0;w<(len>>1);w++)
    605 			outw(((unsigned short *)buf)[w],iobase+NE_DATA);
    606 		if(len & 0x01) {
    607 			short tmp=buf[len-1]; //so we can output a whole 16-bits
    608 			// if buf were on the end of something, we would die.
    609 			outw(tmp,iobase+NE_DATA);
    610 		}
    611 	} else for(f=0;f<len;f++)
    612 		outb(((unsigned char *)buf)[f],iobase+NE_DATA);
    613 
    614 	for(f=0;f<timeout;f++) {
    615 		uint high=inb_p(iobase + REMOTESTARTADDRESS1);
    616 		uint low=inb_p(iobase + REMOTESTARTADDRESS0);
    617 		addr=(high<<8)+low;
    618 		if(( (((page<<8)+(nic->wordlength==2 && (len&0x01))?len:len+1))
    619 				&0xff)==low)
    620 			break;
    621 	}
    622 /*	if(f>=timeout)
    623 		kprintf("NE2000: Remote DMA address invalid.  expected:%x - actual:%x\n",
    624 			page<<8, addr);*/
    625 	
    626 	outb_p(ISR_RDC, iobase + INTERRUPTSTATUS);	/* finish DMA cycle */
    627 }