Resent-Date: Sun, 31 Oct 1999 00:24:53 +0200 (MET DST)
Date: Sun, 31 Oct 1999 00:24:44 +0200
From: Peter De Schrijver <p2@ace.ulyssis.student.kuleuven.ac.be>
To: linux-m68k@lists.linux-m68k.org
Subject: linux/apollo
Resent-From: linux-m68k@phil.uni-sb.de


--huq684BweRXVnRxX
Content-Type: text/plain; charset=us-ascii

Hi,

After a long time the apollo patch is back ! :)

The attached patch updates 2.3.16 with support for the DN3500. My test machine
has a monochrome 1280x1024 board and a 3c505 ethernet card. There are non-
accelerated drivers for the 4 and 8 plane 1024x768 cards, but monochrome only.
No DMA support or ESDI/SCSI support yet. I am working on DMA. Keyboard and
mouse seem to work.

Peter.


--huq684BweRXVnRxX
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="linux.apollo.patch"

--- ../linux-2.3.16/./arch/m68k/apollo/Makefile	Sat Nov  1 19:01:21 1997
+++ ./arch/m68k/apollo/Makefile	Sat Oct 16 18:16:46 1999
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := apollo.o
-O_OBJS   := config.o dn_ints.o \
+O_OBJS   := config.o dn_ints.o dma.o \
 
 
 include $(TOPDIR)/Rules.make
--- ../linux-2.3.16/./arch/m68k/apollo/config.c	Thu May 13 17:04:23 1999
+++ ./arch/m68k/apollo/config.c	Sat Oct 30 20:36:11 1999
@@ -1,3 +1,4 @@
+#include <stdarg.h>
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -7,19 +8,26 @@
 #include <linux/console.h>
 
 #include <asm/setup.h>
+#include <asm/bootinfo.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/apollohw.h>
 #include <asm/irq.h>
 #include <asm/machdep.h>
 
+u_long sio01_physaddr;
+u_long sio23_physaddr;
+u_long rtc_physaddr;
+u_long pica_physaddr;
+u_long picb_physaddr;
+u_long cpuctrl_physaddr;
+u_long timer_physaddr;
+u_long apollo_model;
+
 extern void dn_sched_init(void (*handler)(int,void *,struct pt_regs *));
 extern int dn_keyb_init(void);
 extern int dn_dummy_kbdrate(struct kbd_repeat *);
 extern void dn_init_IRQ(void);
-#if 0
-extern void (*dn_default_handler[])(int,void *,struct pt_regs *);
-#endif
 extern int dn_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id);
 extern void dn_free_irq(unsigned int irq, void *dev_id);
 extern void dn_enable_irq(unsigned int);
@@ -37,20 +45,93 @@
 extern void (*kd_mksound)(unsigned int, unsigned int);
 extern void dn_dummy_video_setup(char *,int *);
 extern void dn_process_int(int irq, struct pt_regs *fp);
-
-static struct console dn_console_driver;
-static void dn_debug_init(void);
+#ifdef CONFIG_HEARTBEAT
+static void dn_heartbeat(int on);
+#endif
 static void dn_timer_int(int irq,void *, struct pt_regs *);
 static void (*sched_timer_handler)(int, void *, struct pt_regs *)=NULL;
+static void dn_get_model(char *model);
+static int dn_cpuctrl=0xff00;
+static const char *apollo_models[] = {
+	"DN3000 (Otter)",
+	"DN3010 (Otter)",
+	"DN3500 (Cougar II)",
+	"DN4000 (Mink)",
+	"DN4500 (Roadrunner)" };
+
+int apollo_parse_bootinfo(const struct bi_record *record) {
+
+	int unknown = 0;
+	const unsigned long *data = record->data;
+
+	switch(record->tag) {
+		case BI_APOLLO_MODEL: 
+			apollo_model=*data;	
+			break;
+
+		default:
+			 unknown=1;
+	}
+	
+	return unknown;
+}
+
+void dn_setup_model(void) {
+	
 
-int dn_serial_console_wait_key(void) {
+	printk("Apollo hardware found: ");
+	printk("[%s]\n", apollo_models[apollo_model - APOLLO_DN3000]);
+
+	switch(apollo_model) {
+		case APOLLO_UNKNOWN:
+			panic("Unknown apollo model");
+			break;
+		case APOLLO_DN3000:
+		case APOLLO_DN3010:
+			sio01_physaddr=SAU8_SIO01_PHYSADDR;	
+			rtc_physaddr=SAU8_RTC_PHYSADDR;	
+			pica_physaddr=SAU8_PICA;	
+			picb_physaddr=SAU8_PICB;	
+			cpuctrl_physaddr=SAU8_CPUCTRL;
+			timer_physaddr=SAU8_TIMER;
+			break;
+		case APOLLO_DN4000:
+			sio01_physaddr=SAU7_SIO01_PHYSADDR;	
+			sio23_physaddr=SAU7_SIO23_PHYSADDR;	
+			rtc_physaddr=SAU7_RTC_PHYSADDR;	
+			pica_physaddr=SAU7_PICA;	
+			picb_physaddr=SAU7_PICB;	
+			cpuctrl_physaddr=SAU7_CPUCTRL;
+			timer_physaddr=SAU7_TIMER;
+			break;
+		case APOLLO_DN4500:
+			panic("Apollo model not yet supported");
+			break;
+		case APOLLO_DN3500:
+			sio01_physaddr=SAU7_SIO01_PHYSADDR;	
+			sio23_physaddr=SAU7_SIO23_PHYSADDR;	
+			rtc_physaddr=SAU7_RTC_PHYSADDR;	
+			pica_physaddr=SAU7_PICA;	
+			picb_physaddr=SAU7_PICB;	
+			cpuctrl_physaddr=SAU7_CPUCTRL;
+			timer_physaddr=SAU7_TIMER;
+			break;
+		default:
+			panic("Undefined apollo model");
+			break;
+	}
+
+
+}
+
+int dn_serial_console_wait_key(struct console *co) {
 
 	while(!(sio01.srb_csrb & 1))
 		barrier();
 	return sio01.rhrb_thrb;
 }
 
-void dn_serial_console_write (const char *str,unsigned int count)
+void dn_serial_console_write (struct console *co, const char *str,unsigned int count)
 {
    while(count--) {
 	if (*str == '\n') { 
@@ -80,12 +161,9 @@
 
 void config_apollo(void) {
 
-	dn_serial_print("Config apollo !\n");
-#if 0
-	dn_debug_init();	
-#endif
-	printk("Config apollo !\n");
+	int i;
 
+	dn_setup_model();	
 
 	mach_sched_init=dn_sched_init; /* */
 	mach_keyb_init=dn_keyb_init;
@@ -109,13 +187,19 @@
 #endif
 	mach_reset	     = dn_dummy_reset;  /* */
 #ifdef CONFIG_DUMMY_CONSOLE
-	conswitchp	     = &dummy_con;
-#endif
-#if 0
-	mach_fb_init 	     = dn_fb_init; 
-	mach_video_setup     = dn_dummy_video_setup; 
+        conswitchp           = &dummy_con;
 #endif
 	kd_mksound	     = dn_mksound;
+#ifdef CONFIG_HEARTBEAT
+  	mach_heartbeat = dn_heartbeat;
+#endif
+	mach_get_model       = dn_get_model;
+
+	cpuctrl=0xaa00;
+
+	/* clear DMA translation table */
+	for(i=0;i<0x400;i++) 
+		addr_xlat_map[i]=0;
 
 }		
 
@@ -125,44 +209,30 @@
 
 	sched_timer_handler(irq,dev_id,fp);
 	
-	x=*(volatile unsigned char *)(IO_BASE+0x10803);
-	x=*(volatile unsigned char *)(IO_BASE+0x10805);
+	x=*(volatile unsigned char *)(timer+3);
+	x=*(volatile unsigned char *)(timer+5);
 
 }
 
 void dn_sched_init(void (*timer_routine)(int, void *, struct pt_regs *)) {
 
-	dn_serial_print("dn sched_init\n");
-
-#if 0
-	/* program timer 2 */
-	*(volatile unsigned char *)(IO_BASE+0x10803)=0x00;
-	*(volatile unsigned char *)(IO_BASE+0x10809)=0;
-	*(volatile unsigned char *)(IO_BASE+0x1080b)=50;
-
-	/* program timer 3 */
-	*(volatile unsigned char *)(IO_BASE+0x10801)=0x00;
-	*(volatile unsigned char *)(IO_BASE+0x1080c)=0;
-	*(volatile unsigned char *)(IO_BASE+0x1080f)=50;	
-#endif
 	/* program timer 1 */       	
-	*(volatile unsigned char *)(IO_BASE+0x10803)=0x01;
-	*(volatile unsigned char *)(IO_BASE+0x10801)=0x40;
-	*(volatile unsigned char *)(IO_BASE+0x10805)=0x09;
-	*(volatile unsigned char *)(IO_BASE+0x10807)=0xc4;
+	*(volatile unsigned char *)(timer+3)=0x01;
+	*(volatile unsigned char *)(timer+1)=0x40;
+	*(volatile unsigned char *)(timer+5)=0x09;
+	*(volatile unsigned char *)(timer+7)=0xc4;
 
 	/* enable IRQ of PIC B */
-	*(volatile unsigned char *)(IO_BASE+PICA+1)&=(~8);
-
-
+	*(volatile unsigned char *)(pica+1)&=(~8);
 
-	printk("*(0x10803) %02x\n",*(volatile unsigned char *)(IO_BASE+0x10803));
-	printk("*(0x10803) %02x\n",*(volatile unsigned char *)(IO_BASE+0x10803));
+#if 0
+	printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
+	printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
+#endif
 
 	sched_timer_handler=timer_routine;
 	request_irq(0,dn_timer_int,0,NULL,NULL);
 
-	
 }
 
 unsigned long dn_gettimeoffset(void) {
@@ -187,7 +257,6 @@
 
 int dn_dummy_hwclk(int op, struct hwclk_time *t) {
 
-  dn_serial_print("hwclk !\n");
 
   if(!op) { /* read */
     t->sec=rtc->second;
@@ -208,7 +277,6 @@
     rtc->year=t->year;
   }
 
-  dn_serial_print("hwclk end!\n");
   return 0;
 
 }
@@ -235,11 +303,25 @@
 
 }
 
-#if 0
-void dn_debug_init(void) {
-
-  dn_console_driver.write=dn_serial_console_write;
-  register_console(&dn_console_driver);
-
+static void dn_get_model(char *model)
+{
+    strcpy(model, "Apollo ");
+    if (apollo_model >= APOLLO_DN3000 && apollo_model <= APOLLO_DN4500)
+        strcat(model, apollo_models[apollo_model - APOLLO_DN3000]);
+}
+
+#ifdef CONFIG_HEARTBEAT
+static void dn_heartbeat(int on) {
+
+	if(on) { 
+		dn_cpuctrl&=~0x100;
+		cpuctrl=dn_cpuctrl;
+	}
+	else {
+		dn_cpuctrl&=~0x100;
+		dn_cpuctrl|=0x100;
+		cpuctrl=dn_cpuctrl;
+	}
 }
 #endif
+
--- ../linux-2.3.16/./arch/m68k/apollo/dn_debug.c	Thu May 13 17:04:23 1999
+++ ./arch/m68k/apollo/dn_debug.c	Fri Oct 15 04:53:18 1999
@@ -1,3 +1,4 @@
+#include <stdarg.h>
 
 #define DN_DEBUG_BUFFER_BASE 0x82800000
 #define DN_DEBUG_BUFFER_SIZE 8*1024*1024
--- ../linux-2.3.16/./arch/m68k/apollo/dn_ints.c	Thu Jan  7 16:29:38 1999
+++ ./arch/m68k/apollo/dn_ints.c	Wed Oct 27 00:05:29 1999
@@ -16,30 +16,19 @@
 static char BellOnCommand[] =  { 0xFF, 0x21, 0x81 },
 		    BellOffCommand[] = { 0xFF, 0x21, 0x82 };
 
+extern void dn_serial_print (const char *str);
 void dn_process_int(int irq, struct pt_regs *fp) {
 
-#if 0
-  unsigned char x;
-#endif 
-
-#if 0
-  printk("Aha DN interrupt ! : %d\n",irq);
-#endif
 
   if(dn_irqs[irq-160].handler) {
     dn_irqs[irq-160].handler(irq,dn_irqs[irq-160].dev_id,fp);
   }
   else {
-    printk("spurious irq %d occurred\n",irq);
+    printk("spurious irq %d occured\n",irq);
   }
 
-#if 0  
-  printk("*(0x10803) %02x\n",*(volatile unsigned char *)(IO_BASE+0x10803));
-  x=*(volatile unsigned char *)(IO_BASE+0x10805);
-#endif
-
-  *(volatile unsigned char *)(IO_BASE+0x11000)=0x20;
-  *(volatile unsigned char *)(IO_BASE+0x11100)=0x20;
+  *(volatile unsigned char *)(pica)=0x20;
+  *(volatile unsigned char *)(picb)=0x20;
 
 }
 
@@ -47,8 +36,6 @@
 
   int i;
 
-  printk("Init IRQ\n");
-
   for(i=0;i<16;i++) {
     dn_irqs[i].handler=NULL;
     dn_irqs[i].flags=IRQ_FLG_STD;
@@ -60,8 +47,6 @@
 
 int dn_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) {
 
-  printk("dn request IRQ\n");
-
   if((irq<0) || (irq>15)) {
     printk("Trying to request illegal IRQ\n");
     return -ENXIO;
@@ -73,9 +58,10 @@
     dn_irqs[irq].dev_id=dev_id;
     dn_irqs[irq].devname=devname;
     if(irq<8)
-      *(volatile unsigned char *)(IO_BASE+PICA+1)&=~(1<<irq);
+      *(volatile unsigned char *)(pica+1)&=~(1<<irq);
     else
-      *(volatile unsigned char *)(IO_BASE+PICB+1)&=~(1<<(irq-8));
+      *(volatile unsigned char *)(picb+1)&=~(1<<(irq-8));
+
     return 0;
   }
   else {
@@ -87,17 +73,15 @@
 
 void dn_free_irq(unsigned int irq, void *dev_id) {
 
-  printk("dn free irq\n");
-
   if((irq<0) || (irq>15)) {
     printk("Trying to free illegal IRQ\n");
     return ;
   }
 
   if(irq<8)
-    *(volatile unsigned char *)(IO_BASE+PICA+1)|=(1<<irq);
+    *(volatile unsigned char *)(pica+1)|=(1<<irq);
   else
-    *(volatile unsigned char *)(IO_BASE+PICB+1)|=(1<<(irq-8));  
+    *(volatile unsigned char *)(picb+1)|=(1<<(irq-8));  
 
   dn_irqs[irq].handler=NULL;
   dn_irqs[irq].flags=IRQ_FLG_STD;
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./arch/m68k/apollo/dma.c	Sun Oct 17 02:17:35 1999
@@ -0,0 +1,51 @@
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/apollodma.h>
+
+/* note only works for 16 Bit 1 page DMA's */
+
+static unsigned short next_free_xlat_entry=0;
+
+unsigned short dma_map_page(unsigned long phys_addr,int count,int type) {
+
+	unsigned long page_aligned_addr=phys_addr & (~((1<<12)-1));
+	unsigned short start_map_addr=page_aligned_addr >> 10;
+	unsigned short free_xlat_entry, *xlat_map_entry;
+	int i;
+
+	free_xlat_entry=next_free_xlat_entry;
+	for(i=0,xlat_map_entry=addr_xlat_map+(free_xlat_entry<<2);i<8;i++,xlat_map_entry++) {
+#if 0
+		printk("phys_addr: %x, page_aligned_addr: %x, start_map_addr: %x\n",phys_addr,page_aligned_addr,start_map_addr+i);
+#endif
+		outw(start_map_addr+i, xlat_map_entry);
+	}
+
+	next_free_xlat_entry+=2;
+	if(next_free_xlat_entry>125)
+		next_free_xlat_entry=0;
+	
+#if 0
+	printk("next_free_xlat_entry: %d\n",next_free_xlat_entry);
+#endif
+
+	return free_xlat_entry<<10;
+}
+
+void dma_unmap_page(unsigned short dma_addr) {
+
+	return ;
+
+}
+
--- ../linux-2.3.16/./arch/m68k/kernel/head.S	Sat Aug  7 14:17:31 1999
+++ ./arch/m68k/kernel/head.S	Wed Oct 27 00:11:08 1999
@@ -534,9 +534,16 @@
 #define is_not_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jne lab
 #define is_not_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jne lab
 #define is_not_hp300(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); jne lab
+#define is_not_apollo(lab) cmpl &MACH_APOLLO,%pc@(m68k_machtype); jne lab
 #define is_not_q40(lab) cmpl &MACH_Q40,%pc@(m68k_machtype); jne lab
 #define is_not_sun3x(lab) cmpl &MACH_SUN3X,%pc@(m68k_machtype); jne lab
 
+#define hasnt_leds(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); \
+			jeq 42f; \
+			cmpl &MACH_APOLLO,%pc@(m68k_machtype); \
+			jne lab ;\
+		42:\
+
 #define is_040_or_060(lab)	btst &CPUTYPE_0460,%pc@(L(cputype)+3); jne lab
 #define is_not_040_or_060(lab)	btst &CPUTYPE_0460,%pc@(L(cputype)+3); jeq lab
 #define is_040(lab)		btst &CPUTYPE_040,%pc@(L(cputype)+3); jne lab
@@ -549,8 +556,8 @@
    the console is running.  Writing a 1 bit turns the corresponding LED
    _off_ - on the 340 bit 7 is towards the back panel of the machine.  */
 .macro	leds	mask
-#ifdef CONFIG_HP300
-	is_not_hp300(.Lled\@)
+#if defined(CONFIG_HP300) || defined(CONFIG_APOLLO)
+	hasnt_leds(.Lled\@)
 	pea	\mask
 	func_call	set_leds
 	addql	#4,%sp
@@ -589,6 +596,7 @@
 /*
  * Setup initial stack pointer
  */
+
 	lea	%pc@(SYMBOL_NAME(_stext)),%sp
 
 /*
@@ -710,6 +718,7 @@
 	lea	%pc@(L(cputype)),%a0
 	movel	%d1,%a0@
 
+
 	/*
 	 * NOTE:
 	 *
@@ -1126,8 +1135,18 @@
 
 	/* setup tt1 for I/O */
 	mmu_map_tt	#1,#0x40000000,#0x40000000,#_PAGE_NOCACHE_S
+	jbra	L(mmu_init_done)
 
 L(notsun3x):
+#endif
+
+#ifdef CONFIG_APOLLO
+	is_not_apollo(L(notapollo))
+
+	putc	'P'
+	mmu_map         #0x80000000,#0,#0x02000000,#_PAGE_NOCACHE030
+	
+L(notapollo):	
 	jbra	L(mmu_init_done)
 #endif
 
@@ -1275,7 +1294,7 @@
 #ifdef CONFIG_HP300
 	is_not_hp300(1f)
 	/*
-	 * Fix up the custom register to point to the new location of the LEDs.
+         * Fix up the custom register to point to the new location of the LEDs.
 	 */
 	movel	#0xf0000000,L(custom)
 
@@ -1294,6 +1313,16 @@
 1:
 #endif
 
+#ifdef CONFIG_APOLLO
+	is_not_apollo(1f)
+
+        /*
+	 * Fix up the iobase before printing
+         */
+        movel   #0x80000000,L(iobase)
+1:
+#endif
+	
 /*
  * Fixup the addresses for the kernel pointer table and availmem.
  * Convert them from physical addresses to virtual addresses.
@@ -2810,6 +2839,10 @@
 2:		
 #endif	
 	
+#ifdef CONFIG_APOLLO
+/* We count on the PROM initializing SIO1 */
+#endif
+
 L(serial_init_done):
 func_return	serial_init
 
@@ -2944,9 +2977,20 @@
 	move.b	%d0,%a0@
 	addq.l	#4,%a0
 	move.l	%a0,%a1@
+	jbra    L(serial_putc_done)
 2:		
 #endif	
 
+#ifdef CONFIG_APOLLO
+	is_not_apollo(2f)
+	movl    %pc@(L(iobase)),%a1
+        moveb   %d0,%a1@(LTHRB0)
+1:      moveb   %a1@(LSRB0),%d0
+        andb    #0x4,%d0
+        beq     1b
+2:
+#endif
+
 L(serial_putc_done):
 func_return	serial_putc
 
@@ -3029,11 +3073,23 @@
 	rts
 #endif /* CONFIG_MAC */
 
-#ifdef CONFIG_HP300
+#if defined(CONFIG_HP300) || defined(CONFIG_APOLLO)
 func_start	set_leds,%d0/%a0
 	movel	ARG1,%d0
+#ifdef CONFIG_HP300
+	is_not_hp300(1f)
 	movel	%pc@(Lcustom),%a0
 	moveb	%d0,%a0@(0x1ffff)
+	jra	2f
+#endif
+1:	
+#ifdef CONFIG_APOLLO
+	movel   %pc@(L(iobase)),%a0
+	lsll    #8,%d0
+        eorw    #0xff00,%d0
+	moveb	%d0,%a0@(LCPUCTRL)
+#endif
+2:
 func_return	set_leds
 #endif
 
@@ -3625,6 +3681,14 @@
 L(mac_sccbase):
 	.long	0
 #endif /* MAC_SERIAL_DEBUG */
+#endif
+
+#if defined (CONFIG_APOLLO)
+LSRB0        = 0x10412
+LTHRB0       = 0x10416
+LCPUCTRL     = 0x10100
+L(iobase):
+        .long 0
 #endif
 
 __FINIT
--- ../linux-2.3.16/./arch/m68k/kernel/setup.c	Fri Sep  3 16:12:20 1999
+++ ./arch/m68k/kernel/setup.c	Fri Oct 15 15:21:34 1999
@@ -24,7 +24,9 @@
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
 #include <asm/irq.h>
+#if 0
 #include <asm/io.h>
+#endif
 #include <asm/machdep.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
@@ -32,6 +34,9 @@
 #ifdef CONFIG_ATARI
 #include <asm/atarihw.h>
 #endif
+#ifdef CONFIG_APOLLO
+#include <asm/apollohw.h>
+#endif
 
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>
@@ -120,6 +125,7 @@
 extern int atari_parse_bootinfo(const struct bi_record *);
 extern int mac_parse_bootinfo(const struct bi_record *);
 extern int q40_parse_bootinfo(const struct bi_record *);
+extern int apollo_parse_bootinfo(const struct bi_record *);
 
 extern void config_amiga(void);
 extern void config_atari(void);
@@ -179,6 +185,8 @@
 		    unknown = mac_parse_bootinfo(record);
 		else if (MACH_IS_Q40)
 		    unknown = q40_parse_bootinfo(record);
+                else if (MACH_IS_APOLLO)
+                    unknown = apollo_parse_bootinfo(record);
 		else
 		    unknown = 1;
 	}
@@ -205,9 +213,18 @@
 	int i;
 	char *p, *q;
 
+	sio01_physaddr=SAU7_SIO01_PHYSADDR;
+        sio01.rhrb_thrb = 'S';
+        while (!(sio01.srb_csrb & 0x4))
+            ;
+
 	/* The bootinfo is located right after the kernel bss */
 	m68k_parse_bootinfo((const struct bi_record *)&_end);
 
+        sio01.rhrb_thrb = 's';
+        while (!(sio01.srb_csrb & 0x4))
+            ;
+
 	if (CPU_IS_040)
 		m68k_is040or060 = 4;
 	else if (CPU_IS_060)
@@ -270,6 +287,10 @@
 		if ((p = strchr( p, ' ' ))) ++p;
 	    }
 	}
+
+        sio01.rhrb_thrb = '0'+m68k_machtype;
+        while (!(sio01.srb_csrb & 0x4))
+            ;
 
 	switch (m68k_machtype) {
 #ifdef CONFIG_AMIGA
--- ../linux-2.3.16/./drivers/char/Makefile	Fri Sep  3 16:14:10 1999
+++ ./drivers/char/Makefile	Sat Oct 30 22:35:22 1999
@@ -140,6 +140,10 @@
 S = y
 endif
 
+ifeq ($(CONFIG_APOLLO),y)
+S = y
+endif
+
 ifeq ($(CONFIG_APUS),y)
 A = y
 endif
@@ -154,6 +158,7 @@
 S = y
 endif
 
+
 ifdef S
 OX_OBJS += m68kserial.o
 else
@@ -201,6 +206,11 @@
 
 ifdef CONFIG_AMIGA
 O_OBJS += amikeyb.o
+endif
+
+ifdef CONFIG_APOLLO
+O_OBJS += dn_keyb.o busmouse.o
+S=y
 endif
 
 ifdef CONFIG_MAGIC_SYSRQ
--- ../linux-2.3.16/./drivers/char/dn_keyb.c	Thu Aug  5 10:45:39 1999
+++ ./drivers/char/dn_keyb.c	Sat Oct 16 02:58:56 1999
@@ -1,4 +1,3 @@
-#include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/errno.h>
@@ -18,8 +17,9 @@
 #include <asm/apollohw.h>
 #include <asm/uaccess.h>
 
+#include "busmouse.h"
 
-extern void handle_scancode(unsigned char);
+/* extern void handle_scancode(unsigned char,int ); */
 
 #define DNKEY_CAPS 0x7e
 #define BREAK_FLAG 0x80
@@ -37,18 +37,12 @@
 #define APOLLO_KBD_MODE_MOUSE   0x02
 #define APOLLO_KBD_MODE_CHANGE 0xff
 
-#define MSE_UPDATE_ON() mouse_update_allowed=1
-#define MSE_UPDATE_OFF() mouse_update_allowed=0
-
 static u_char keyb_cmds[APOLLO_KEYB_CMD_ENTRIES];
 static short keyb_cmd_read=0, keyb_cmd_write=0;
 static int keyb_cmd_transmit=0;
+static int msedev;
 
 static unsigned int kbd_mode=APOLLO_KBD_MODE_KEYB;
-static short mouse_dx,mouse_dy,mouse_buttons;
-static int mouse_ready=0,mouse_update_allowed=0,mouse_active=0;
-static DECLARE_WAIT_QUEUE_HEAD(mouse_wait);
-static struct fasync_struct *mouse_fasyncptr=NULL;
 
 #if 0
 static void debug_keyb_timer_handler(unsigned long ignored);
@@ -302,103 +296,6 @@
 }
 #endif
 
-static unsigned int mouse_poll(struct file *file, poll_table * wait)
-{
-        poll_wait(&mouse_wait, wait);
-        if (mouse_ready)
-                return POLLIN | POLLRDNORM;
-        return 0;
-}
-
-static ssize_t write_mouse(struct file * file, const char * buffer,
-			   size_t count, loff_t *ppos)
-{
-        return -EINVAL;
-}
-
-static ssize_t read_mouse(struct file * file, char * buffer,
-			  size_t count, loff_t *ppos)
-{
-	int dx,dy,r;
-	unsigned char buttons;
-
-	if (count < 3)
-		return -EINVAL;
-	if ((r = verify_area(VERIFY_WRITE, buffer, count)))
-   		return r;
-	if (!mouse_ready)
-		return -EAGAIN;	
-
-	MSE_UPDATE_OFF();
-	dx=mouse_dx;	
-	dy=mouse_dy;	
-	if (dx < -127)
-		dx = -127;
-	else
-	if (dx > 127)
-   		dx = 127;
-	if (dy < -127)
-   		dy = -127;
-	else
-	if (dy > 127)
-   		dy = 127;
-	buttons=(mouse_buttons & 1 ? 4 : 0) |
-			(mouse_buttons & 2 ? 1 : 0) |
-			(mouse_buttons & 4 ? 2 : 0);
-
-	mouse_dx-=dx;
-	mouse_dy-=dy;
-	MSE_UPDATE_ON();
-
-	if (put_user(buttons | 0x80, buffer++) ||
-	    put_user((char)dx, buffer++) ||
-	    put_user((char)dy, buffer++))
-		return -EINVAL;
-
-	if (count > 3)
-		if (clear_user(buffer, count - 3))
-			return -EFAULT;
-	return count;
-}
-
-static int fasync_mouse(int fd, struct file *filp, int on)
-{
-        int retval;
-
-        retval = fasync_helper(fd, filp, on, &mouse_fasyncptr);
-        if (retval < 0)
-                return retval;
-        return 0;
-}
-
-
-static int release_mouse(struct inode * inode, struct file * file)
-{
-        fasync_mouse(-1, file, 0);
-        if (--mouse_active)
-                return 0;
-        MSE_UPDATE_OFF();
-        MOD_DEC_USE_COUNT;
-        return 0;
-}
-
-static int open_mouse(struct inode * inode, struct file * file)
-{
-        if (mouse_active++)
-                return 0;
-        /*
-         *  use VBL to poll mouse deltas
-         */
-
-        mouse_dx = 0;
-        mouse_dy = 0;
-        mouse_buttons = 0;
-        mouse_active = 1;
-        MOD_INC_USE_COUNT;
-        MSE_UPDATE_ON();
-        return 0;
-}
-
 static void dn_keyb_process_key_event(unsigned char scancode) {
 
 	static unsigned char lastscancode;
@@ -411,7 +308,7 @@
 
 	if(prev_scancode==APOLLO_KBD_MODE_CHANGE) {
 		kbd_mode=scancode;
-/*		printk("modechange: %d\n",scancode);*/
+/*		printk("modechange: %d\n",scancode); */
 	}
 	else if((scancode & (~BREAK_FLAG)) == DNKEY_CAPS) {
     	/* printk("handle_scancode: %02x\n",DNKEY_CAPS); */
@@ -440,7 +337,8 @@
 
 	static short mouse_byte_count=0;
 	static u_char mouse_packet[3];
-	
+	short mouse_buttons;	
+
 	mouse_packet[mouse_byte_count++]=mouse_data;
 
 	if(mouse_byte_count==3) {
@@ -452,28 +350,14 @@
 				dn_keyb_process_key_event(mouse_packet[2]);
 		}				
 		if((mouse_packet[0] & 0x8f) == 0x80) {
-			if(mouse_update_allowed) {
-				mouse_ready=1;
-				mouse_buttons=(mouse_packet[0] >> 4) & 0x7;
-				mouse_dx+=mouse_packet[1] == 0xff ? 0 : (signed char)mouse_packet[1];
-				mouse_dy+=mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2];
-				wake_up_interruptible(&mouse_wait);
-				if (mouse_dx < -2048)
-              		mouse_dx = -2048;
-          		else
-          		if (mouse_dx >  2048)
-              		mouse_dx =  2048;
-          		if (mouse_dy < -2048)
-              		mouse_dy = -2048;
-          		else
-          		if (mouse_dy >  2048)
-              		mouse_dy =  2048;
-				if (mouse_fasyncptr)
-              		kill_fasync(mouse_fasyncptr, SIGIO);
-			}
-			mouse_byte_count=0;
-/*			printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */
+			mouse_buttons=(mouse_packet[0] >> 4) & 0x7;
+			busmouse_add_movementbuttons(msedev, 
+                    mouse_packet[1] == 0xff ? 0 : (signed char)mouse_packet[1],
+                    mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2],
+ 		            (mouse_buttons >>1) | ((mouse_buttons &1)<<2));
+/*			printk("mouse: %x\n",mouse_buttons);  */
 		}
+		mouse_byte_count=0;
 	}
 }
 
@@ -552,23 +436,20 @@
 
 }
 
-struct file_operations apollo_mouse_fops = {
-        NULL,           /* mouse_seek */
-        read_mouse,
-        write_mouse,
-        NULL,           /* mouse_readdir */
-        mouse_poll,     /* mouse_poll */
-        NULL,           /* mouse_ioctl */
-        NULL,           /* mouse_mmap */
-        open_mouse,
-	NULL,		/* flush */
-        release_mouse,
-        NULL,
-        fasync_mouse,
-};
+static int release_mouse(struct inode * inode, struct file * file)
+{
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+
+static int open_mouse(struct inode * inode, struct file * file)
+{
+        MOD_INC_USE_COUNT;
+        return 0;
+}
 
-static struct miscdevice apollo_mouse = {
-        APOLLO_MOUSE_MINOR, "apollomouse", &apollo_mouse_fops
+static struct busmouse apollo_mouse = {
+        APOLLO_MOUSE_MINOR, "apollomouse", open_mouse, release_mouse,7
 };
 
 int __init dn_keyb_init(void){
@@ -583,12 +464,12 @@
   memcpy(key_maps[8], dnalt_map, sizeof(plain_map));
   memcpy(key_maps[12], dnctrl_alt_map, sizeof(plain_map));
 
-  mouse_dx=0; 
-  mouse_dy=0; 
-  mouse_buttons=0; 
-  mouse_wait=NULL;
 
-  misc_register(&apollo_mouse);
+  msedev=register_busmouse(&apollo_mouse);
+  if (msedev < 0)
+      printk(KERN_WARNING "Unable to install Apollo mouse driver.\n");
+   else
+      printk(KERN_INFO "Apollo mouse installed.\n");
 
   /* program UpDownMode */
 
--- ../linux-2.3.16/./drivers/char/m68kserial.c	Sun Jun 27 15:54:45 1999
+++ ./drivers/char/m68kserial.c	Fri Oct 15 16:23:18 1999
@@ -1675,10 +1675,13 @@
 extern int hpdca_serial_console_wait_key(struct console *co);
 extern int hpdca_init_serial_console(int cflag);
 
+extern void dn_serial_console_write(struct console *co, const char *s, unsigned int count);
+extern int dn_serial_console_wait_key(struct console *co);
+
 /*
  *	Setup initial baud/bits/parity.
  */
-__initfunc(static int serial_console_setup(struct console *co, char *options))
+static int __init serial_console_setup(struct console *co, char *options)
 {
 	char *s;
 	int baud = 0, bits, parity;
@@ -1782,6 +1785,11 @@
 	else if (MACH_IS_BVME6000 && co->index == 0) {
 		bvme6000_init_console_port (co, cflag);
 	}
+        else if (MACH_IS_APOLLO && sercons.index == 0) {
+                co->write = dn_serial_console_write;
+                co->wait_key = dn_serial_console_wait_key;
+                /* we rely on MD to initialize the serial port */
+        }
 	return( 0 );
 }
 
--- ../linux-2.3.16/./drivers/char/tty_io.c	Fri Sep  3 16:14:32 1999
+++ ./drivers/char/tty_io.c	Fri Oct 15 18:25:18 1999
@@ -2066,8 +2066,11 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 #ifdef CONFIG_8xx
 	kmem_start = console_8xx_init(kmem_start, kmem_end);
-#else 	
+#else
+	kmem_start = m68k_serial_console_init(kmem_start, kmem_end);
+#if 0
 	kmem_start = serial_console_init(kmem_start, kmem_end);
+#endif
 #endif /* CONFIG_8xx */
 #endif
 	return kmem_start;
--- ../linux-2.3.16/./drivers/net/Makefile	Mon Aug 30 14:22:14 1999
+++ ./drivers/net/Makefile	Tue Oct 26 23:51:31 1999
@@ -1051,6 +1051,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_APOLLO_ELPLUS),y)
+L_OBJS += 3c505_pio_apollo.o
+else
+  ifeq ($(CONFIG_APOLLO_ELPLUS),m)
+  M_OBJS += 3c505_pio_apollo.o
+  endif
+endif
+
 ifeq ($(CONFIG_DGRS),y)
 L_OBJS += dgrs.o
 else
--- ../linux-2.3.16/./drivers/net/Space.c	Mon Aug 30 14:22:15 1999
+++ ./drivers/net/Space.c	Fri Oct 15 17:43:52 1999
@@ -379,6 +379,9 @@
 #ifdef CONFIG_ELPLUS		/* 3c505 */
 	{elplus_probe, 0},
 #endif
+#ifdef CONFIG_APOLLO_ELPLUS             /* 3c505 */
+        {elplus_probe, 0},
+#endif
 #ifdef CONFIG_SK_G16
 	{SK_init, 0},
 #endif
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/net/3c505_pio_apollo.c	Wed Oct 27 01:07:56 1999
@@ -0,0 +1,1537 @@
+/*
+ * Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
+ * 	By Craig Southeren and Juha Laiho
+ *
+ * 3c505.c	This module implements an interface to the 3Com
+ *		Etherlink Plus (3c505) ethernet card. Linux device 
+ *		driver interface reverse engineered from the Linux 3C509
+ *		device drivers. Some 3C505 information gleaned from
+ *		the Crynwr packet driver. Still this driver would not
+ *		be here without 3C505 technical reference provided by
+ *		3Com.
+ *
+ * Version:	@(#)3c505.c	0.8.4	17-Dec-95
+ *
+ * Authors:	Linux 3c505 device driver by
+ *			Craig Southeren, <craigs@ineluki.apana.org.au>
+ *              Final debugging by
+ *			Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ *		Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
+ *			Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ *              Linux 3C509 driver by
+ *             		Donald Becker, <becker@super.org>
+ *		Crynwr packet driver by
+ *			Krishnan Gopalan and Gregg Stefancik,
+ * 			   Clemson University Engineering Computer Operations.
+ *			Portions of the code have been adapted from the 3c505
+ *			   driver for NCSA Telnet by Bruce Orchard and later
+ *			   modified by Warren Van Houten and krus@diku.dk.
+ *              3C505 technical information provided by
+ *                      Terry Murphy, of 3Com Network Adapter Division
+ *		Linux 1.3.0 changes by
+ *			Alan Cox <Alan.Cox@linux.org>
+ *                     
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/apollohw.h>
+#include <asm/irq.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c505_pio_apollo.h"
+
+#define swapw(x)        ((((x)>>8)&0x00ffU)|(((x)<<8)&0xff00U))
+#define swapd(x)        ((((x)>>24)&0xffU)|(((x)>>8) & 0xff00U) \
+			 |(((x)<<8)&0xff0000U)|(((x)<<24)&0xff000000U))
+
+/*********************************************************
+ *
+ *  define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static const char * filename = __FILE__;
+
+static const char * null_msg = "*** NULL at %s:%s (line %d) ***\n";
+#define CHECK_NULL(p) \
+	if (!p) printk(null_msg, filename,__FUNCTION__,__LINE__)
+
+static const char * timeout_msg = "*** timeout at %s:%s (line %d) ***\n";
+#define TIMEOUT_MSG(lineno) \
+	printk(timeout_msg, filename,__FUNCTION__,(lineno))
+
+static const char * invalid_pcb_msg =
+	"*** invalid pcb length %d at %s:%s (line %d) ***\n";
+#define INVALID_PCB_MSG(len) \
+	printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
+
+static const char * search_msg = "%s: Looking for 3c505 adapter at address %#x...";
+
+static const char * stilllooking_msg = "still looking...";
+
+static const char * found_msg = "found.\n";
+
+static const char * notfound_msg = "not found (reason = %d)\n";
+
+static const char * couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ *  various other debug stuff
+ *
+ *********************************************************/
+#define ELP_NEED_HARD_RESET 0
+
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
+#else
+static int elp_debug = 0;
+#endif
+
+/*
+ *  0 = no messages (well, some)
+ *  1 = messages when high level commands performed
+ *  2 = messages when low level commands performed
+ *  3 = messages when interrupts received
+ */
+
+#define	ELP_VERSION	"0.8.4"
+
+/*****************************************************************
+ *
+ * useful macros
+ *
+ *****************************************************************/
+
+#ifndef	TRUE
+#define	TRUE	1
+#endif
+
+#ifndef	FALSE
+#define	FALSE	0
+#endif
+
+
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+const int addr_list[]={IO_BASE + 0x58000,0}; 
+
+/*****************************************************************
+ *
+ * Functions for I/O (note the inline !)
+ *
+ *****************************************************************/
+
+static inline unsigned char
+inb_status (unsigned int base_addr)
+{
+	return inb(base_addr+PORT_STATUS);
+}
+
+static inline unsigned char
+inb_control (unsigned int base_addr)
+{
+	return inb(base_addr+PORT_CONTROL);
+}
+
+static inline int
+inb_command (unsigned int base_addr)
+{
+	return inb(base_addr+PORT_COMMAND);
+}
+
+static inline void
+outb_control (unsigned char val, unsigned int base_addr)
+{
+	outb(val, base_addr+PORT_CONTROL);
+}
+
+static inline void
+outb_command (unsigned char val, unsigned int base_addr)
+{
+	outb(val, base_addr+PORT_COMMAND);
+}
+
+static inline unsigned int
+inw_data (unsigned int base_addr)
+{
+	return inw(base_addr+PORT_DATA);
+}
+
+static inline void
+outw_data (unsigned int val, unsigned int base_addr)
+{
+	outw(val, base_addr+PORT_DATA);
+}
+
+
+/*****************************************************************
+ *
+ *  structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+typedef struct {
+	volatile short got[NUM_TRANSMIT_CMDS];	/* flags for command completion */
+	pcb_struct tx_pcb;	/* PCB for foreground sending */
+	pcb_struct rx_pcb;	/* PCB for foreground receiving */
+	pcb_struct itx_pcb;	/* PCB for background sending */
+	pcb_struct irx_pcb;	/* PCB for background receiving */
+	struct enet_statistics stats;
+} elp_device;
+
+static int reset_count=0;
+
+/*****************************************************************
+ *
+ *  useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define	GET_ASF(addr) \
+	(get_status(addr)&ASF_PCB_MASK)
+
+static inline int
+get_status (unsigned int base_addr)
+{
+	int timeout = jiffies + 10;
+	register int stat1;
+	do {
+		stat1 = inb_status(base_addr);
+	} while (stat1 != inb_status(base_addr) && jiffies < timeout);
+	if (jiffies >= timeout)
+		TIMEOUT_MSG(__LINE__);
+	return stat1;
+}
+
+static inline void
+set_hsf (unsigned int base_addr, int hsf)
+{
+	cli();
+	outb_control((inb_control(base_addr)&~HSF_PCB_MASK)|hsf, base_addr);
+	sti(); 
+}
+
+#define WAIT_HCRE(addr,toval) wait_hcre((addr),(toval),__LINE__)
+static inline int
+wait_hcre (unsigned int base_addr, int toval, int lineno)
+{
+	int timeout = jiffies + toval;
+	while (((inb_status(base_addr)&HCRE)==0) && (jiffies <= timeout))
+		;
+	if (jiffies >= timeout) {
+		TIMEOUT_MSG(lineno);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static inline int
+wait_fast_hcre (unsigned int base_addr, int toval, int lineno)
+{
+	int timeout = 0;
+	while (((inb_status(base_addr)&HCRE)==0) && (timeout++ < toval))
+		;
+	if (timeout >= toval) {
+		sti();
+		TIMEOUT_MSG(lineno);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static int start_receive (struct net_device *, pcb_struct *);
+static void adapter_hard_reset (struct net_device *);
+
+inline static void
+adapter_reset (struct net_device * dev)
+{
+	int timeout;
+	unsigned char orig_hcr=inb_control(dev->base_addr);
+
+	elp_device * adapter=dev->priv;
+
+	outb_control(0,dev->base_addr);
+
+	if (inb_status(dev->base_addr)&ACRF) {
+		do {
+			inb_command(dev->base_addr);
+			timeout=jiffies+2;
+			while ((jiffies<=timeout) && !(inb_status(dev->base_addr)&ACRF))
+				;
+		} while (inb_status(dev->base_addr)&ACRF);
+		set_hsf(dev->base_addr,HSF_PCB_NAK);
+	}
+
+	outb_control(inb_control(dev->base_addr)|ATTN|DIR,dev->base_addr);
+	timeout=jiffies+1;
+	while (jiffies<=timeout)
+		;
+	outb_control(inb_control(dev->base_addr)&~ATTN,dev->base_addr);
+	timeout=jiffies+1;
+	while (jiffies<=timeout)
+		;
+	outb_control(inb_control(dev->base_addr)|FLSH,dev->base_addr);
+	timeout=jiffies+1;
+	while (jiffies<=timeout)
+		;
+	outb_control(inb_control(dev->base_addr)&~FLSH,dev->base_addr);
+	timeout=jiffies+1;
+	while (jiffies<=timeout)
+		;
+
+	outb_control(orig_hcr, dev->base_addr);
+	if (!start_receive(dev, &adapter->tx_pcb))
+		printk("%s: start receive command failed \n", dev->name);
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ *   Send a PCB to the adapter. 
+ *
+ *	output byte to command reg  --<--+
+ *	wait until HCRE is non zero      |
+ *	loop until all bytes sent   -->--+
+ *	set HSF1 and HSF2 to 1
+ *	output pcb length
+ *	wait until ASF give ACK or NAK
+ *	set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+static int
+send_pcb (struct net_device * dev, pcb_struct * pcb)
+{
+	int i;
+	int timeout;
+	int cont;
+
+	/*
+	 * load each byte into the command register and
+	 * wait for the HCRE bit to indicate the adapter
+	 * had read the byte
+	 */
+	set_hsf(dev->base_addr,0); 
+	if ((cont = WAIT_HCRE(dev->base_addr,5))) {
+		cli();
+		if (pcb->command==CMD_TRANSMIT_PACKET)
+			outb_control(inb_control(dev->base_addr)&~DIR,dev->base_addr);
+		outb_command(pcb->command, dev->base_addr);
+		sti();
+		cont = WAIT_HCRE(dev->base_addr,5);
+	}
+
+	if (cont) {
+		outb_command(pcb->length, dev->base_addr);
+		cont = WAIT_HCRE(dev->base_addr,5);
+	}
+
+	cli();
+	for (i = 0; cont && (i < pcb->length); i++) {
+		outb_command(pcb->data.raw[i], dev->base_addr);
+		cont = wait_fast_hcre(dev->base_addr,20000,__LINE__);
+	} /* if wait_fast_hcre() failed, has already done sti() */
+
+	/* set the host status bits to indicate end of PCB */
+	/* send the total packet length as well */
+	/* wait for the adapter to indicate that it has read the PCB */
+	if (cont) {
+		set_hsf(dev->base_addr,HSF_PCB_END);
+		outb_command(2+pcb->length, dev->base_addr);
+		sti();
+		timeout = jiffies + 7;
+		while (jiffies < timeout) {
+			i = GET_ASF(dev->base_addr);
+			if ((i == ASF_PCB_ACK) || (i == ASF_PCB_NAK))
+				break;
+		}
+
+		if (i == ASF_PCB_ACK) {
+			reset_count=0;
+			return TRUE;
+		}
+		else if (i == ASF_PCB_NAK) {
+			printk("%s: PCB send was NAKed\n", dev->name);
+		} else {
+			printk("%s: timeout after sending PCB\n", dev->name);
+		}
+	} else {
+		sti();
+		printk("%s: timeout in middle of sending PCB\n", dev->name);
+	}
+
+	adapter_reset(dev);
+	return FALSE;
+}
+
+/*****************************************************************
+ *
+ * receive_pcb
+ *   Read a PCB to the adapter
+ *
+ *	wait for ACRF to be non-zero        ---<---+
+ *	input a byte                               |
+ *	if ASF1 and ASF2 were not both one         |
+ *		before byte was read, loop      --->---+
+ *	set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static int
+receive_pcb (struct net_device * dev, pcb_struct * pcb)
+{
+	int i, j;
+	int total_length;
+	int stat;
+	int timeout;
+
+	CHECK_NULL(pcb);
+	CHECK_NULL(dev);
+
+	set_hsf(dev->base_addr,0);
+
+	/* get the command code */
+	do {
+	  timeout = jiffies + 2;
+	  while (((stat = get_status(dev->base_addr))&ACRF) == 0 && jiffies < timeout)
+		;
+	  if (jiffies >= timeout) {
+	    TIMEOUT_MSG(__LINE__);
+	    return FALSE;
+	  }
+	  
+	  pcb->command = inb_command(dev->base_addr);
+	} while((pcb->command < CMD_CONFIGURE_ADAPTER_RESPONSE) ||
+		(pcb->command > CMD_ADAPTER_INFO_RESPONSE));
+
+	/* read the data length */
+
+	do {
+	  timeout = jiffies + 3;
+	  while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout)
+		;
+	  if (jiffies >= timeout) {
+	    TIMEOUT_MSG(__LINE__);
+	    return FALSE;
+	  }
+	  pcb->length = inb_command(dev->base_addr);
+	} while(pcb->length==pcb->command);
+
+	if (pcb->length > MAX_PCB_DATA) {
+		INVALID_PCB_MSG(pcb->length);
+		adapter_reset(dev);
+		return FALSE;
+	}
+
+	/* read the data */
+	cli();
+	i = 0;
+	do {
+		j = 0;
+		while (((stat = get_status(dev->base_addr))&ACRF) == 0 && j++ < 20000)
+			;
+		pcb->data.raw[i++] = inb_command(dev->base_addr);
+		if (i > MAX_PCB_DATA)
+			INVALID_PCB_MSG(i);
+	} while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000);
+	sti();
+	if (j >= 20000) {
+		TIMEOUT_MSG(__LINE__);
+		return FALSE;
+	}
+
+	/* woops, the last "data" byte was really the length! */
+	total_length = pcb->data.raw[--i];
+
+	/* safety check total length vs data length */
+	if (total_length != (pcb->length + 2)) {
+		if (elp_debug >= 2)
+			printk("%s: mangled PCB received\n", dev->name);
+		set_hsf(dev->base_addr,HSF_PCB_NAK);
+		return FALSE;
+	}
+
+	set_hsf(dev->base_addr,HSF_PCB_ACK);
+	reset_count=0;
+	return TRUE;
+}
+
+static void
+adapter_hard_reset (struct net_device * dev)
+{
+	int timeout;
+	long flags;
+
+	CHECK_NULL(dev);
+
+	save_flags(flags);
+	sti();
+
+	if (elp_debug > 0)
+		printk("%s: Resetting the adapter, please wait (approx 20 s)\n",
+			dev->name);
+	/*
+	 * take FLSH and ATTN high
+	 */
+	outb_control(ATTN|FLSH, dev->base_addr);
+
+	/*
+	 * wait for a little bit
+	 */
+	for (timeout = jiffies + 20; jiffies <= timeout; )
+		;
+
+	/*
+	 * now take them low
+	 */
+	outb_control(0, dev->base_addr);
+
+	/*
+	 * wait for a little bit
+	 */
+	for (timeout = jiffies + 20; jiffies <= timeout; )
+		;
+
+	/*
+	 * now hang around until the board gets it's act together
+	 */
+	for (timeout = jiffies + (100 * 15); jiffies <= timeout; ) 
+		if (GET_ASF(dev->base_addr) != ASF_PCB_END)
+			break;
+	restore_flags(flags);
+}
+
+/******************************************************
+ *
+ *  queue a receive command on the adapter so we will get an
+ *  interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static int
+start_receive (struct net_device * dev, pcb_struct * tx_pcb)
+{
+	CHECK_NULL(dev);
+	CHECK_NULL(tx_pcb);
+
+	if (elp_debug >= 3)
+		printk("%s: restarting receiver\n", dev->name);
+	tx_pcb->command = CMD_RECEIVE_PACKET;
+	tx_pcb->length = sizeof(struct Rcv_pkt);
+	tx_pcb->data.rcv_pkt.buf_seg
+		= tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+	tx_pcb->data.rcv_pkt.buf_len = swapw(1600);
+	tx_pcb->data.rcv_pkt.timeout = 0;	/* set timeout to zero */
+	return send_pcb(dev, tx_pcb); 
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void
+receive_packet (struct net_device * dev, int len)
+{
+	register int i;
+	unsigned short * ptr;
+	int timeout;
+	int rlen;
+	struct sk_buff *skb;
+	elp_device * adapter;
+
+	CHECK_NULL(dev);
+	adapter=dev->priv;
+
+	if (len <= 0 || ((len & ~1) != len))
+		if (elp_debug >= 3) {
+			sti();
+			printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+			cli();
+		}
+
+	rlen = (len+1) & ~1;
+
+	skb = dev_alloc_skb(rlen+2);
+
+	/*
+	 * make sure the data register is going the right way
+	 */
+
+	outb_control(inb_control(dev->base_addr)|DIR, dev->base_addr);
+
+	/*
+	 * if buffer could not be allocated, swallow it
+	 */
+	if (skb == NULL) {
+		for (i = 0; i < (rlen/2); i++) {
+			timeout = 0;
+			while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+				;
+			if (timeout >= 20000) {
+				sti();
+				TIMEOUT_MSG(__LINE__);
+				break;
+			}
+
+			inw_data(dev->base_addr);
+		}
+		adapter->stats.rx_dropped++;
+
+	} else {
+		skb_reserve(skb,2);	/* 16 byte alignment */
+		skb->dev = dev;
+
+		/*
+		 * now read the data from the adapter
+		 */
+		ptr = (unsigned short *)skb_put(skb,len);
+		for (i = 0; i < (rlen/2); i++) { 
+			timeout = 0;
+			while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000) 
+				;
+			if (timeout >= 20000) {
+				sti();
+				printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+					filename,__LINE__, i, rlen/2);	
+				kfree_skb(skb);
+				return;
+			}
+
+			*ptr = inw_data(dev->base_addr); 
+			ptr++; 
+		}
+
+		sti();
+		skb->protocol=eth_type_trans(skb,dev);
+		netif_rx(skb);
+	}
+
+	outb_control(inb_control(dev->base_addr)&~DIR, dev->base_addr);
+}
+
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static void
+elp_interrupt (int irq, void *dev_id, struct pt_regs *reg_ptr)
+{
+	int len;
+	int dlen;
+	struct net_device *dev;
+	elp_device * adapter;
+	int timeout;
+
+	irq-=160;
+	if (irq < 0 || irq > 15) {
+		printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
+		return;
+	}
+
+	dev = dev_id;
+
+	if (dev == NULL) {
+		printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	adapter = (elp_device *) dev->priv;
+
+	CHECK_NULL(adapter);
+
+	if (dev->interrupt)
+		if (elp_debug >= 2)
+			printk("%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	/*
+	 * allow interrupts (we need timers!)
+	 */
+	sti();
+
+	/*
+	 * receive a PCB from the adapter
+	 */
+	timeout = jiffies + 3;
+	while ((inb_status(dev->base_addr)&ACRF) != 0 && jiffies < timeout) {
+
+		if (receive_pcb(dev, &adapter->irx_pcb)) {
+
+			switch (adapter->irx_pcb.command) {
+
+				/*
+				 * received a packet - this must be handled fast
+				 */
+				case CMD_RECEIVE_PACKET_COMPLETE:
+					/* if the device isn't open, don't pass packets up the stack */
+					if (dev->start == 0)
+						break;
+					cli();
+					/* Set direction of adapter FIFO */
+					outb_control(inb_control(dev->base_addr)|DIR,
+					             dev->base_addr);
+					len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+					len=swapw(len);
+
+					dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+					dlen=swapw(dlen);
+					if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+						printk("%s: interrupt - packet not received correctly\n", dev->name);
+						sti();
+					} else {
+						if (elp_debug >= 3) {
+							sti();
+							printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+							cli();
+						}
+						receive_packet(dev, dlen);
+						sti();
+						if (elp_debug >= 3)
+							printk("%s: packet received\n", dev->name);
+					}
+					if (dev->start && !start_receive(dev, &adapter->itx_pcb)) 
+						if (elp_debug >= 2)
+							printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
+					if (elp_debug >= 3)
+					printk("%s: receive procedure complete\n", dev->name);
+
+					break;
+
+				/*
+				 * 82586 configured correctly
+				 */
+				case CMD_CONFIGURE_82586_RESPONSE:
+					adapter->got[CMD_CONFIGURE_82586] = 1;
+					if (elp_debug >= 3)
+						printk("%s: interrupt - configure response received\n", dev->name);
+					break;
+
+				/*
+				 * Adapter memory configuration
+				 */
+				case CMD_CONFIGURE_ADAPTER_RESPONSE:
+					adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Adapter memory configuration %s.\n",dev->name,
+							adapter->irx_pcb.data.failed?"failed":"succeeded");
+					break;
+
+				/*
+				 * Multicast list loading
+				 */
+				case CMD_LOAD_MULTICAST_RESPONSE:
+					adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Multicast address list loading %s.\n",dev->name,
+							adapter->irx_pcb.data.failed?"failed":"succeeded");
+					break;
+
+				/*
+				 * Station address setting
+				 */
+				case CMD_SET_ADDRESS_RESPONSE:
+					adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Ethernet address setting %s.\n",dev->name,
+							adapter->irx_pcb.data.failed?"failed":"succeeded");
+					break;
+
+
+				/*
+				 * received board statistics
+				 */
+				case CMD_NETWORK_STATISTICS_RESPONSE:
+					adapter->stats.rx_packets += swapd(adapter->irx_pcb.data.netstat.tot_recv);
+					adapter->stats.tx_packets += swapd(adapter->irx_pcb.data.netstat.tot_xmit);
+					adapter->stats.rx_crc_errors += swapw(adapter->irx_pcb.data.netstat.err_CRC);
+					adapter->stats.rx_frame_errors += swapw(adapter->irx_pcb.data.netstat.err_align);
+					adapter->stats.rx_fifo_errors += swapw(adapter->irx_pcb.data.netstat.err_ovrrun);
+					adapter->got[CMD_NETWORK_STATISTICS] = 1;
+					if (elp_debug >= 3)
+						printk("%s: interrupt - statistics response received\n", dev->name);
+					break;
+
+				/*
+				 * sent a packet
+				 */
+				case CMD_TRANSMIT_PACKET_COMPLETE:
+					if (elp_debug >= 3) 
+					printk("%s: interrupt - packet sent\n", dev->name);
+					if (dev->start == 0)
+						break;
+					if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
+						if (elp_debug >= 2)
+							printk("%s: interrupt - error sending packet %4.4x\n",
+								dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
+					dev->tbusy = 0;
+					mark_bh(NET_BH);
+					break;
+
+				/*
+				 * some unknown PCB
+				 */
+				default:
+					printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+					break;
+			}
+		} else {
+			printk("%s: failed to read PCB on interrupt\n", dev->name);
+			adapter_reset(dev);
+		}
+	}
+
+	/*
+	 * indicate no longer in interrupt routine
+	 */
+	dev->interrupt = 0;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int
+elp_open (struct net_device *dev)
+{
+	elp_device * adapter;
+
+	CHECK_NULL(dev);
+
+	adapter = dev->priv;
+
+	if (elp_debug >= 3)  
+		printk("%s: request to open device\n", dev->name);
+
+	/*
+	 * make sure we actually found the device
+	 */
+	if (adapter == NULL) {
+		printk("%s: Opening a non-existent physical device\n", dev->name);
+		return -EAGAIN;
+	}
+
+	/*
+	 * disable interrupts on the board
+	 */
+	outb_control(0x00, dev->base_addr);
+
+	/*
+	 * clear any pending interrupts
+	 */
+	inb_command(dev->base_addr);
+	adapter_reset(dev);
+
+	/*
+	 * interrupt routine not entered
+	 */
+	dev->interrupt = 0;
+
+	/*
+	 *  transmitter not busy 
+	 */
+	dev->tbusy = 0;
+
+	/*
+	 * make sure we can find the device header given the interrupt number
+	 */
+
+	/*
+	 * install our interrupt service routine
+	 */
+	if (request_irq(dev->irq, elp_interrupt, 0, "3c505",dev)) {
+		return -EAGAIN;
+	}
+
+	/*
+	 * enable interrupts on the board
+	 */
+	outb_control(CMDE, dev->base_addr);
+
+	/*
+	 * device is now officially open!
+	 */
+	dev->start = 1;
+
+	/*
+	 * configure adapter memory: we need 10 multicast addresses, default==0
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 3c505 memory configuration command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+	adapter->tx_pcb.data.memconf.cmd_q = swapw(10);
+	adapter->tx_pcb.data.memconf.rcv_q = swapw(20);
+	adapter->tx_pcb.data.memconf.mcast = swapw(10);
+	adapter->tx_pcb.data.memconf.frame = swapw(20);
+	adapter->tx_pcb.data.memconf.rcv_b = swapw(20);
+	adapter->tx_pcb.data.memconf.progs = swapw(0);
+	adapter->tx_pcb.length = sizeof(struct Memconf);
+	adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send memory configuration command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+			;
+		if (jiffies >= timeout)
+			TIMEOUT_MSG(__LINE__);
+	}
+
+
+	/*
+	 * configure adapter to receive broadcast messages and wait for response
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 82586 configure command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+	adapter->tx_pcb.data.configure = swapw(NO_LOOPBACK | RECV_BROAD);
+	adapter->tx_pcb.length  = 2;
+	adapter->got[CMD_CONFIGURE_82586] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send 82586 configure command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+			;
+		if (jiffies >= timeout)
+			TIMEOUT_MSG(__LINE__);
+	}
+
+	/*
+	 * queue receive commands to provide buffering
+	 */
+	if (!start_receive(dev, &adapter->tx_pcb))
+		printk("%s: start receive command failed \n", dev->name);
+	if (elp_debug >= 3)
+		printk("%s: start receive command sent\n", dev->name);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;			/* Always succeed */
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static int
+send_packet (struct net_device * dev, unsigned char * ptr, int len)
+{
+	int i;
+	int timeout = 0;
+	elp_device * adapter;
+
+	/*
+	 * make sure the length is even and no shorter than 60 bytes
+	 */
+	unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+
+	CHECK_NULL(dev);
+	CHECK_NULL(ptr);
+
+	adapter = dev->priv;
+
+	if (nlen < len)
+		printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
+	/*
+	 * send the adapter a transmit packet command. Ignore segment and offset
+	 * and make sure the length is even
+	 */
+	adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+	adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+	adapter->tx_pcb.data.xmit_pkt.buf_ofs
+		= adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+	adapter->tx_pcb.data.xmit_pkt.pkt_len = swapw(nlen);
+	if (!send_pcb(dev, &adapter->tx_pcb)) {
+		return FALSE;
+	}
+
+	/*
+	 * write data to the adapter
+	 */
+	cli();
+	for (i = 0; i < (nlen/2);i++) {
+		while (((inb_status(dev->base_addr)&HRDY) == 0)
+		       && (timeout++ < 20000))
+			;
+		if (timeout >= 20000) {
+			sti();
+			printk("%s: timeout at %s(%d) writing word %d of %d ***\n",
+				dev->name,filename,__LINE__, i, nlen/2);
+			return FALSE;
+		}
+
+		outw_data(*(short *)ptr, dev->base_addr);
+		ptr +=2;
+	}
+	sti();
+
+	return TRUE;
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ *    return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static int
+elp_start_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+	CHECK_NULL(dev);
+
+#if 0
+	/*
+	 * not sure what this does, but the 3c509 driver does it, so...
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+#endif
+
+	/*
+	 * if we ended up with a munged length, don't send it
+	 */
+	if (skb->len <= 0)
+		return 0;
+
+	if (elp_debug >= 3)
+		printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
+
+	/*
+	 * if the transmitter is still busy, we have a transmit timeout...
+	 */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		int stat;
+		if (tickssofar < 50) /* was 500, AJT */
+			return 1;
+		printk("%s: transmit timed out, not resetting adapter\n", dev->name);
+		if (((stat=inb_status(dev->base_addr))&ACRF) != 0) 
+			printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
+		printk("%s: status %#02x\n", dev->name, stat);
+		dev->trans_start = jiffies;
+		dev->tbusy = 0;
+	}
+
+	/*
+	 * send the packet at skb->data for skb->len
+	 */
+	if (!send_packet(dev, skb->data, skb->len)) {
+		printk("%s: send packet PCB failed\n", dev->name);
+		return 1;
+	}
+
+	if (elp_debug >= 3)
+		printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
+
+
+	/*
+	 * start the transmit timeout
+	 */
+	dev->trans_start = jiffies;
+
+	/*
+	 * the transmitter is now busy
+	 */
+	dev->tbusy = 1;
+
+	/*
+	 * free the buffer
+	 */
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct enet_statistics *
+elp_get_stats (struct net_device *dev)
+{
+	elp_device *adapter = (elp_device *) dev->priv;
+
+	if (elp_debug >= 3)
+		printk("%s: request for stats\n", dev->name);
+
+	/* If the device is closed, just return the latest stats we have,
+	   - we cannot ask from the adapter without interrupts */
+	if (!dev->start)
+		return &adapter->stats;
+
+	/* send a get statistics command to the board */
+	adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+	adapter->tx_pcb.length  = 0;
+	adapter->got[CMD_NETWORK_STATISTICS] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send get statistics command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+			;
+		if (jiffies >= timeout) {
+			TIMEOUT_MSG(__LINE__);
+			return &adapter->stats;
+		}
+	}
+
+	/* statistics are now up to date */
+	return &adapter->stats;
+}
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int
+elp_close (struct net_device *dev)
+{
+	elp_device * adapter;
+
+	CHECK_NULL(dev);
+	adapter = dev->priv;
+	CHECK_NULL(adapter);
+
+	if (elp_debug >= 3)
+		printk("%s: request to close device\n", dev->name);
+
+	/* Someone may request the device statistic information even when
+	 * the interface is closed. The following will update the statistics
+	 * structure in the driver, so we'll be able to give current statistics.
+	 */
+	(void) elp_get_stats(dev);
+
+	/*
+	 * disable interrupts on the board
+	 */
+	outb_control(0x00, dev->base_addr);
+
+	/*
+	 *  flag transmitter as busy (i.e. not available)
+	 */
+	dev->tbusy = 1;
+
+	/*
+	 *  indicate device is closed
+	 */
+	dev->start = 0;
+
+	/*
+	 * release the IRQ
+	 */
+	free_irq(dev->irq,NULL);
+
+	/*
+	 * and we no longer have to map irq to dev either
+	 */
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void
+elp_set_mc_list (struct net_device *dev)
+{
+	elp_device *adapter = (elp_device *) dev->priv;
+	struct dev_mc_list *dmi=dev->mc_list;
+	int i;
+
+	if (elp_debug >= 3)
+		printk("%s: request to set multicast list\n", dev->name);
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
+	{
+		/* send a "load multicast list" command to the board, max 10 addrs/cmd */
+		/* if num_addrs==0 the list will be cleared */
+		adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+		adapter->tx_pcb.length  = swapw(6*dev->mc_count);
+		for (i=0;i<dev->mc_count;i++)
+		{
+			memcpy(adapter->tx_pcb.data.multicast[i], dmi->dmi_addr,6);
+			dmi=dmi->next;
+		}
+		adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+		if (!send_pcb(dev, &adapter->tx_pcb))
+			printk("%s: couldn't send set_multicast command\n", dev->name);
+		else {
+			int timeout = jiffies + TIMEOUT;
+			while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+				;
+			if (jiffies >= timeout) {
+				TIMEOUT_MSG(__LINE__);
+			}
+		}
+		if (dev->mc_count)
+			adapter->tx_pcb.data.configure = swapw(NO_LOOPBACK | RECV_BROAD | RECV_MULTI);
+		else /* num_addrs == 0 */
+			adapter->tx_pcb.data.configure = swapw(NO_LOOPBACK | RECV_BROAD);
+	} 
+	else
+		adapter->tx_pcb.data.configure = swapw(NO_LOOPBACK | RECV_PROMISC);
+	/*
+	 * configure adapter to receive messages (as specified above)
+	 * and wait for response
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 82586 configure command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+	adapter->tx_pcb.length  = 2;
+	adapter->got[CMD_CONFIGURE_82586]  = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send 82586 configure command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+			;
+		if (jiffies >= timeout)
+			TIMEOUT_MSG(__LINE__);
+	}
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static void
+elp_init (struct net_device *dev)
+{
+	elp_device * adapter;
+
+	CHECK_NULL(dev);
+
+	/*
+	 * set ptrs to various functions
+	 */
+	dev->open = elp_open;	/* local */
+	dev->stop = elp_close;	/* local */
+	dev->get_stats = elp_get_stats;	/* local */
+	dev->hard_start_xmit = elp_start_xmit;	/* local */
+	dev->set_multicast_list = elp_set_mc_list;	/* local */
+
+	/* Setup the generic properties */
+	ether_setup(dev);
+
+	/*
+	 * setup ptr to adapter specific information
+	 */
+	adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+	CHECK_NULL(adapter);
+	if (adapter == NULL)
+		return;
+	memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
+
+	/*
+	 * memory information
+	 */
+	dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0;
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int
+elp_sense (struct net_device * dev)
+{
+	int timeout;
+	int addr=dev->base_addr;
+	const char *name=dev->name;
+	long flags;
+	byte orig_HCR, orig_HSR;
+
+#if 0
+	if (check_region(addr, 0xf)) 
+	  return -1;  
+#endif
+
+	orig_HCR=inb_control(addr);
+	orig_HSR=inb_status(addr);
+
+	if (elp_debug > 0)
+		printk(search_msg, name, addr);
+
+	if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+	    ((orig_HCR & DIR) != (orig_HSR & DIR))) {
+		if (elp_debug > 0)
+			printk(notfound_msg, 1);
+		return -1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+	}
+
+	/* Enable interrupts - we need timers! */
+	save_flags(flags);
+	sti();
+
+	/* Wait for a while; the adapter may still be booting up */
+	if (elp_debug > 0)
+		printk(stilllooking_msg);
+	for (timeout = jiffies + (100 * 15); jiffies <= timeout; ) 
+		if (GET_ASF(addr) != ASF_PCB_END)
+			break;
+
+	if (orig_HCR & DIR) {
+		/* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+		outb_control(orig_HCR & ~DIR,addr);
+		timeout = jiffies+30;
+		while (jiffies < timeout)
+			;
+		restore_flags(flags);
+		if (inb_status(addr) & DIR) {
+			outb_control(orig_HCR,addr);
+			if (elp_debug > 0)
+				printk(notfound_msg, 2);
+			return -1;
+		}
+	} else {
+		/* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+		outb_control(orig_HCR | DIR,addr);
+		timeout = jiffies+300;
+		while (jiffies < timeout)
+			;
+		restore_flags(flags);
+		if (!(inb_status(addr) & DIR)) {
+			outb_control(orig_HCR,addr);
+			if (elp_debug > 0)
+				printk(notfound_msg, 3);
+			return -1;
+		}
+	}
+	/*
+	 * It certainly looks like a 3c505. If it has DMA enabled, it needs
+	 * a hard reset. Also, do a hard reset if selected at the compile time.
+	 */
+	if (elp_debug > 0)
+			printk(found_msg);
+
+	if (((orig_HCR==0x35) && (orig_HSR==0x5b)) || ELP_NEED_HARD_RESET)
+		adapter_hard_reset(dev);
+	return 0;
+}
+
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int
+elp_autodetect (struct net_device * dev)
+{
+	int idx=0;
+
+	/* if base address set, then only check that address
+	otherwise, run through the table */
+	if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */
+		if (elp_sense(dev) == 0)
+			return dev->base_addr;
+	} else while ( (dev->base_addr=addr_list[idx++]) ) {
+		if (elp_sense(dev) == 0)
+			return dev->base_addr;
+	}
+
+	/* could not find an adapter */
+	if (elp_debug > 0)
+		printk(couldnot_msg, dev->name);
+
+	return 0; /* Because of this, the layer above will return -ENODEV */
+}
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ *
+ ******************************************************/
+
+int
+elplus_probe (struct net_device *dev)
+{
+	elp_device adapter;
+	int i;
+
+	CHECK_NULL(dev);
+
+	/*
+	 *  setup adapter structure
+	 */
+
+	dev->base_addr = elp_autodetect(dev);
+	if ( !(dev->base_addr) )
+		return -ENODEV;
+
+	/*
+	 *  As we enter here from bootup, the adapter should have IRQs enabled,
+	 *  but we can as well enable them anyway.
+	 */
+	outb_control(inb_control(dev->base_addr) | CMDE, dev->base_addr);
+#if 0
+	autoirq_setup(0);
+#endif
+
+	/*
+	 * use ethernet address command to probe for board in polled mode
+	 * (this also makes us the IRQ that we need for automatic detection)
+	 */
+	adapter.tx_pcb.command = CMD_STATION_ADDRESS;
+	adapter.tx_pcb.length  = 0;
+	if (!send_pcb   (dev, &adapter.tx_pcb) ||
+	    !receive_pcb(dev, &adapter.rx_pcb) ||
+	    (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+	    (adapter.rx_pcb.length != 6)) {
+		printk("%s: not responding to first PCB\n", dev->name);
+		return -ENODEV;
+	}
+
+	dev->irq=10;
+
+#if 0
+	if (dev->irq) { /* Is there a preset IRQ? */
+		if (dev->irq != autoirq_report(0)) {
+			printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+			return -ENODEV;
+		}
+		/* if dev->irq == autoirq_report(0), all is well */
+	} else /* No preset IRQ; just use what we can detect */
+		dev->irq=autoirq_report(0);
+#endif
+	switch (dev->irq) { /* Legal, sane? */
+		case 0:
+			printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+			printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+			return -ENODEV;
+		case 1:
+		case 6:
+		case 8:
+		case 13: 
+			printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+			       dev->name, dev->irq);
+			return -ENODEV;
+	}
+	/*
+	 *  Now we have the IRQ number so we can disable the interrupts from
+	 *  the board until the board is opened.
+	 */
+	outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr);
+
+	/*
+	 * copy ethernet address into structure
+	 */
+	for (i = 0; i < 6; i++) 
+		dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
+
+	/*
+	 * print remainder of startup message
+	 */
+	printk("%s: 3c505 card found at I/O %#lx using IRQ%d"
+	       " has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       dev->name, dev->base_addr, dev->irq,
+	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+	/*
+	 * and reserve the address region
+	 */
+	request_region(dev->base_addr, ELP_IO_EXTENT, "3c505");
+
+	/*
+	 * initialise the device
+	 */
+	elp_init(dev);
+	return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct net_device dev_3c505 = {
+	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, elplus_probe };
+
+int io = 0x300;
+int irq = 0;
+
+int init_module(void)
+{
+	if (io == 0)
+		printk("3c505: You should not use auto-probing with insmod!\n");
+	dev_3c505.base_addr = io;
+	dev_3c505.irq       = irq;
+	if (register_netdev(&dev_3c505) != 0) {
+		printk("3c505: register_netdev() returned non-zero.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	unregister_netdev(&dev_3c505);
+	kfree(dev_3c505.priv);
+	dev_3c505.priv = NULL;
+
+	/* If we don't do this, we can't re-insmod it later. */
+	release_region(dev_3c505.base_addr, ELP_IO_EXTENT);
+}
+#endif /* MODULE */
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/net/3c505_pio_apollo.h	Fri Oct 15 05:24:38 1999
@@ -0,0 +1,245 @@
+/*****************************************************************
+ *
+ *  defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+/*
+ * I/O register offsets
+ */
+#define	PORT_COMMAND	0x00	/* read/write, 8-bit */
+#define	PORT_STATUS	0x02	/* read only, 8-bit */
+#define	PORT_AUXDMA	0x02	/* write only, 8-bit */
+#define	PORT_DATA	0x04	/* read/write, 16-bit */
+#define	PORT_CONTROL	0x06	/* read/write, 8-bit */
+
+#define ELP_IO_EXTENT	0x10	/* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define	ATTN	0x80	/* attention */
+#define	FLSH	0x40	/* flush data register */
+#define DMAE	0x20	/* DMA enable */
+#define DIR	0x10	/* direction */
+#define	TCEN	0x08	/* terminal count interrupt enable */
+#define	CMDE	0x04	/* command register interrupt enable */
+#define	HSF2	0x02	/* host status flag 2 */
+#define	HSF1	0x01	/* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define	HSF_PCB_ACK	HSF1
+#define	HSF_PCB_NAK	HSF2
+#define	HSF_PCB_END	(HSF2|HSF1)
+#define	HSF_PCB_MASK	(HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define	HRDY	0x80	/* data register ready */
+#define	HCRE	0x40	/* command register empty */
+#define	ACRF	0x20	/* adapter command register full */
+/* #define DIR 	0x10	direction - same as in control register */
+#define	DONE	0x08	/* DMA done */
+#define	ASF3	0x04	/* adapter status flag 3 */
+#define	ASF2	0x02	/* adapter status flag 2 */
+#define	ASF1	0x01	/* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define	ASF_PCB_ACK	ASF1
+#define	ASF_PCB_NAK	ASF2
+#define	ASF_PCB_END	(ASF2|ASF1)
+#define	ASF_PCB_MASK	(ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define	DMA_BRST	0x01	/* DMA burst */
+
+/*
+ * maximum amount of data data allowed in a PCB
+ */
+#define	MAX_PCB_DATA	62
+
+/*****************************************************************
+ *
+ *  timeout value
+ *	this is a rough value used for loops to stop them from 
+ *	locking up the whole machine in the case of failure or
+ *	error conditions
+ *
+ *****************************************************************/
+
+#define	TIMEOUT	300
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+  /*
+   * host PCB commands
+   */
+  CMD_CONFIGURE_ADAPTER_MEMORY	= 0x01,
+  CMD_CONFIGURE_82586		= 0x02,
+  CMD_STATION_ADDRESS		= 0x03,
+  CMD_DMA_DOWNLOAD		= 0x04,
+  CMD_DMA_UPLOAD		= 0x05,
+  CMD_PIO_DOWNLOAD		= 0x06,
+  CMD_PIO_UPLOAD		= 0x07,
+  CMD_RECEIVE_PACKET		= 0x08,
+  CMD_TRANSMIT_PACKET		= 0x09,
+  CMD_NETWORK_STATISTICS	= 0x0a,
+  CMD_LOAD_MULTICAST_LIST	= 0x0b,
+  CMD_CLEAR_PROGRAM		= 0x0c,
+  CMD_DOWNLOAD_PROGRAM		= 0x0d,
+  CMD_EXECUTE_PROGRAM		= 0x0e,
+  CMD_SELF_TEST			= 0x0f,
+  CMD_SET_STATION_ADDRESS	= 0x10,
+  CMD_ADAPTER_INFO		= 0x11,
+  NUM_TRANSMIT_CMDS,
+
+  /*
+   * adapter PCB commands
+   */
+  CMD_CONFIGURE_ADAPTER_RESPONSE	= 0x31,
+  CMD_CONFIGURE_82586_RESPONSE		= 0x32,
+  CMD_ADDRESS_RESPONSE			= 0x33,
+  CMD_DOWNLOAD_DATA_REQUEST		= 0x34,
+  CMD_UPLOAD_DATA_REQUEST		= 0x35,
+  CMD_RECEIVE_PACKET_COMPLETE		= 0x38,
+  CMD_TRANSMIT_PACKET_COMPLETE		= 0x39,
+  CMD_NETWORK_STATISTICS_RESPONSE	= 0x3a,
+  CMD_LOAD_MULTICAST_RESPONSE		= 0x3b,
+  CMD_CLEAR_PROGRAM_RESPONSE		= 0x3c,
+  CMD_DOWNLOAD_PROGRAM_RESPONSE		= 0x3d,
+  CMD_EXECUTE_RESPONSE			= 0x3e,
+  CMD_SELF_TEST_RESPONSE		= 0x3f,
+  CMD_SET_ADDRESS_RESPONSE		= 0x40,
+  CMD_ADAPTER_INFO_RESPONSE		= 0x41
+};
+
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char         byte;
+typedef unsigned short int    word;
+typedef unsigned long int     dword;
+
+/* Data structures */
+struct Memconf {
+	word	cmd_q,
+		rcv_q,
+		mcast,
+		frame,
+		rcv_b,
+		progs;
+};
+
+struct Rcv_pkt {
+	word	buf_ofs,
+		buf_seg,
+		buf_len,
+		timeout;
+};
+
+struct Xmit_pkt {
+	word	buf_ofs,
+		buf_seg,
+		pkt_len;
+};
+
+struct Rcv_resp {
+	word	buf_ofs,
+		buf_seg,
+		buf_len,
+		pkt_len,
+		timeout,
+		status;
+	dword	timetag;
+};
+
+struct Xmit_resp {
+	word	buf_ofs,
+		buf_seg,
+		c_stat,
+		status;
+};
+
+
+struct Netstat {
+	dword	tot_recv,
+		tot_xmit;
+	word	err_CRC,
+		err_align,
+		err_res,
+		err_ovrrun;
+};
+
+
+struct Selftest {
+	word	error;
+	union {
+		word ROM_cksum;
+		struct {
+			word ofs, seg;
+		} RAM;
+		word i82586;
+	} failure;
+};
+
+struct Info {
+	byte	minor_vers,
+		major_vers;
+	word	ROM_cksum,
+		RAM_sz,
+		free_ofs,
+		free_seg;
+};
+
+struct Memdump {
+       word size,
+            off,
+            seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+ethernet data, which has different packaging.)
+*/
+typedef struct {
+	byte	command;
+	byte	length;
+	union	{
+		struct Memconf		memconf;
+		word			configure;
+		struct Rcv_pkt		rcv_pkt;
+		struct Xmit_pkt		xmit_pkt;
+		byte			multicast[10][6];
+		byte			eth_addr[6];
+		byte			failed;
+		struct Rcv_resp		rcv_resp;
+		struct Xmit_resp	xmit_resp;
+		struct Netstat		netstat;
+		struct Selftest		selftest;
+		struct Info		info;
+		struct Memdump    	memdump;
+		byte			raw[62];
+	} data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION	0x00
+#define RECV_BROAD	0x01
+#define RECV_MULTI	0x02
+#define RECV_PROMISC	0x04
+#define NO_LOOPBACK	0x00
+#define INT_LOOPBACK	0x08
+#define EXT_LOOPBACK	0x10
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/net/3c505_dma_apollo.c	Sun Oct 17 05:15:34 1999
@@ -0,0 +1,1757 @@
+/*
+ * Linux Ethernet device driver for the 3Com Etherlink Plus (3C505)
+ *      By Craig Southeren, Juha Laiho and Philip Blundell
+ *
+ * 3c505.c      This module implements an interface to the 3Com
+ *              Etherlink Plus (3c505) Ethernet card. Linux device
+ *              driver interface reverse engineered from the Linux 3C509
+ *              device drivers. Some 3C505 information gleaned from
+ *              the Crynwr packet driver. Still this driver would not
+ *              be here without 3C505 technical reference provided by
+ *              3Com.
+ *
+ * $Id: 3c505.c,v 1.10 1996/04/16 13:06:27 phil Exp $
+ *
+ * Authors:     Linux 3c505 device driver by
+ *                      Craig Southeren, <craigs@ineluki.apana.org.au>
+ *              Final debugging by
+ *                      Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ *              Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
+ *                      Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ *              Linux 3C509 driver by
+ *                      Donald Becker, <becker@super.org>
+ *              Crynwr packet driver by
+ *                      Krishnan Gopalan and Gregg Stefancik,
+ *                      Clemson University Engineering Computer Operations.
+ *                      Portions of the code have been adapted from the 3c505
+ *                         driver for NCSA Telnet by Bruce Orchard and later
+ *                         modified by Warren Van Houten and krus@diku.dk.
+ *              3C505 technical information provided by
+ *                      Terry Murphy, of 3Com Network Adapter Division
+ *              Linux 1.3.0 changes by
+ *                      Alan Cox <Alan.Cox@linux.org>
+ *              More debugging, DMA support, currently maintained by
+ *                      Philip Blundell <Philip.Blundell@pobox.com>
+ *              Multicard/soft configurable dma channel/rev 2 hardware support
+ *                      by Christopher Collins <ccollins@pcug.org.au>
+ */
+
+/* Theory of operation:
+ *
+ * The 3c505 is quite an intelligent board.  All communication with it is done
+ * by means of Primary Command Blocks (PCBs); these are transferred using PIO
+ * through the command register.  The card has 256k of on-board RAM, which is
+ * used to buffer received packets.  It might seem at first that more buffers
+ * are better, but in fact this isn't true.  From my tests, it seems that
+ * more than about 10 buffers are unnecessary, and there is a noticeable
+ * performance hit in having more active on the card.  So the majority of the
+ * card's memory isn't, in fact, used.  Sadly, the card only has one transmit
+ * buffer and, short of loading our own firmware into it (which is what some
+ * drivers resort to) there's nothing we can do about this.
+ *
+ * We keep up to 4 "receive packet" commands active on the board at a time.
+ * When a packet comes in, so long as there is a receive command active, the
+ * board will send us a "packet received" PCB and then add the data for that
+ * packet to the DMA queue.  If a DMA transfer is not already in progress, we
+ * set one up to start uploading the data.  We have to maintain a list of
+ * backlogged receive packets, because the card may decide to tell us about
+ * a newly-arrived packet at any time, and we may not be able to start a DMA
+ * transfer immediately (ie one may already be going on).  We can't NAK the
+ * PCB, because then it would throw the packet away.
+ *
+ * Trying to send a PCB to the card at the wrong moment seems to have bad
+ * effects.  If we send it a transmit PCB while a receive DMA is happening,
+ * it will just NAK the PCB and so we will have wasted our time.  Worse, it
+ * sometimes seems to interrupt the transfer.  The majority of the low-level
+ * code is protected by one huge semaphore -- "busy" -- which is set whenever
+ * it probably isn't safe to do anything to the card.  The receive routine
+ * must gain a lock on "busy" before it can start a DMA transfer, and the
+ * transmit routine must gain a lock before it sends the first PCB to the card.
+ * The send_pcb() routine also has an internal semaphore to protect it against
+ * being re-entered (which would be disastrous) -- this is needed because
+ * several things can happen asynchronously (re-priming the receiver and
+ * asking the card for statistics, for example).  send_pcb() will also refuse
+ * to talk to the card at all if a DMA upload is happening.  The higher-level
+ * networking code will reschedule a later retry if some part of the driver
+ * is blocked.  In practice, this doesn't seem to happen very often.
+ */
+
+/* This driver may now work with revision 2.x hardware, since all the read
+ * operations on the HCR have been removed (we now keep our own softcopy).
+ * But I don't have an old card to test it on.
+ *
+ * This has had the bad effect that the autoprobe routine is now a bit
+ * less friendly to other devices.  However, it was never very good.
+ * before, so I doubt it will hurt anybody.
+ */
+
+/* The driver is a mess.  I took Craig's and Juha's code, and hacked it firstly
+ * to make it more reliable, and secondly to add DMA mode.  Many things could
+ * probably be done better; the concurrency protection is particularly awful.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/apollodma.h>
+#include <asm/apollohw.h>
+#include <asm/virtconvert.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include "3c505_dma_apollo.h"
+
+#define swapw(x)        ((((x)>>8)&0x00ffU)|(((x)<<8)&0xff00U))
+#define swapd(x)        ((((x)>>24)&0xffU)|(((x)>>8) & 0xff00U) \
+			|(((x)<<8)&0xff0000U)|(((x)<<24)&0xff000000U))
+
+/*********************************************************
+ *
+ *  define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static const char *filename = __FILE__;
+
+static const char *timeout_msg = "*** timeout at %s:%s (line %d) ***\n";
+#define TIMEOUT_MSG(lineno) \
+	printk(timeout_msg, filename,__FUNCTION__,(lineno))
+
+static const char *invalid_pcb_msg =
+"*** invalid pcb length %d at %s:%s (line %d) ***\n";
+#define INVALID_PCB_MSG(len) \
+	printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
+
+static const char *search_msg = "%s: Looking for 3c505 adapter at address %#x...";
+
+static const char *stilllooking_msg = "still looking...";
+
+static const char *found_msg = "found.\n";
+
+static const char *notfound_msg = "not found (reason = %d)\n";
+
+static const char *couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ *  various other debug stuff
+ *
+ *********************************************************/
+
+#define ELP_DEBUG 3
+#ifdef ELP_DEBUG
+static const int elp_debug = ELP_DEBUG;
+#else
+static const int elp_debug = 0;
+#endif
+
+/*
+ *  0 = no messages (well, some)
+ *  1 = messages when high level commands performed
+ *  2 = messages when low level commands performed
+ *  3 = messages when interrupts received
+ */
+
+/*****************************************************************
+ *
+ * useful macros
+ *
+ *****************************************************************/
+
+#ifndef	TRUE
+#define	TRUE	1
+#endif
+
+#ifndef	FALSE
+#define	FALSE	0
+#endif
+
+
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+static const int addr_list[] __initdata = {isaIO2mem(0x300), 0};
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order(unsigned long size)
+{
+	int order;
+
+	size = (size - 1) >> (PAGE_SHIFT - 1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+	int order = __get_order(size);
+
+	return __get_dma_pages(GFP_KERNEL, order);
+}
+
+
+/*****************************************************************
+ *
+ * Functions for I/O (note the inline !)
+ *
+ *****************************************************************/
+
+static inline unsigned char inb_status(unsigned int base_addr)
+{
+	return inb(base_addr + PORT_STATUS);
+}
+
+static inline int inb_command(unsigned int base_addr)
+{
+	return inb(base_addr + PORT_COMMAND);
+}
+
+static inline void outb_control(unsigned char val, struct net_device *dev)
+{
+	outb(val, dev->base_addr + PORT_CONTROL);
+	((elp_device *)(dev->priv))->hcr_val = val;
+}
+
+#define HCR_VAL(x)   (((elp_device *)((x)->priv))->hcr_val)
+
+static inline void outb_command(unsigned char val, unsigned int base_addr)
+{
+	outb(val, base_addr + PORT_COMMAND);
+}
+
+static inline unsigned int inw_data(unsigned int base_addr)
+{
+	return inw(base_addr + PORT_DATA);
+}
+
+static inline void outw_data(unsigned int val, unsigned int base_addr)
+{
+	outw(val, base_addr + PORT_DATA);
+}
+
+static inline unsigned int backlog_next(unsigned int n)
+{
+	return (n + 1) % BACKLOG_SIZE;
+}
+
+/*****************************************************************
+ *
+ *  useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define	GET_ASF(addr) \
+	(get_status(addr)&ASF_PCB_MASK)
+
+static inline int get_status(unsigned int base_addr)
+{
+	int timeout = jiffies + 10*HZ/100;
+	register int stat1;
+	do {
+		stat1 = inb_status(base_addr);
+	} while (stat1 != inb_status(base_addr) && time_before(jiffies, timeout));
+	if (time_after_eq(jiffies, timeout))
+		TIMEOUT_MSG(__LINE__);
+	return stat1;
+}
+
+static inline void set_hsf(struct net_device *dev, int hsf)
+{
+	cli();
+	outb_control((HCR_VAL(dev) & ~HSF_PCB_MASK) | hsf, dev);
+	sti();
+}
+
+static int start_receive(struct net_device *, pcb_struct *);
+
+inline static void adapter_reset(struct net_device *dev)
+{
+	int timeout;
+	elp_device *adapter = dev->priv;
+	unsigned char orig_hcr = adapter->hcr_val;
+
+	outb_control(0, dev);
+
+	if (inb_status(dev->base_addr) & ACRF) {
+		do {
+			inb_command(dev->base_addr);
+			timeout = jiffies + 2*HZ/100;
+			while (time_before_eq(jiffies, timeout) && !(inb_status(dev->base_addr) & ACRF));
+		} while (inb_status(dev->base_addr) & ACRF);
+		set_hsf(dev, HSF_PCB_NAK);
+	}
+	outb_control(adapter->hcr_val | ATTN | DIR, dev);
+	timeout = jiffies + 1*HZ/100;
+	while (time_before_eq(jiffies, timeout));
+	outb_control(adapter->hcr_val & ~ATTN, dev);
+	timeout = jiffies + 1*HZ/100;
+	while (time_before_eq(jiffies, timeout));
+	outb_control(adapter->hcr_val | FLSH, dev);
+	timeout = jiffies + 1*HZ/100;
+	while (time_before_eq(jiffies, timeout));
+	outb_control(adapter->hcr_val & ~FLSH, dev);
+	timeout = jiffies + 1*HZ/100;
+	while (time_before_eq(jiffies, timeout));
+
+	outb_control(orig_hcr, dev);
+	if (!start_receive(dev, &adapter->tx_pcb))
+		printk("%s: start receive command failed \n", dev->name);
+}
+
+/* Check to make sure that a DMA transfer hasn't timed out.  This should
+ * never happen in theory, but seems to occur occasionally if the card gets
+ * prodded at the wrong time.
+ */
+static inline void check_3c505_dma(struct net_device *dev)
+{
+	elp_device *adapter = dev->priv;
+	if (adapter->dmaing && time_after(jiffies, adapter->current_dma.start_time + 10)) {
+		unsigned long flags, f;
+		printk("%s: DMA %s timed out, %d bytes left\n", dev->name, adapter->current_dma.direction ? "download" : "upload", get_dma_residue(dev->dma));
+		save_flags(flags);
+		cli();
+		adapter->dmaing = 0;
+		adapter->busy = 0;
+		
+		f=claim_dma_lock();
+		disable_dma(dev->dma);
+		release_dma_lock(f);
+		
+		if (adapter->rx_active)
+			adapter->rx_active--;
+		outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev);
+		restore_flags(flags);
+	}
+}
+
+/* Primitive functions used by send_pcb() */
+static inline unsigned int send_pcb_slow(unsigned int base_addr, unsigned char byte)
+{
+	unsigned int timeout;
+	outb_command(byte, base_addr);
+	for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) {
+		if (inb_status(base_addr) & HCRE)
+			return FALSE;
+	}
+	printk("3c505: send_pcb_slow timed out\n");
+	return TRUE;
+}
+
+static inline unsigned int send_pcb_fast(unsigned int base_addr, unsigned char byte)
+{
+	unsigned int timeout;
+	outb_command(byte, base_addr);
+	for (timeout = 0; timeout < 40000; timeout++) {
+		if (inb_status(base_addr) & HCRE)
+			return FALSE;
+	}
+	printk("3c505: send_pcb_fast timed out\n");
+	return TRUE;
+}
+
+/* Check to see if the receiver needs restarting, and kick it if so */
+static inline void prime_rx(struct net_device *dev)
+{
+	elp_device *adapter = dev->priv;
+	while (adapter->rx_active < ELP_RX_PCBS && dev->start) {
+		if (!start_receive(dev, &adapter->itx_pcb))
+			break;
+	}
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ *   Send a PCB to the adapter.
+ *
+ *	output byte to command reg  --<--+
+ *	wait until HCRE is non zero      |
+ *	loop until all bytes sent   -->--+
+ *	set HSF1 and HSF2 to 1
+ *	output pcb length
+ *	wait until ASF give ACK or NAK
+ *	set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+/* This can be quite slow -- the adapter is allowed to take up to 40ms
+ * to respond to the initial interrupt.
+ *
+ * We run initially with interrupts turned on, but with a semaphore set
+ * so that nobody tries to re-enter this code.  Once the first byte has
+ * gone through, we turn interrupts off and then send the others (the
+ * timeout is reduced to 500us).
+ */
+
+static int send_pcb(struct net_device *dev, pcb_struct * pcb)
+{
+	int i;
+	int timeout;
+	elp_device *adapter = dev->priv;
+
+	check_3c505_dma(dev);
+
+	if (adapter->dmaing && adapter->current_dma.direction == 0)
+		return FALSE;
+
+	/* Avoid contention */
+	if (test_and_set_bit(1, &adapter->send_pcb_semaphore)) {
+		if (elp_debug >= 3) {
+			printk("%s: send_pcb entered while threaded\n", dev->name);
+		}
+		return FALSE;
+	}
+	/*
+	 * load each byte into the command register and
+	 * wait for the HCRE bit to indicate the adapter
+	 * had read the byte
+	 */
+	set_hsf(dev, 0);
+
+	if (send_pcb_slow(dev->base_addr, pcb->command))
+		goto abort;
+
+	cli();
+
+	if (send_pcb_fast(dev->base_addr, pcb->length))
+		goto sti_abort;
+
+	for (i = 0; i < pcb->length; i++) {
+		if (send_pcb_fast(dev->base_addr, pcb->data.raw[i]))
+			goto sti_abort;
+	}
+
+	outb_control(adapter->hcr_val | 3, dev);	/* signal end of PCB */
+	outb_command(2 + pcb->length, dev->base_addr);
+
+	/* now wait for the acknowledgement */
+	sti();
+
+	for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) {
+		switch (GET_ASF(dev->base_addr)) {
+		case ASF_PCB_ACK:
+			adapter->send_pcb_semaphore = 0;
+			return TRUE;
+			break;
+		case ASF_PCB_NAK:
+#ifdef ELP_DEBUG
+			printk(KERN_DEBUG "%s: send_pcb got NAK\n", dev->name);
+#endif
+			goto abort;
+			break;
+		}
+	}
+
+	if (elp_debug >= 1)
+		printk("%s: timeout waiting for PCB acknowledge (status %02x)\n", dev->name, inb_status(dev->base_addr));
+
+      sti_abort:
+	sti();
+      abort:
+	adapter->send_pcb_semaphore = 0;
+	return FALSE;
+}
+
+
+/*****************************************************************
+ *
+ * receive_pcb
+ *   Read a PCB from the adapter
+ *
+ *	wait for ACRF to be non-zero        ---<---+
+ *	input a byte                               |
+ *	if ASF1 and ASF2 were not both one         |
+ *		before byte was read, loop      --->---+
+ *	set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static int receive_pcb(struct net_device *dev, pcb_struct * pcb)
+{
+	int i, j;
+	int total_length;
+	int stat;
+	int timeout;
+
+	elp_device *adapter = dev->priv;
+
+	set_hsf(dev, 0);
+
+	/* get the command code */
+	timeout = jiffies + 2*HZ/100;
+	while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout));
+	if (time_after_eq(jiffies, timeout)) {
+		TIMEOUT_MSG(__LINE__);
+		return FALSE;
+	}
+	pcb->command = inb_command(dev->base_addr);
+
+	/* read the data length */
+	timeout = jiffies + 3*HZ/100;
+	while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout));
+	if (time_after_eq(jiffies, timeout)) {
+		TIMEOUT_MSG(__LINE__);
+		printk("%s: status %02x\n", dev->name, stat);
+		return FALSE;
+	}
+	pcb->length = inb_command(dev->base_addr);
+
+	if (pcb->length > MAX_PCB_DATA) {
+		INVALID_PCB_MSG(pcb->length);
+		adapter_reset(dev);
+		return FALSE;
+	}
+	/* read the data */
+	cli();
+	i = 0;
+	do {
+		j = 0;
+		while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && j++ < 20000);
+		pcb->data.raw[i++] = inb_command(dev->base_addr);
+		if (i > MAX_PCB_DATA)
+			INVALID_PCB_MSG(i);
+	} while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000);
+	sti();
+	if (j >= 20000) {
+		TIMEOUT_MSG(__LINE__);
+		return FALSE;
+	}
+	/* woops, the last "data" byte was really the length! */
+	total_length = pcb->data.raw[--i];
+
+	/* safety check total length vs data length */
+	if (total_length != (pcb->length + 2)) {
+		if (elp_debug >= 2)
+			printk("%s: mangled PCB received\n", dev->name);
+		set_hsf(dev, HSF_PCB_NAK);
+		return FALSE;
+	}
+
+	if (pcb->command == CMD_RECEIVE_PACKET_COMPLETE) {
+		if (test_and_set_bit(0, (void *) &adapter->busy)) {
+			if (backlog_next(adapter->rx_backlog.in) == adapter->rx_backlog.out) {
+				set_hsf(dev, HSF_PCB_NAK);
+				printk("%s: PCB rejected, transfer in progress and backlog full\n", dev->name);
+				pcb->command = 0;
+				return TRUE;
+			} else {
+				pcb->command = 0xff;
+			}
+		}
+	}
+	set_hsf(dev, HSF_PCB_ACK);
+	return TRUE;
+}
+
+/******************************************************
+ *
+ *  queue a receive command on the adapter so we will get an
+ *  interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static int start_receive(struct net_device *dev, pcb_struct * tx_pcb)
+{
+	int status;
+	elp_device *adapter = dev->priv;
+
+	if (elp_debug >= 3)
+		printk("%s: restarting receiver\n", dev->name);
+	tx_pcb->command = CMD_RECEIVE_PACKET;
+	tx_pcb->length = sizeof(struct Rcv_pkt);
+	tx_pcb->data.rcv_pkt.buf_seg
+	    = tx_pcb->data.rcv_pkt.buf_ofs = 0;		/* Unused */
+	tx_pcb->data.rcv_pkt.buf_len = __le16_to_cpu(1600);
+	tx_pcb->data.rcv_pkt.timeout = 0;	/* set timeout to zero */
+	status = send_pcb(dev, tx_pcb);
+	if (status)
+		adapter->rx_active++;
+	return status;
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void receive_packet(struct net_device *dev, int len)
+{
+	int rlen;
+	elp_device *adapter = dev->priv;
+	void *target;
+	struct sk_buff *skb;
+	unsigned long flags;
+	unsigned short dma_addr;
+
+	rlen = (len + 1) & ~1;
+	skb = dev_alloc_skb(rlen + 2);
+
+	if (!skb) {
+		printk("%s: memory squeeze, dropping packet\n", dev->name);
+		target = adapter->dma_buffer;
+		adapter->current_dma.target = NULL;
+	} else {
+		skb_reserve(skb, 2);
+		target = skb_put(skb, rlen);
+		adapter->current_dma.target = NULL;
+	}
+
+	dma_addr=dma_map_page(virt_to_phys(target),1,DMA_16BIT);
+
+	/* if this happens, we die */
+	if (test_and_set_bit(0, (void *) &adapter->dmaing))
+		printk("%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
+
+	skb->dev = dev;
+	adapter->current_dma.direction = 0;
+	adapter->current_dma.length = rlen;
+	adapter->current_dma.skb = skb;
+	adapter->current_dma.start_time = jiffies;
+	adapter->current_dma.dma_addr = dma_addr ;
+
+	outb_control(adapter->hcr_val | DIR | TCEN | DMAE, dev);
+
+	flags=claim_dma_lock();
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	set_dma_mode(dev->dma, 0x04);	/* dma read */
+	set_dma_addr(dev->dma, dma_addr + (virt_to_phys(target) & ((1<<12)-1)));
+	set_dma_count(dev->dma, rlen);
+	enable_dma(dev->dma);
+	release_dma_lock(flags);
+
+	if (elp_debug >= 3) {
+		printk("%s: rx DMA transfer started\n", dev->name);
+	}
+
+	if (adapter->rx_active)
+		adapter->rx_active--;
+
+	if (!adapter->busy)
+		printk("%s: receive_packet called, busy not set.\n", dev->name);
+}
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static void elp_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
+{
+	int len;
+	int dlen;
+	int icount = 0;
+	struct net_device *dev;
+	elp_device *adapter;
+	int timeout;
+
+	irq-=160;
+	if (irq < 0 || irq > 15) {
+		printk("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
+		return;
+	}
+	dev = dev_id;
+
+	if (dev == NULL) {
+		printk("elp_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+	adapter = (elp_device *) dev->priv;
+
+	if (dev->interrupt) {
+		printk("%s: re-entering the interrupt handler!\n", dev->name);
+		return;
+	}
+	dev->interrupt = 1;
+
+	do {
+		/*
+		 * has a DMA transfer finished?
+		 */
+		if (inb_status(dev->base_addr) & DONE) {
+			if (!adapter->dmaing) {
+				printk("%s: phantom DMA completed\n", dev->name);
+			}
+			if (elp_debug >= 3) {
+				printk("%s: %s DMA complete, status %02x\n", dev->name, adapter->current_dma.direction ? "tx" : "rx", inb_status(dev->base_addr));
+			}
+			dma_unmap_page(adapter->current_dma.dma_addr);
+			outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR),
+				     dev);
+			if (adapter->current_dma.direction) {
+				dev_kfree_skb(adapter->current_dma.skb);
+			} else {
+				struct sk_buff *skb = adapter->current_dma.skb;
+				if (skb) {
+				  if (adapter->current_dma.target) {
+				    /* have already done the skb_put() */
+				    memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
+				  }
+				  skb->protocol = eth_type_trans(skb,dev);
+				  adapter->stats.rx_bytes += skb->len;
+{
+	int i;
+
+	printk("frame(%d) ",skb->len);
+	for(i=0;i<skb->len;i++) 
+		printk("%x ",skb->data[i]);
+
+	printk("\n");
+}
+				  netif_rx(skb);
+				}
+			}
+			adapter->dmaing = 0;
+			if (adapter->rx_backlog.in != adapter->rx_backlog.out) {
+				int t = adapter->rx_backlog.length[adapter->rx_backlog.out];
+				adapter->rx_backlog.out = backlog_next(adapter->rx_backlog.out);
+				if (elp_debug >= 2)
+					printk("%s: receiving backlogged packet (%d)\n", dev->name, t);
+				receive_packet(dev, t);
+			} else {
+				adapter->busy = 0;
+			}
+		} else {
+			/* has one timed out? */
+			check_3c505_dma(dev);
+		}
+
+		sti();
+
+		/*
+		 * receive a PCB from the adapter
+		 */
+		timeout = jiffies + 3*HZ/100;
+		while ((inb_status(dev->base_addr) & ACRF) != 0 && time_before(jiffies, timeout)) {
+			if (receive_pcb(dev, &adapter->irx_pcb)) {
+				switch (adapter->irx_pcb.command) {
+				case 0:
+					break;
+					/*
+					 * received a packet - this must be handled fast
+					 */
+				case 0xff:
+				case CMD_RECEIVE_PACKET_COMPLETE:
+					/* if the device isn't open, don't pass packets up the stack */
+					if (dev->start == 0)
+						break;
+					cli();
+					len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+					len = __le16_to_cpu(len);
+					dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+					dlen = __le16_to_cpu(dlen);
+					if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+						printk("%s: interrupt - packet not received correctly\n", dev->name);
+						sti();
+					} else {
+						if (elp_debug >= 3) {
+							sti();
+							printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+							cli();
+						}
+						if (adapter->irx_pcb.command == 0xff) {
+							if (elp_debug >= 2)
+								printk("%s: adding packet to backlog (len = %d)\n", dev->name, dlen);
+							adapter->rx_backlog.length[adapter->rx_backlog.in] = dlen;
+							adapter->rx_backlog.in = backlog_next(adapter->rx_backlog.in);
+						} else {
+							receive_packet(dev, dlen);
+						}
+						sti();
+						if (elp_debug >= 3)
+							printk("%s: packet received\n", dev->name);
+					}
+					break;
+
+					/*
+					 * 82586 configured correctly
+					 */
+				case CMD_CONFIGURE_82586_RESPONSE:
+					adapter->got[CMD_CONFIGURE_82586] = 1;
+					if (elp_debug >= 3)
+						printk("%s: interrupt - configure response received\n", dev->name);
+					break;
+
+					/*
+					 * Adapter memory configuration
+					 */
+				case CMD_CONFIGURE_ADAPTER_RESPONSE:
+					adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Adapter memory configuration %s.\n", dev->name,
+						       adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+					break;
+
+					/*
+					 * Multicast list loading
+					 */
+				case CMD_LOAD_MULTICAST_RESPONSE:
+					adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Multicast address list loading %s.\n", dev->name,
+						       adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+					break;
+
+					/*
+					 * Station address setting
+					 */
+				case CMD_SET_ADDRESS_RESPONSE:
+					adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+					if (elp_debug >= 3)
+						printk("%s: Ethernet address setting %s.\n", dev->name,
+						       adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+					break;
+
+
+					/*
+					 * received board statistics
+					 */
+				case CMD_NETWORK_STATISTICS_RESPONSE:
+					adapter->stats.rx_packets += __le32_to_cpu(adapter->irx_pcb.data.netstat.tot_recv);
+					adapter->stats.tx_packets += __le32_to_cpu(adapter->irx_pcb.data.netstat.tot_xmit);
+					adapter->stats.rx_crc_errors += __le16_to_cpu(adapter->irx_pcb.data.netstat.err_CRC);
+					adapter->stats.rx_frame_errors += __le16_to_cpu(adapter->irx_pcb.data.netstat.err_align);
+					adapter->stats.rx_fifo_errors += __le16_to_cpu(adapter->irx_pcb.data.netstat.err_ovrrun);
+					adapter->stats.rx_over_errors += __le16_to_cpu(adapter->irx_pcb.data.netstat.err_res);
+					adapter->got[CMD_NETWORK_STATISTICS] = 1;
+					if (elp_debug >= 3)
+						printk("%s: interrupt - statistics response received\n", dev->name);
+					break;
+
+					/*
+					 * sent a packet
+					 */
+				case CMD_TRANSMIT_PACKET_COMPLETE:
+					if (elp_debug >= 3)
+						printk("%s: interrupt - packet sent\n", dev->name);
+					if (dev->start == 0)
+						break;
+					switch (adapter->irx_pcb.data.xmit_resp.c_stat) {
+					case 0xffff:
+						adapter->stats.tx_aborted_errors++;
+						printk(KERN_INFO "%s: transmit timed out, network cable problem?\n", dev->name);
+						break;
+					case 0xfffe:
+						adapter->stats.tx_fifo_errors++;
+						printk(KERN_INFO "%s: transmit timed out, FIFO underrun\n", dev->name);
+						break;
+					}
+					dma_unmap_page(adapter->current_dma.dma_addr);
+					dev->tbusy = 0;
+					mark_bh(NET_BH);
+					break;
+
+					/*
+					 * some unknown PCB
+					 */
+				default:
+					printk(KERN_DEBUG "%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+					break;
+				}
+			} else {
+				printk("%s: failed to read PCB on interrupt\n", dev->name);
+				adapter_reset(dev);
+			}
+		}
+
+	} while (icount++ < 5 && (inb_status(dev->base_addr) & (ACRF | DONE)));
+
+	prime_rx(dev);
+
+	/*
+	 * indicate no longer in interrupt routine
+	 */
+	dev->interrupt = 0;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int elp_open(struct net_device *dev)
+{
+	elp_device *adapter;
+
+	adapter = dev->priv;
+
+	if (elp_debug >= 3)
+		printk("%s: request to open device\n", dev->name);
+
+	/*
+	 * make sure we actually found the device
+	 */
+	if (adapter == NULL) {
+		printk("%s: Opening a non-existent physical device\n", dev->name);
+		return -EAGAIN;
+	}
+	/*
+	 * disable interrupts on the board
+	 */
+	outb_control(0, dev);
+
+	/*
+	 * clear any pending interrupts
+	 */
+	inb_command(dev->base_addr);
+	adapter_reset(dev);
+
+	/*
+	 * interrupt routine not entered
+	 */
+	dev->interrupt = 0;
+
+	/*
+	 *  transmitter not busy
+	 */
+	dev->tbusy = 0;
+
+	/*
+	 * no receive PCBs active
+	 */
+	adapter->rx_active = 0;
+
+	adapter->busy = 0;
+	adapter->send_pcb_semaphore = 0;
+	adapter->rx_backlog.in = 0;
+	adapter->rx_backlog.out = 0;
+
+	/*
+	 * install our interrupt service routine
+	 */
+	if (request_irq(dev->irq, &elp_interrupt, 0, "3c505", dev)) {
+		return -EAGAIN;
+	}
+	if (request_dma(dev->dma, "3c505")) {
+		printk("%s: could not allocate DMA channel\n", dev->name);
+		return -EAGAIN;
+	}
+	adapter->dma_buffer = (void *) dma_mem_alloc(DMA_BUFFER_SIZE);
+	if (!adapter->dma_buffer) {
+		printk("Could not allocate DMA buffer\n");
+	}
+	adapter->dmaing = 0;
+
+	/*
+	 * enable interrupts on the board
+	 */
+	outb_control(CMDE, dev);
+
+	/*
+	 * configure adapter memory: we need 10 multicast addresses, default==0
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 3c505 memory configuration command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+	adapter->tx_pcb.data.memconf.cmd_q = __cpu_to_le16(10);
+	adapter->tx_pcb.data.memconf.rcv_q = __cpu_to_le16(20);
+	adapter->tx_pcb.data.memconf.mcast = __cpu_to_le16(10);
+	adapter->tx_pcb.data.memconf.frame = __cpu_to_le16(20);
+	adapter->tx_pcb.data.memconf.rcv_b = __cpu_to_le16(20);
+	adapter->tx_pcb.data.memconf.progs = __cpu_to_le16(0);
+	adapter->tx_pcb.length = sizeof(struct Memconf);
+	adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send memory configuration command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && time_before(jiffies, timeout));
+		if (time_after_eq(jiffies, timeout))
+			TIMEOUT_MSG(__LINE__);
+	}
+
+
+	/*
+	 * configure adapter to receive broadcast messages and wait for response
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 82586 configure command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+	adapter->tx_pcb.data.configure = __cpu_to_le16(NO_LOOPBACK | RECV_BROAD);
+	adapter->tx_pcb.length = 2;
+	adapter->got[CMD_CONFIGURE_82586] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send 82586 configure command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout));
+		if (time_after_eq(jiffies, timeout))
+			TIMEOUT_MSG(__LINE__);
+	}
+
+	/* enable burst-mode DMA */
+	/* outb(0x1, dev->base_addr + PORT_AUXDMA); */
+
+	/*
+	 * queue receive commands to provide buffering
+	 */
+	prime_rx(dev);
+	if (elp_debug >= 3)
+		printk("%s: %d receive PCBs active\n", dev->name, adapter->rx_active);
+
+	/*
+	 * device is now officially open!
+	 */
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;		/* Always succeed */
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static int send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+	elp_device *adapter = dev->priv;
+	unsigned long target;
+	unsigned long flags;
+	unsigned short dma_addr;
+
+	/*
+	 * make sure the length is even and no shorter than 60 bytes
+	 */
+	unsigned int nlen = (((skb->len < 60) ? 60 : skb->len) + 1) & (~1);
+
+	if (test_and_set_bit(0, (void *) &adapter->busy)) {
+		if (elp_debug >= 2)
+			printk("%s: transmit blocked\n", dev->name);
+		return FALSE;
+	}
+
+	adapter->stats.tx_bytes += nlen;
+	
+	/*
+	 * send the adapter a transmit packet command. Ignore segment and offset
+	 * and make sure the length is even
+	 */
+	adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+	adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+	adapter->tx_pcb.data.xmit_pkt.buf_ofs
+	    = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0;	/* Unused */
+	adapter->tx_pcb.data.xmit_pkt.pkt_len = __cpu_to_le16(nlen);
+printk("adapter->tx_pcb.data.xmit_pkt.pkt_len: %x, nlen: %x\n",adapter->tx_pcb.data.xmit_pkt.pkt_len,nlen);
+
+	if (!send_pcb(dev, &adapter->tx_pcb)) {
+		adapter->busy = 0;
+		return FALSE;
+	}
+	/* if this happens, we die */
+	if (test_and_set_bit(0, (void *) &adapter->dmaing))
+		printk("%s: tx: DMA %d in progress\n", dev->name, adapter->current_dma.direction);
+
+	adapter->current_dma.direction = 1;
+	adapter->current_dma.start_time = jiffies;
+
+	target = virt_to_phys(&skb->data[0]);
+printk("target: %x\n",target); 
+	adapter->current_dma.skb = skb;
+#if 1
+	dma_addr=dma_map_page(target,1,DMA_16BIT);
+#endif
+	adapter->current_dma.dma_addr = dma_addr ;
+#if 0
+printk("dma_addr: %x\n",dma_addr);
+#endif
+	
+	flags=claim_dma_lock();
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	set_dma_mode(dev->dma, 0x48);	/* dma memory -> io */
+#if 1
+	set_dma_addr(dev->dma, dma_addr + (target & ((1<<12)-1)));
+#else
+	set_dma_addr(dev->dma, 0);
+#endif
+#if 1
+printk("set_dma_addr: %x\n",dma_addr + (target & ((1<<12)-1)));
+#endif
+	set_dma_count(dev->dma, nlen);
+	outb_control(adapter->hcr_val | DMAE | TCEN, dev);
+	enable_dma(dev->dma);
+	release_dma_lock(flags);
+	
+	if (elp_debug >= 3)
+		printk("%s: DMA transfer started\n", dev->name);
+
+	return TRUE;
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ *    return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static int elp_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	if (dev->interrupt) {
+		printk("%s: start_xmit aborted (in irq)\n", dev->name);
+		return 1;
+	}
+
+	check_3c505_dma(dev);
+
+	/*
+	 * if the transmitter is still busy, we have a transmit timeout...
+	 */
+	if (dev->tbusy) {
+		elp_device *adapter = dev->priv;
+       		int tickssofar = jiffies - dev->trans_start;
+		int stat;
+
+		if (tickssofar < 1000)
+			return 1;
+
+		stat = inb_status(dev->base_addr);
+		printk("%s: transmit timed out, lost %s?\n", dev->name, (stat & ACRF) ? "interrupt" : "command");
+		if (elp_debug >= 1)
+			printk("%s: status %#02x\n", dev->name, stat);
+		dev->trans_start = jiffies;
+		dev->tbusy = 0;
+		adapter->stats.tx_dropped++;
+	}
+
+	if (elp_debug >= 3)
+		printk("%s: request to send packet of length %d\n", dev->name, (int) skb->len);
+
+	if (test_and_set_bit(0, (void *) &dev->tbusy)) {
+		printk("%s: transmitter access conflict\n", dev->name);
+		return 1;
+	}
+	/*
+	 * send the packet at skb->data for skb->len
+	 */
+	if (!send_packet(dev, skb)) {
+		if (elp_debug >= 2) {
+			printk("%s: failed to transmit packet\n", dev->name);
+		}
+		dev->tbusy = 0;
+		return 1;
+	}
+	if (elp_debug >= 3)
+		printk("%s: packet of length %d sent\n", dev->name, (int) skb->len);
+
+	/*
+	 * start the transmit timeout
+	 */
+	dev->trans_start = jiffies;
+
+	prime_rx(dev);
+
+	return 0;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct net_device_stats *elp_get_stats(struct net_device *dev)
+{
+	elp_device *adapter = (elp_device *) dev->priv;
+
+	if (elp_debug >= 3)
+		printk("%s: request for stats\n", dev->name);
+
+	/* If the device is closed, just return the latest stats we have,
+	   - we cannot ask from the adapter without interrupts */
+	if (!dev->start)
+		return &adapter->stats;
+
+	/* send a get statistics command to the board */
+	adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+	adapter->tx_pcb.length = 0;
+	adapter->got[CMD_NETWORK_STATISTICS] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send get statistics command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && time_before(jiffies, timeout));
+		if (time_after_eq(jiffies, timeout)) {
+			TIMEOUT_MSG(__LINE__);
+			return &adapter->stats;
+		}
+	}
+
+	/* statistics are now up to date */
+	return &adapter->stats;
+}
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int elp_close(struct net_device *dev)
+{
+	elp_device *adapter;
+
+	adapter = dev->priv;
+
+	if (elp_debug >= 3)
+		printk("%s: request to close device\n", dev->name);
+
+	/* Someone may request the device statistic information even when
+	 * the interface is closed. The following will update the statistics
+	 * structure in the driver, so we'll be able to give current statistics.
+	 */
+	(void) elp_get_stats(dev);
+
+	/*
+	 * disable interrupts on the board
+	 */
+	outb_control(0, dev);
+
+	/*
+	 *  flag transmitter as busy (i.e. not available)
+	 */
+	dev->tbusy = 1;
+
+	/*
+	 *  indicate device is closed
+	 */
+	dev->start = 0;
+
+	/*
+	 * release the IRQ
+	 */
+	free_irq(dev->irq, dev);
+
+	free_dma(dev->dma);
+	free_pages((unsigned long) adapter->dma_buffer, __get_order(DMA_BUFFER_SIZE));
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void elp_set_mc_list(struct net_device *dev)
+{
+	elp_device *adapter = (elp_device *) dev->priv;
+	struct dev_mc_list *dmi = dev->mc_list;
+	int i;
+
+	if (elp_debug >= 3)
+		printk("%s: request to set multicast list\n", dev->name);
+
+	if (!(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+		/* send a "load multicast list" command to the board, max 10 addrs/cmd */
+		/* if num_addrs==0 the list will be cleared */
+		adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+		adapter->tx_pcb.length = 6 * dev->mc_count;
+		for (i = 0; i < dev->mc_count; i++) {
+			memcpy(adapter->tx_pcb.data.multicast[i], dmi->dmi_addr, 6);
+			dmi = dmi->next;
+		}
+		adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+		if (!send_pcb(dev, &adapter->tx_pcb))
+			printk("%s: couldn't send set_multicast command\n", dev->name);
+		else {
+			int timeout = jiffies + TIMEOUT;
+			while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && time_before(jiffies, timeout));
+			if (time_after_eq(jiffies, timeout)) {
+				TIMEOUT_MSG(__LINE__);
+			}
+		}
+		if (dev->mc_count)
+			adapter->tx_pcb.data.configure = __cpu_to_le16(NO_LOOPBACK | RECV_BROAD | RECV_MULTI);
+		else		/* num_addrs == 0 */
+			adapter->tx_pcb.data.configure = __cpu_to_le16(NO_LOOPBACK | RECV_BROAD);
+	} else
+		adapter->tx_pcb.data.configure = __cpu_to_le16(NO_LOOPBACK | RECV_PROMISC);
+	/*
+	 * configure adapter to receive messages (as specified above)
+	 * and wait for response
+	 */
+	if (elp_debug >= 3)
+		printk("%s: sending 82586 configure command\n", dev->name);
+	adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+	adapter->tx_pcb.length = 2;
+	adapter->got[CMD_CONFIGURE_82586] = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb))
+		printk("%s: couldn't send 82586 configure command\n", dev->name);
+	else {
+		int timeout = jiffies + TIMEOUT;
+		while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout));
+		if (time_after_eq(jiffies, timeout))
+			TIMEOUT_MSG(__LINE__);
+	}
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static inline void elp_init(struct net_device *dev)
+{
+	elp_device *adapter = dev->priv;
+
+	/*
+	 * set ptrs to various functions
+	 */
+	dev->open = elp_open;	/* local */
+	dev->stop = elp_close;	/* local */
+	dev->get_stats = elp_get_stats;		/* local */
+	dev->hard_start_xmit = elp_start_xmit;	/* local */
+	dev->set_multicast_list = elp_set_mc_list;	/* local */
+
+	/* Setup the generic properties */
+	ether_setup(dev);
+
+	/*
+	 * setup ptr to adapter specific information
+	 */
+	memset(&(adapter->stats), 0, sizeof(struct net_device_stats));
+
+	/*
+	 * memory information
+	 */
+	dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0;
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int __init elp_sense(struct net_device *dev)
+{
+	int timeout;
+	int addr = dev->base_addr;
+	const char *name = dev->name;
+	long flags;
+	byte orig_HSR;
+
+#if 0
+	if (check_region(addr, 0xf))
+		return -1;
+#endif
+
+	orig_HSR = inb_status(addr);
+
+	if (elp_debug > 0)
+		printk(search_msg, name, addr);
+
+	if (orig_HSR == 0xff) {
+		if (elp_debug > 0)
+			printk(notfound_msg, 1);
+		return -1;
+	}
+	/* Enable interrupts - we need timers! */
+	save_flags(flags);
+	sti();
+
+	/* Wait for a while; the adapter may still be booting up */
+	if (elp_debug > 0)
+		printk(stilllooking_msg);
+
+	if (orig_HSR & DIR) {
+		/* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+		outb(0, dev->base_addr + PORT_CONTROL);
+		timeout = jiffies + 30*HZ/100;
+		while (time_before(jiffies, timeout));
+		restore_flags(flags);
+		if (inb_status(addr) & DIR) {
+			if (elp_debug > 0)
+				printk(notfound_msg, 2);
+			return -1;
+		}
+	} else {
+		/* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+		outb(DIR, dev->base_addr + PORT_CONTROL);
+		timeout = jiffies + 30*HZ/100;
+		while (time_before(jiffies, timeout));
+		restore_flags(flags);
+		if (!(inb_status(addr) & DIR)) {
+			if (elp_debug > 0)
+				printk(notfound_msg, 3);
+			return -1;
+		}
+	}
+	/*
+	 * It certainly looks like a 3c505.
+	 */
+	if (elp_debug > 0)
+		printk(found_msg);
+
+	return 0;
+}
+
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int __init elp_autodetect(struct net_device *dev)
+{
+	int idx = 0;
+
+	/* if base address set, then only check that address
+	   otherwise, run through the table */
+	if (dev->base_addr != 0) {	/* dev->base_addr == 0 ==> plain autodetect */
+		if (elp_sense(dev) == 0)
+			return dev->base_addr;
+	} else
+		while ((dev->base_addr = addr_list[idx++])) {
+			if (elp_sense(dev) == 0)
+				return dev->base_addr;
+		}
+
+	/* could not find an adapter */
+	if (elp_debug > 0)
+		printk(couldnot_msg, dev->name);
+
+	return 0;		/* Because of this, the layer above will return -ENODEV */
+}
+
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ *
+ ******************************************************/
+
+/* There are three situations we need to be able to detect here:
+
+ *  a) the card is idle
+ *  b) the card is still booting up
+ *  c) the card is stuck in a strange state (some DOS drivers do this)
+ *
+ * In case (a), all is well.  In case (b), we wait 10 seconds to see if the
+ * card finishes booting, and carry on if so.  In case (c), we do a hard reset,
+ * loop round, and hope for the best.
+ *
+ * This is all very unpleasant, but hopefully avoids the problems with the old
+ * probe code (which had a 15-second delay if the card was idle, and didn't
+ * work at all if it was in a weird state).
+ */
+
+int __init elplus_probe(struct net_device *dev)
+{
+	elp_device *adapter;
+	int i, tries, tries1, timeout, okay;
+
+	/*
+	 *  setup adapter structure
+	 */
+
+	dev->base_addr = elp_autodetect(dev);
+	if (!(dev->base_addr))
+		return -ENODEV;
+
+	/*
+	 * setup ptr to adapter specific information
+	 */
+	adapter = (elp_device *) (dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+	adapter->dmaing=0;
+	adapter->current_dma.direction=0;
+
+	if (adapter == NULL) {
+		printk("%s: out of memory\n", dev->name);
+		return -ENODEV;
+        }
+
+	adapter->send_pcb_semaphore = 0;
+
+	for (tries1 = 0; tries1 < 3; tries1++) {
+		outb_control((adapter->hcr_val | CMDE) & ~DIR, dev);
+		/* First try to write just one byte, to see if the card is
+		 * responding at all normally.
+		 */
+		timeout = jiffies + 5*HZ/100;
+		okay = 0;
+		while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE));
+		if ((inb_status(dev->base_addr) & HCRE)) {
+			outb_command(0, dev->base_addr);	/* send a spurious byte */
+			timeout = jiffies + 5*HZ/100;
+			while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE));
+			if (inb_status(dev->base_addr) & HCRE)
+				okay = 1;
+		}
+		if (!okay) {
+			/* Nope, it's ignoring the command register.  This means that
+			 * either it's still booting up, or it's died.
+			 */
+			printk("%s: command register wouldn't drain, ", dev->name);
+			if ((inb_status(dev->base_addr) & 7) == 3) {
+				/* If the adapter status is 3, it *could* still be booting.
+				 * Give it the benefit of the doubt for 10 seconds.
+				 */
+				printk("assuming 3c505 still starting\n");
+				timeout = jiffies + 10*HZ;
+				while (time_before(jiffies, timeout) && (inb_status(dev->base_addr) & 7));
+				if (inb_status(dev->base_addr) & 7) {
+					printk("%s: 3c505 failed to start\n", dev->name);
+				} else {
+					okay = 1;  /* It started */
+				}
+			} else {
+				/* Otherwise, it must just be in a strange
+				 * state.  We probably need to kick it.
+				 */
+				printk("3c505 is sulking\n");
+			}
+		}
+		for (tries = 0; tries < 5 && okay; tries++) {
+
+			/*
+			 * Try to set the Ethernet address, to make sure that the board
+			 * is working.
+			 */
+			adapter->tx_pcb.command = CMD_STATION_ADDRESS;
+			adapter->tx_pcb.length = 0;
+#if 0
+			autoirq_setup(0);
+#endif
+			if (!send_pcb(dev, &adapter->tx_pcb)) {
+				printk("%s: could not send first PCB\n", dev->name);
+				autoirq_report(0);
+				continue;
+			}
+			if (!receive_pcb(dev, &adapter->rx_pcb)) {
+				printk("%s: could not read first PCB\n", dev->name);
+				autoirq_report(0);
+				continue;
+			}
+			if ((adapter->rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+			    (adapter->rx_pcb.length != 6)) {
+				printk("%s: first PCB wrong (%d, %d)\n", dev->name, adapter->rx_pcb.command, adapter->rx_pcb.length);
+				autoirq_report(0);
+				continue;
+			}
+			goto okay;
+		}
+		/* It's broken.  Do a hard reset to re-initialise the board,
+		 * and try again.
+		 */
+		printk(KERN_INFO "%s: resetting adapter\n", dev->name);
+		outb_control(adapter->hcr_val | FLSH | ATTN, dev);
+		outb_control(adapter->hcr_val & ~(FLSH | ATTN), dev);
+	}
+	printk("%s: failed to initialise 3c505\n", dev->name);
+	return -ENODEV;
+
+      okay:
+dev->irq=10;
+#if 0
+	if (dev->irq) {		/* Is there a preset IRQ? */
+		int rpt = autoirq_report(0);
+		if (dev->irq != rpt) {
+			printk("%s: warning, irq %d configured but %d detected\n", dev->name, dev->irq, rpt);
+		}
+		/* if dev->irq == autoirq_report(0), all is well */
+	} else		       /* No preset IRQ; just use what we can detect */
+		dev->irq = autoirq_report(0);
+#endif
+	switch (dev->irq) {    /* Legal, sane? */
+	case 0:
+		printk("%s: IRQ probe failed: check 3c505 jumpers.\n",
+		       dev->name);
+		return -ENODEV;
+	case 1:
+	case 6:
+	case 8:
+	case 13:
+		printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+		       dev->name, dev->irq);
+		return -ENODEV;
+	}
+	/*
+	 *  Now we have the IRQ number so we can disable the interrupts from
+	 *  the board until the board is opened.
+	 */
+	outb_control(adapter->hcr_val & ~CMDE, dev);
+
+	/*
+	 * copy Ethernet address into structure
+	 */
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = adapter->rx_pcb.data.eth_addr[i];
+
+	/* find a DMA channel */
+	if (!dev->dma) {
+		if (dev->mem_start) {
+			dev->dma = dev->mem_start & 7;
+		}
+		else {
+			printk(KERN_WARNING "%s: warning, DMA channel not specified, using default\n", dev->name);
+			dev->dma = ELP_DMA;
+		}
+	}
+
+	/*
+	 * print remainder of startup message
+	 */
+	printk("%s: 3c505 at %#lx, irq %d, dma %d, ",
+	       dev->name, dev->base_addr, dev->irq, dev->dma);
+	printk("addr %02x:%02x:%02x:%02x:%02x:%02x, ",
+	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+	/*
+	 * read more information from the adapter
+	 */
+
+	adapter->tx_pcb.command = CMD_ADAPTER_INFO;
+	adapter->tx_pcb.length = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb) ||
+	    !receive_pcb(dev, &adapter->rx_pcb) ||
+	    (adapter->rx_pcb.command != CMD_ADAPTER_INFO_RESPONSE) ||
+	    (adapter->rx_pcb.length != 10)) {
+		printk("not responding to second PCB\n");
+	}
+	printk("rev %d.%d, %dk\n", adapter->rx_pcb.data.info.major_vers, adapter->rx_pcb.data.info.minor_vers, adapter->rx_pcb.data.info.RAM_sz);
+
+	/*
+	 * reconfigure the adapter memory to better suit our purposes
+	 */
+	adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+	adapter->tx_pcb.length = 12;
+	adapter->tx_pcb.data.memconf.cmd_q = 8;
+	adapter->tx_pcb.data.memconf.rcv_q = 8;
+	adapter->tx_pcb.data.memconf.mcast = 10;
+	adapter->tx_pcb.data.memconf.frame = 10;
+	adapter->tx_pcb.data.memconf.rcv_b = 10;
+	adapter->tx_pcb.data.memconf.progs = 0;
+	if (!send_pcb(dev, &adapter->tx_pcb) ||
+	    !receive_pcb(dev, &adapter->rx_pcb) ||
+	    (adapter->rx_pcb.command != CMD_CONFIGURE_ADAPTER_RESPONSE) ||
+	    (adapter->rx_pcb.length != 2)) {
+		printk("%s: could not configure adapter memory\n", dev->name);
+	}
+	if (adapter->rx_pcb.data.configure) {
+		printk("%s: adapter configuration failed\n", dev->name);
+	}
+	/*
+	 * and reserve the address region
+	 */
+	request_region(dev->base_addr, ELP_IO_EXTENT, "3c505");
+
+	/*
+	 * initialise the device
+	 */
+	elp_init(dev);
+
+	return 0;
+}
+
+#ifdef MODULE
+#define NAMELEN 9
+static char devicename[ELP_MAX_CARDS][NAMELEN] = {{0,}};
+static struct net_device dev_3c505[ELP_MAX_CARDS] =
+{
+	{ NULL,		/* device name is inserted by net_init.c */
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, elplus_probe},
+};
+
+static int io[ELP_MAX_CARDS] = { 0, };
+static int irq[ELP_MAX_CARDS] = { 0, };
+static int dma[ELP_MAX_CARDS] = { 0, };
+MODULE_PARM(io, "1-" __MODULE_STRING(ELP_MAX_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(ELP_MAX_CARDS) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(ELP_MAX_CARDS) "i");
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+		struct net_device *dev = &dev_3c505[this_dev];
+		dev->name = devicename[this_dev];
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		if (dma[this_dev]) {
+			dev->dma = dma[this_dev];
+		} else {
+			dev->dma = ELP_DMA;
+			printk(KERN_WARNING "3c505.c: warning, using default DMA channel,\n");
+		}
+		if (io[this_dev] == 0) {
+			if (this_dev) break;
+			printk(KERN_NOTICE "3c505.c: module autoprobe not recommended, give io=xx.\n");
+		}
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "3c505.c: Failed to register card at 0x%x.\n", io[this_dev]);
+			if (found != 0) return 0;
+			return -ENXIO;
+		}
+		found++;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+		struct net_device *dev = &dev_3c505[this_dev];
+		if (dev->priv != NULL) {
+			unregister_netdev(dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(dev->base_addr, ELP_IO_EXTENT);
+		}
+	}
+}
+
+#endif				/* MODULE */
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/net/3c505_dma_apollo.h	Sat Oct 16 18:18:23 1999
@@ -0,0 +1,293 @@
+/*****************************************************************
+ *
+ *  defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+#define ELP_DMA       6
+#define ELP_RX_PCBS   4
+#define ELP_MAX_CARDS 4
+
+/*
+ * I/O register offsets
+ */
+#define	PORT_COMMAND	0x00	/* read/write, 8-bit */
+#define	PORT_STATUS	0x02	/* read only, 8-bit */
+#define	PORT_AUXDMA	0x02	/* write only, 8-bit */
+#define	PORT_DATA	0x04	/* read/write, 16-bit */
+#define	PORT_CONTROL	0x06	/* read/write, 8-bit */
+
+#define ELP_IO_EXTENT	0x10	/* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define	ATTN	0x80	/* attention */
+#define	FLSH	0x40	/* flush data register */
+#define DMAE	0x20	/* DMA enable */
+#define DIR	0x10	/* direction */
+#define	TCEN	0x08	/* terminal count interrupt enable */
+#define	CMDE	0x04	/* command register interrupt enable */
+#define	HSF2	0x02	/* host status flag 2 */
+#define	HSF1	0x01	/* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define	HSF_PCB_ACK	HSF1
+#define	HSF_PCB_NAK	HSF2
+#define	HSF_PCB_END	(HSF2|HSF1)
+#define	HSF_PCB_MASK	(HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define	HRDY	0x80	/* data register ready */
+#define	HCRE	0x40	/* command register empty */
+#define	ACRF	0x20	/* adapter command register full */
+/* #define DIR 	0x10	direction - same as in control register */
+#define	DONE	0x08	/* DMA done */
+#define	ASF3	0x04	/* adapter status flag 3 */
+#define	ASF2	0x02	/* adapter status flag 2 */
+#define	ASF1	0x01	/* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define	ASF_PCB_ACK	ASF1
+#define	ASF_PCB_NAK	ASF2
+#define	ASF_PCB_END	(ASF2|ASF1)
+#define	ASF_PCB_MASK	(ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define	DMA_BRST	0x01	/* DMA burst */
+
+/*
+ * maximum amount of data allowed in a PCB
+ */
+#define	MAX_PCB_DATA	62
+
+/*****************************************************************
+ *
+ *  timeout value
+ *	this is a rough value used for loops to stop them from 
+ *	locking up the whole machine in the case of failure or
+ *	error conditions
+ *
+ *****************************************************************/
+
+#define	TIMEOUT	300
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+  /*
+   * host PCB commands
+   */
+  CMD_CONFIGURE_ADAPTER_MEMORY	= 0x01,
+  CMD_CONFIGURE_82586		= 0x02,
+  CMD_STATION_ADDRESS		= 0x03,
+  CMD_DMA_DOWNLOAD		= 0x04,
+  CMD_DMA_UPLOAD		= 0x05,
+  CMD_PIO_DOWNLOAD		= 0x06,
+  CMD_PIO_UPLOAD		= 0x07,
+  CMD_RECEIVE_PACKET		= 0x08,
+  CMD_TRANSMIT_PACKET		= 0x09,
+  CMD_NETWORK_STATISTICS	= 0x0a,
+  CMD_LOAD_MULTICAST_LIST	= 0x0b,
+  CMD_CLEAR_PROGRAM		= 0x0c,
+  CMD_DOWNLOAD_PROGRAM		= 0x0d,
+  CMD_EXECUTE_PROGRAM		= 0x0e,
+  CMD_SELF_TEST			= 0x0f,
+  CMD_SET_STATION_ADDRESS	= 0x10,
+  CMD_ADAPTER_INFO		= 0x11,
+  NUM_TRANSMIT_CMDS,
+
+  /*
+   * adapter PCB commands
+   */
+  CMD_CONFIGURE_ADAPTER_RESPONSE	= 0x31,
+  CMD_CONFIGURE_82586_RESPONSE		= 0x32,
+  CMD_ADDRESS_RESPONSE			= 0x33,
+  CMD_DOWNLOAD_DATA_REQUEST		= 0x34,
+  CMD_UPLOAD_DATA_REQUEST		= 0x35,
+  CMD_RECEIVE_PACKET_COMPLETE		= 0x38,
+  CMD_TRANSMIT_PACKET_COMPLETE		= 0x39,
+  CMD_NETWORK_STATISTICS_RESPONSE	= 0x3a,
+  CMD_LOAD_MULTICAST_RESPONSE		= 0x3b,
+  CMD_CLEAR_PROGRAM_RESPONSE		= 0x3c,
+  CMD_DOWNLOAD_PROGRAM_RESPONSE		= 0x3d,
+  CMD_EXECUTE_RESPONSE			= 0x3e,
+  CMD_SELF_TEST_RESPONSE		= 0x3f,
+  CMD_SET_ADDRESS_RESPONSE		= 0x40,
+  CMD_ADAPTER_INFO_RESPONSE		= 0x41
+};
+
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char         byte;
+typedef unsigned short int    word;
+typedef unsigned long int     dword;
+
+/* Data structures */
+struct Memconf {
+	word	cmd_q,
+		rcv_q,
+		mcast,
+		frame,
+		rcv_b,
+		progs;
+};
+
+struct Rcv_pkt {
+	word	buf_ofs,
+		buf_seg,
+		buf_len,
+		timeout;
+};
+
+struct Xmit_pkt {
+	word	buf_ofs,
+		buf_seg,
+		pkt_len;
+};
+
+struct Rcv_resp {
+	word	buf_ofs,
+		buf_seg,
+		buf_len,
+		pkt_len,
+		timeout,
+		status;
+	dword	timetag;
+};
+
+struct Xmit_resp {
+	word	buf_ofs,
+		buf_seg,
+		c_stat,
+		status;
+};
+
+
+struct Netstat {
+	dword	tot_recv,
+		tot_xmit;
+	word	err_CRC,
+		err_align,
+		err_res,
+		err_ovrrun;
+};
+
+
+struct Selftest {
+	word	error;
+	union {
+		word ROM_cksum;
+		struct {
+			word ofs, seg;
+		} RAM;
+		word i82586;
+	} failure;
+};
+
+struct Info {
+	byte	minor_vers,
+		major_vers;
+	word	ROM_cksum,
+		RAM_sz,
+		free_ofs,
+		free_seg;
+};
+
+struct Memdump {
+       word size,
+            off,
+            seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+Ethernet data, which has different packaging.)
+*/
+typedef struct {
+	byte	command;
+	byte	length;
+	union	{
+		struct Memconf		memconf;
+		word			configure;
+		struct Rcv_pkt		rcv_pkt;
+		struct Xmit_pkt		xmit_pkt;
+		byte			multicast[10][6];
+		byte			eth_addr[6];
+		byte			failed;
+		struct Rcv_resp		rcv_resp;
+		struct Xmit_resp	xmit_resp;
+		struct Netstat		netstat;
+		struct Selftest		selftest;
+		struct Info		info;
+		struct Memdump    	memdump;
+		byte			raw[62];
+	} data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION	0x00
+#define RECV_BROAD	0x01
+#define RECV_MULTI	0x02
+#define RECV_PROMISC	0x04
+#define NO_LOOPBACK	0x00
+#define INT_LOOPBACK	0x08
+#define EXT_LOOPBACK	0x10
+
+/*****************************************************************
+ *
+ *  structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+#define DMA_BUFFER_SIZE  1600
+#define BACKLOG_SIZE      4
+
+typedef struct {
+	volatile short got[NUM_TRANSMIT_CMDS];	/* flags for
+						   command completion */
+	pcb_struct tx_pcb;	/* PCB for foreground sending */
+	pcb_struct rx_pcb;	/* PCB for foreground receiving */
+	pcb_struct itx_pcb;	/* PCB for background sending */
+	pcb_struct irx_pcb;	/* PCB for background receiving */
+	struct net_device_stats stats;
+
+	void *dma_buffer;
+
+	struct {
+		unsigned int length[BACKLOG_SIZE];
+		unsigned int in;
+		unsigned int out;
+	} rx_backlog;
+
+	struct {
+		unsigned int direction;
+		unsigned int length;
+		struct sk_buff *skb;
+	        void *target;
+		long int start_time;
+		unsigned short dma_addr;
+	} current_dma;
+
+	/* flags */
+	unsigned long send_pcb_semaphore;
+	unsigned int dmaing;
+	unsigned long busy;
+
+	unsigned int rx_active;  /* number of receive PCBs */
+        volatile unsigned char hcr_val;  /* what we think the HCR contains */
+} elp_device;
--- ../linux-2.3.16/./drivers/video/Makefile	Mon Aug 30 14:22:56 1999
+++ ./drivers/video/Makefile	Sun Oct 24 04:23:57 1999
@@ -90,7 +90,7 @@
 endif
 
 ifeq ($(CONFIG_FB_APOLLO),y)
-L_OBJS += dnfb.o
+L_OBJS += dn_fb.o
 endif
 
 ifeq ($(CONFIG_FB_Q40),y)
--- ../linux-2.3.16/./drivers/video/fbcon.c	Fri Sep  3 16:16:37 1999
+++ ./drivers/video/fbcon.c	Wed Oct 27 00:30:41 1999
@@ -342,9 +342,11 @@
     int unit = conp->vc_num;
     struct fb_info *info;
 
+
     /* on which frame buffer will we open this console? */
     info = registered_fb[(int)con2fb_map[unit]];
 
+
     info->changevar = &fbcon_changevar;
     fb_display[unit] = *(info->disp);	/* copy from default */
     DPRINTK("mode:   %s\n",info->modename);
@@ -419,7 +421,7 @@
 	    p->_fontheightlog = i;
 }
 
-#define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
+#define fontwidthvalid(p,w) (((p)->dispsw->fontwidthmask) & (FONTWIDTH(w)))
 
 static void fbcon_setup(int con, int init, int logo)
 {
@@ -431,6 +433,7 @@
     int i, charcnt = 256;
     struct fbcon_font_desc *font;
     
+
     if (con != fg_console || (p->fb_info->flags & FBINFO_FLAG_MODULE) ||
         p->type == FB_TYPE_TEXT)
     	logo = 0;
@@ -494,7 +497,7 @@
         fbcon_font_widths(p);
     }
     
-    if (!fontwidthvalid(p,fontwidth(p))) {
+    if (!(fontwidthvalid(p,fontwidth(p)))) {
 #ifdef CONFIG_MAC
 	if (MACH_IS_MAC)
 	    /* ++Geert: hack to make 6x11 fonts work on mac */
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/video/dn_accel.h	Fri Oct 15 05:24:55 1999
@@ -0,0 +1,9 @@
+#ifndef _DN_ACCEL_H_
+#define _DN_ACCEL_H_
+
+#include <linux/fb.h>
+
+void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
+	       int x_count, int y_count);
+
+#endif
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/video/dn_cfb4.c	Sat Oct 16 01:41:56 1999
@@ -0,0 +1,547 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/apollohw.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+#include "dn_accel.h"
+
+/* apollo video HW definitions */
+
+/*
+ * Control Registers.   IOBASE + $x
+ *
+ * Note: these are the Memory/IO BASE definitions for a mono card set to the
+ * alternate address
+ *
+ * Control 3A and 3B serve identical functions except that 3A
+ * deals with control 1 and 3b deals with Color LUT reg.
+ */
+
+#define AP_IOBASE       0x3d0          /* Base address of 1 plane board. */
+#define AP_STATUS       isaIO2mem(AP_IOBASE+0) /* Status register.  Read */
+#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */
+#define AP_DEVICE_ID    isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */
+#define AP_ROP_1        isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */
+#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+4) /* Diagnostic Memory Request. Write Word */
+#define AP_CONTROL_0    isaIO2mem(AP_IOBASE+8) /* Control Register 0.  Read/Write */
+#define AP_CONTROL_1    isaIO2mem(AP_IOBASE+0xa) /* Control Register 1.  Read/Write */
+#define AP_CONTROL_2    isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */
+#define AP_CONTROL_3A   isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */
+#define AP_LUT_RED     isaIO2mem(AP_IOBASE+9) /* Red Lookup Table register. Write */
+#define AP_LUT_GREEN  isaIO2mem(AP_IOBASE+0xb) /* Green Lookup Table register. Write */
+#define AP_LUT_BLUE   isaIO2mem(AP_IOBASE+0xd) /* Blue Lookup Table register. Write */
+#define AP_AD_CHANNEL   isaIO2mem(AP_IOBASE+0xf) /* A/D Result/Channel register. Read/Write */
+
+
+#define FRAME_BUFFER_START 0x0A0000
+#define FRAME_BUFFER_LEN 0x20000
+
+/* CREG 0 */
+#define VECTOR_MODE 0x40 /* 010x.xxxx */
+#define DBLT_MODE   0x80 /* 100x.xxxx */
+#define NORMAL_MODE 0xE0 /* 111x.xxxx */
+#define SHIFT_BITS  0x1F /* xxx1.1111 */
+        /* other bits are Shift value */
+
+/* CREG 1 */
+#define AD_BLT      0x80 /* 1xxx.xxxx */
+
+#define ROP_EN          0x10 /* xxx1.xxxx */
+#define DST_EQ_SRC      0x00 /* xxx0.xxxx */
+#define nRESET_SYNC     0x08 /* xxxx.1xxx */
+#define SYNC_ENAB       0x02 /* xxxx.xx1x */
+
+#define BLANK_DISP      0x00 /* xxxx.xxx0 */
+#define ENAB_DISP       0x01 /* xxxx.xxx1 */
+
+#define NORM_CREG1      (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
+
+/* CREG 2B */
+
+/*
+ * Following 3 defines are common to 1, 4 and 8 plane.
+ */
+
+#define S_DATA_1s   0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
+#define S_DATA_PIX  0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
+#define S_DATA_PLN  0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
+ one plane of image mem */
+
+/* CREG 3A/CREG 3B */
+#       define RESET_CREG 0x80 /* 1000.0000 */
+
+/* ROP REG  -  all one nibble */
+/*      ********* NOTE : this is used r0,r1,r2,r3 *********** */
+#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
+#define DEST_ZERO               0x0
+#define SRC_AND_DEST    0x1
+#define SRC_AND_nDEST   0x2
+#define SRC                             0x3
+#define nSRC_AND_DEST   0x4
+#define DEST                    0x5
+#define SRC_XOR_DEST    0x6
+#define SRC_OR_DEST             0x7
+#define SRC_NOR_DEST    0x8
+#define SRC_XNOR_DEST   0x9
+#define nDEST                   0xA
+#define SRC_OR_nDEST    0xB
+#define nSRC                    0xC
+#define nSRC_OR_DEST    0xD
+#define SRC_NAND_DEST   0xE
+#define DEST_ONE                0xF
+
+#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
+
+
+void dn_video_setup(char *options, int *ints);
+
+/* frame buffer operations */
+
+static int dn_fb_open(struct fb_info *info,int user);
+static int dn_fb_release(struct fb_info *info,int user);
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, 
+			 struct fb_info *info);
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive,
+			 struct fb_info *info);
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		       unsigned long arg, int con, struct fb_info *info);
+
+static int dnfbcon_switch(int con,struct fb_info *info);
+static int dnfbcon_updatevar(int con,struct fb_info *info);
+static void dnfbcon_blank(int blank,struct fb_info *info);
+
+static void dn_fb_set_disp(int con,struct fb_info *info);
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static struct fb_ops dn_fb_ops = { 
+	dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var,
+	dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl
+};
+
+static int currcon=0;
+
+#define NUM_TOTAL_MODES 1
+struct fb_var_screeninfo dn_fb_predefined[] = {
+
+	{ 0, },
+
+};
+
+static char dn_fb_name[]="Apollo ";
+
+/* accel stuff */
+#define USE_DN_ACCEL
+
+static struct display_switch dispsw_apollofb;
+
+static int dn_fb_open(struct fb_info *info,int user)
+{
+        /*
+         * Nothing, only a usage count for the moment
+         */
+
+        MOD_INC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_release(struct fb_info *info,int user)
+{
+        MOD_DEC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info) {
+
+	strcpy(fix->id,"Apollo Color4");
+	fix->smem_start=(FRAME_BUFFER_START+IO_BASE);
+	fix->smem_len=FRAME_BUFFER_LEN;
+	fix->type=FB_TYPE_PACKED_PIXELS;
+	fix->type_aux=0;
+	fix->visual=FB_VISUAL_MONO10;
+	fix->xpanstep=0;
+	fix->ypanstep=0;
+	fix->ywrapstep=0;
+        fix->line_length=128;
+
+	return 0;
+
+}
+        
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+		
+	var->xres=1024;
+	var->yres=800;
+	var->xres_virtual=1024;
+	var->yres_virtual=1024;
+	var->xoffset=0;
+	var->yoffset=0;
+	var->bits_per_pixel=1;
+	var->grayscale=0;
+	var->nonstd=0;
+	var->activate=0;
+	var->height=-1;
+	var->width=-1;
+	var->pixclock=0;
+	var->left_margin=0;
+	var->right_margin=0;
+	var->hsync_len=0;
+	var->vsync_len=0;
+	var->sync=0;
+	var->vmode=FB_VMODE_NONINTERLACED;
+
+	return 0;
+
+}
+
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+
+        printk("fb_set_var\n");
+	if(var->xres!=1024) 
+		return -EINVAL;
+	if(var->yres!=800)
+		return -EINVAL;
+	if(var->xres_virtual!=1024)
+		return -EINVAL;
+	if(var->yres_virtual!=1024)
+		return -EINVAL;
+	if(var->xoffset!=0)
+		return -EINVAL;
+	if(var->yoffset!=0)
+		return -EINVAL;
+	if(var->bits_per_pixel!=1)
+		return -EINVAL;
+	if(var->grayscale!=0)
+		return -EINVAL;
+	if(var->nonstd!=0)
+		return -EINVAL;
+	if(var->activate!=0)
+		return -EINVAL;
+	if(var->pixclock!=0)
+		return -EINVAL;
+	if(var->left_margin!=0)
+		return -EINVAL;
+	if(var->right_margin!=0)
+		return -EINVAL;
+	if(var->hsync_len!=0)
+		return -EINVAL;
+	if(var->vsync_len!=0)
+		return -EINVAL;
+	if(var->sync!=0)
+		return -EINVAL;
+	if(var->vmode!=FB_VMODE_NONINTERLACED)
+		return -EINVAL;
+
+	return 0;
+
+}
+
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("get cmap not supported\n");
+
+	return -EINVAL;
+}
+
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("set cmap not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info) {
+
+	printk("panning not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long arg, int con, struct fb_info *info) {
+
+	printk("no IOCTLs as of yet.\n");
+
+	return -EINVAL;
+
+}
+
+static void dn_fb_set_disp(int con, struct fb_info *info) {
+
+  struct fb_fix_screeninfo fix;
+  struct display *display;
+
+
+  dn_fb_get_fix(&fix,con, info);
+
+  if (con>=0)
+	display=&fb_display[con];
+  else
+	display=&disp[0];
+
+  if(con==-1) 
+    con=0;
+
+   display->screen_base = (u_char *)fix.smem_start;
+printk("screenbase: %lx\n",fix.smem_start);
+   display->visual = fix.visual;
+   display->type = fix.type;
+   display->type_aux = fix.type_aux;
+   display->ypanstep = fix.ypanstep;
+   display->ywrapstep = fix.ywrapstep;
+   display->can_soft_blank = 1;
+   display->inverse = 0;
+   display->line_length = fix.line_length;
+   display->scrollmode = SCROLL_YREDRAW;
+#ifdef FBCON_HAS_MFB
+   display->dispsw = &fbcon_mfb;
+#else
+   display->dispsw=&fbcon_dummy;
+#endif
+
+}
+  
+unsigned long dnfb_init(unsigned long mem_start) {
+
+	int err;
+
+printk("dn_fb_init\n");
+
+	fb_info.changevar=NULL;
+	strcpy(&fb_info.modename[0],dn_fb_name);
+	fb_info.fontname[0]=0;
+	fb_info.disp=disp;
+	fb_info.switch_con=&dnfbcon_switch;
+	fb_info.updatevar=&dnfbcon_updatevar;
+	fb_info.blank=&dnfbcon_blank;	
+	fb_info.node = -1;
+	fb_info.fbops = &dn_fb_ops;
+	fb_info.flags = FBINFO_FLAG_DEFAULT;	
+
+        dn_fb_get_var(&disp[0].var,0, &fb_info);
+	dn_fb_set_disp(-1, &fb_info);
+
+printk("dn_fb_init: register\n");
+	err=register_framebuffer(&fb_info);
+	if(err < 0) {
+		panic("unable to register apollo frame buffer\n");
+	}
+ 
+	/* now we have registered we can safely setup the hardware */
+
+        outb(RESET_CREG,  AP_CONTROL_3A);
+        outb(NORMAL_MODE, AP_CONTROL_0); 
+        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1),  AP_CONTROL_1);
+        outb(S_DATA_PLN,  AP_CONTROL_2);
+        outw(SWAP(0x3), AP_ROP_1);
+
+        printk("apollo frame buffer alive and kicking !\n");
+
+
+	return mem_start;
+
+}	
+
+	
+static int dnfbcon_switch(int con,  struct fb_info *info) { 
+
+	currcon=con;
+	
+	return 0;
+
+}
+
+static int dnfbcon_updatevar(int con,  struct fb_info *info) {
+
+	return 0;
+
+}
+
+static void dnfbcon_blank(int blank,  struct fb_info *info) {
+
+	if(blank)  {
+        	outb(0x0,  AP_CONTROL_3A);
+	}
+	else {
+	        outb(0x1,  AP_CONTROL_3A);
+	}
+
+	return ;
+
+}
+
+void dn_video_setup(char *options, int *ints) {
+	
+	return;
+
+}
+
+void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
+               int x_count, int y_count) {
+
+	int incr,y_delta,pre_read=0,x_end,x_word_count;
+	ushort *src,dummy;
+	uint start_mask,end_mask,dest;
+	short i,j;
+
+	incr=(y_dest<=y_src) ? 1 : -1 ;
+
+	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
+	
+	if(incr>0) {
+		y_delta=(p->next_line*8)-x_src-x_count;
+		x_end=x_dest+x_count-1;
+		x_word_count=(x_end>>4) - (x_dest >> 4) + 1;
+		start_mask=0xffff0000 >> (x_dest & 0xf);
+		end_mask=0x7ffff >> (x_end & 0xf);
+		outb((((x_dest & 0xf) - (x_src &0xf))  % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) < (x_src & 0xf))
+			pre_read=1;
+	}
+	else {
+		y_delta=-((p->next_line*8)-x_src-x_count);
+		x_end=x_dest-x_count+1;
+		x_word_count=(x_dest>>4) - (x_end >> 4) + 1;
+		start_mask=0x7ffff >> (x_dest & 0xf);
+		end_mask=0xffff0000 >> (x_end & 0xf);
+		outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) > (x_src & 0xf))
+			pre_read=1;
+	}
+
+	for(i=0;i<y_count;i++) {
+			
+		if(pre_read) {
+			dummy=*src;
+			src+=incr;
+		}
+
+		if(x_word_count) {
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			src+=incr;
+			dest+=incr;
+			outb(0,AP_WRITE_ENABLE);
+
+			for(j=1;j<(x_word_count-1);j++) {
+				*src=dest;
+				src+=incr;	
+				dest+=incr;
+			}
+
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		else {
+			outb(start_mask | end_mask, AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		src+=(y_delta/16);
+		dest+=(y_delta/16);
+	}
+	outb(NORMAL_MODE,AP_CONTROL_0);
+}
+
+static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
+{
+
+   int fontheight,fontwidth;
+
+    fontheight=fontheight(p);
+    fontwidth=fontwidth(p);
+
+#ifdef USE_DN_ACCEL
+    dn_bitblt(p,sx,sy*fontheight,dx,dy*fontheight,width*fontwidth,
+	      height*fontheight);
+#else
+    u_char *src, *dest;
+    u_int rows;
+
+    if (sx == 0 && dx == 0 && width == p->next_line) {
+	src = p->screen_base+sy*fontheight*width;
+	dest = p->screen_base+dy*fontheight*width;
+	mymemmove(dest, src, height*fontheight*width);
+    } else if (dy <= sy) {
+	src = p->screen_base+sy*fontheight*p->next_line+sx;
+	dest = p->screen_base+dy*fontheight*p->next_line+dx;
+	for (rows = height*fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src += p->next_line;
+	    dest += p->next_line;
+	}
+    } else {
+	src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
+	dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
+	for (rows = height*fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src -= p->next_line;
+	    dest -= p->next_line;
+	}
+    }
+#endif
+}
+
+static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+	fbcon_mfb_clear(conp,p,sy,sx,height,width);
+}
+
+static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
+{
+	fbcon_mfb_putc(conp,p,c,yy,xx);
+}
+
+static void putcs_apollofb(struct vc_data *conp, struct display *p, const unsigned short *s,
+		      int count, int yy, int xx)
+{
+	fbcon_mfb_putcs(conp,p,s,count,yy,xx);
+}
+
+static void rev_char_apollofb(struct display *p, int xx, int yy)
+{
+	fbcon_mfb_revc(p,xx,yy);
+}
+
+static struct display_switch dispsw_apollofb = {
+    fbcon_mfb_setup, bmove_apollofb, clear_apollofb,
+    putc_apollofb, putcs_apollofb, rev_char_apollofb
+};
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/video/dn_cfb8.c	Fri Oct 15 04:57:45 1999
@@ -0,0 +1,595 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/apollohw.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include "dn_accel.h"
+#include "fbcon.h"
+#include "fbcon-mfb.h"
+
+/* apollo video HW definitions */
+
+/*
+ * Control Registers.   IOBASE + $x
+ *
+ * Note: these are the Memory/IO BASE definitions for a mono card set to the
+ * alternate address
+ *
+ * Control 3A and 3B serve identical functions except that 3A
+ * deals with control 1 and 3b deals with Color LUT reg.
+ */
+
+#define AP_IOBASE       0x3d0          /* Base address of 1 plane board. */
+#define AP_STATUS       isaIO2mem(AP_IOBASE+0) /* Status register.  Read */
+#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */
+#define AP_DEVICE_ID    isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */
+#define AP_ROP_0        isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */
+#define AP_ROP_1        isaIO2mem(AP_IOBASE+4) /* Raster Operation reg. Write Word */
+#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+6) /* Diagnostic Memory Request. Write Word */
+#define AP_CONTROL_0    isaIO2mem(AP_IOBASE+8) /* Control Register 0.  Read/Write */
+#define AP_LUT_DATA     isaIO2mem(AP_IOBASE+9) /* Control Register 0.  Read/Write */
+#define AP_CONTROL_1    isaIO2mem(AP_IOBASE+0xa) /* Control Register 1.  Read/Write */
+#define AP_LUT_CONTROL  isaIO2mem(AP_IOBASE+0xb) /* Control Register 1.  Read/Write */
+#define AP_CONTROL_2A   isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */
+#define AP_CONTROL_2B   isaIO2mem(AP_IOBASE+0xd) /* Control Register 2. Read/Write */
+#define AP_CONTROL_3A   isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */
+#define AP_CONTROL_3B   isaIO2mem(AP_IOBASE+0xf) /* Control Register 3a. Read/Write */
+
+
+#define FRAME_BUFFER_START 0x0A0000
+#define FRAME_BUFFER_LEN 0x20000
+
+/* CREG 0 */
+#define VECTOR_MODE 0x40 /* 010x.xxxx */
+#define DBLT_MODE   0x80 /* 100x.xxxx */
+#define NORMAL_MODE 0xE0 /* 111x.xxxx */
+#define SHIFT_BITS  0x1F /* xxx1.1111 */
+        /* other bits are Shift value */
+
+/* CREG 1 */
+#define AD_BLT      0x80 /* 1xxx.xxxx */
+
+#define ROP_EN          0x10 /* xxx1.xxxx */
+#define DST_EQ_SRC      0x00 /* xxx0.xxxx */
+#define nRESET_SYNC     0x08 /* xxxx.1xxx */
+#define SYNC_ENAB       0x02 /* xxxx.xx1x */
+
+#define BLANK_DISP      0x00 /* xxxx.xxx0 */
+#define ENAB_DISP       0x01 /* xxxx.xxx1 */
+
+#define NORM_CREG1      (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
+
+/* CREG 2B */
+
+/*
+ * Following 3 defines are common to 1, 4 and 8 plane.
+ */
+
+#define S_DATA_1s   0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
+#define S_DATA_PIX  0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
+#define S_DATA_PLN  0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
+ one plane of image mem */
+
+/* CREG 3A/CREG 3B */
+#       define RESET_CREG 0x80 /* 1000.0000 */
+
+/* ROP REG  -  all one nibble */
+/*      ********* NOTE : this is used r0,r1,r2,r3 *********** */
+#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
+#define DEST_ZERO               0x0
+#define SRC_AND_DEST    0x1
+#define SRC_AND_nDEST   0x2
+#define SRC                             0x3
+#define nSRC_AND_DEST   0x4
+#define DEST                    0x5
+#define SRC_XOR_DEST    0x6
+#define SRC_OR_DEST             0x7
+#define SRC_NOR_DEST    0x8
+#define SRC_XNOR_DEST   0x9
+#define nDEST                   0xA
+#define SRC_OR_nDEST    0xB
+#define nSRC                    0xC
+#define nSRC_OR_DEST    0xD
+#define SRC_NAND_DEST   0xE
+#define DEST_ONE                0xF
+
+#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
+
+
+void dn_video_setup(char *options, int *ints);
+
+/* frame buffer operations */
+
+static int dn_fb_open(struct fb_info *info);
+static int dn_fb_release(struct fb_info *info);
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, 
+			 struct fb_info *info);
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive,
+			 struct fb_info *info);
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		       unsigned long arg, int con, struct fb_info *info);
+
+static int dnfbcon_switch(int con,struct fb_info *info);
+static int dnfbcon_updatevar(int con,struct fb_info *info);
+static void dnfbcon_blank(int blank,struct fb_info *info);
+
+static void dn_fb_set_disp(int con,struct fb_info *info);
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static struct fb_ops dn_fb_ops = { 
+	dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var,
+	dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl
+};
+
+static int currcon=0;
+
+#define NUM_TOTAL_MODES 1
+struct fb_var_screeninfo dn_fb_predefined[] = {
+
+	{ 0, },
+
+};
+
+static char dn_fb_name[]="Apollo ";
+
+/* accel stuff */
+#define USE_DN_ACCEL
+
+static struct display_switch dispsw_apollofb;
+
+static int dn_fb_open(struct fb_info *info)
+{
+        /*
+         * Nothing, only a usage count for the moment
+         */
+
+        MOD_INC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_release(struct fb_info *info)
+{
+        MOD_DEC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info) {
+
+	strcpy(fix->id,"Apollo Color8");
+	fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE);
+	fix->smem_len=FRAME_BUFFER_LEN;
+	fix->type=FB_TYPE_PACKED_PIXELS;
+	fix->type_aux=0;
+	fix->visual=FB_VISUAL_MONO10;
+	fix->xpanstep=0;
+	fix->ypanstep=0;
+	fix->ywrapstep=0;
+        fix->line_length=128;
+
+	return 0;
+
+}
+        
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+		
+	var->xres=1024;
+	var->yres=800;
+	var->xres_virtual=1024;
+	var->yres_virtual=1024;
+	var->xoffset=0;
+	var->yoffset=0;
+	var->bits_per_pixel=1;
+	var->grayscale=0;
+	var->nonstd=0;
+	var->activate=0;
+	var->height=-1;
+	var->width=-1;
+	var->pixclock=0;
+	var->left_margin=0;
+	var->right_margin=0;
+	var->hsync_len=0;
+	var->vsync_len=0;
+	var->sync=0;
+	var->vmode=FB_VMODE_NONINTERLACED;
+
+	return 0;
+
+}
+
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+
+        printk("fb_set_var\n");
+	if(var->xres!=1024) 
+		return -EINVAL;
+	if(var->yres!=800)
+		return -EINVAL;
+	if(var->xres_virtual!=1024)
+		return -EINVAL;
+	if(var->yres_virtual!=1024)
+		return -EINVAL;
+	if(var->xoffset!=0)
+		return -EINVAL;
+	if(var->yoffset!=0)
+		return -EINVAL;
+	if(var->bits_per_pixel!=1)
+		return -EINVAL;
+	if(var->grayscale!=0)
+		return -EINVAL;
+	if(var->nonstd!=0)
+		return -EINVAL;
+	if(var->activate!=0)
+		return -EINVAL;
+	if(var->pixclock!=0)
+		return -EINVAL;
+	if(var->left_margin!=0)
+		return -EINVAL;
+	if(var->right_margin!=0)
+		return -EINVAL;
+	if(var->hsync_len!=0)
+		return -EINVAL;
+	if(var->vsync_len!=0)
+		return -EINVAL;
+	if(var->sync!=0)
+		return -EINVAL;
+	if(var->vmode!=FB_VMODE_NONINTERLACED)
+		return -EINVAL;
+
+	return 0;
+
+}
+
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("get cmap not supported\n");
+
+	return -EINVAL;
+}
+
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("set cmap not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info) {
+
+	printk("panning not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long arg, int con, struct fb_info *info) {
+
+	printk("no IOCTLs as of yet.\n");
+
+	return -EINVAL;
+
+}
+
+static void dn_fb_set_disp(int con, struct fb_info *info) {
+
+  struct fb_fix_screeninfo fix;
+
+  dn_fb_get_fix(&fix,con, info);
+  if(con==-1) 
+    con=0;
+
+   disp[con].screen_base = (u_char *)fix.smem_start;
+printk("screenbase: %p\n",fix.smem_start);
+   disp[con].visual = fix.visual;
+   disp[con].type = fix.type;
+   disp[con].type_aux = fix.type_aux;
+   disp[con].ypanstep = fix.ypanstep;
+   disp[con].ywrapstep = fix.ywrapstep;
+   disp[con].can_soft_blank = 1;
+   disp[con].inverse = 0;
+   disp[con].line_length = fix.line_length;
+   disp[con].dispsw = &dispsw_apollofb;
+}
+  
+unsigned long dn_fb_init(unsigned long mem_start) {
+
+	int err;
+       
+printk("dn_fb_init\n");
+
+	fb_info.changevar=NULL;
+	strcpy(&fb_info.modename[0],dn_fb_name);
+	fb_info.fontname[0]=0;
+	fb_info.disp=disp;
+	fb_info.switch_con=&dnfbcon_switch;
+	fb_info.updatevar=&dnfbcon_updatevar;
+	fb_info.blank=&dnfbcon_blank;	
+	fb_info.node = -1;
+	fb_info.fbops = &dn_fb_ops;
+	
+printk("dn_fb_init: register\n");
+	err=register_framebuffer(&fb_info);
+	if(err < 0) {
+		panic("unable to register apollo frame buffer\n");
+	}
+ 
+	/* now we have registered we can safely setup the hardware */
+
+        outb(RESET_CREG,  AP_CONTROL_3A);
+        outb(RESET_CREG,  AP_CONTROL_3B);
+        outw(0x0,  AP_WRITE_ENABLE);
+        outb(NORMAL_MODE, AP_CONTROL_0); 
+        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1),  AP_CONTROL_1);
+        outb(0,  AP_CONTROL_2A);
+        outb(S_DATA_PLN,  AP_CONTROL_2B);
+        outw(SWAP(0x3), AP_ROP_1);
+
+        printk("apollo frame buffer alive and kicking !\n");
+
+        dn_fb_get_var(&disp[0].var,0, &fb_info);
+
+	dn_fb_set_disp(-1, &fb_info);
+
+	return mem_start;
+
+}	
+
+	
+static int dnfbcon_switch(int con,  struct fb_info *info) { 
+
+	currcon=con;
+	
+	return 0;
+
+}
+
+static int dnfbcon_updatevar(int con,  struct fb_info *info) {
+
+	return 0;
+
+}
+
+static void dnfbcon_blank(int blank,  struct fb_info *info) {
+
+	if(blank)  {
+        	outb(0x0,  AP_CONTROL_3A);
+	}
+	else {
+	        outb(0x1,  AP_CONTROL_3A);
+	}
+
+	return ;
+
+}
+
+void dn_video_setup(char *options, int *ints) {
+	
+	return;
+
+}
+
+void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
+               int x_count, int y_count) {
+
+	int incr,y_delta,pre_read=0,x_end,x_word_count;
+	ushort *src,dummy;
+	uint start_mask,end_mask,dest;
+	short i,j;
+
+	incr=(y_dest<=y_src) ? 1 : -1 ;
+
+	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
+	
+	if(incr>0) {
+		y_delta=(p->next_line*8)-x_src-x_count;
+		x_end=x_dest+x_count-1;
+		x_word_count=(x_end>>4) - (x_dest >> 4) + 1;
+		start_mask=0xffff0000 >> (x_dest & 0xf);
+		end_mask=0x7ffff >> (x_end & 0xf);
+		outb((((x_dest & 0xf) - (x_src &0xf))  % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) < (x_src & 0xf))
+			pre_read=1;
+	}
+	else {
+		y_delta=-((p->next_line*8)-x_src-x_count);
+		x_end=x_dest-x_count+1;
+		x_word_count=(x_dest>>4) - (x_end >> 4) + 1;
+		start_mask=0x7ffff >> (x_dest & 0xf);
+		end_mask=0xffff0000 >> (x_end & 0xf);
+		outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) > (x_src & 0xf))
+			pre_read=1;
+	}
+
+	for(i=0;i<y_count;i++) {
+			
+		if(pre_read) {
+			dummy=*src;
+			src+=incr;
+		}
+
+		if(x_word_count) {
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			src+=incr;
+			dest+=incr;
+			outb(0,AP_WRITE_ENABLE);
+
+			for(j=1;j<(x_word_count-1);j++) {
+				*src=dest;
+				src+=incr;	
+				dest+=incr;
+			}
+
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		else {
+			outb(start_mask | end_mask, AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		src+=(y_delta/16);
+		dest+=(y_delta/16);
+	}
+	outb(NORMAL_MODE,AP_CONTROL_0);
+}
+
+static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
+{
+
+#ifdef USE_DN_ACCEL
+    dn_bitblt(p,sx,sy*p->fontheight,dx,dy*p->fontheight,width*p->fontwidth,
+	      height*p->fontheight);
+#else
+    u_char *src, *dest;
+    u_int rows;
+
+    if (sx == 0 && dx == 0 && width == p->next_line) {
+	src = p->screen_base+sy*p->fontheight*width;
+	dest = p->screen_base+dy*p->fontheight*width;
+	mymemmove(dest, src, height*p->fontheight*width);
+    } else if (dy <= sy) {
+	src = p->screen_base+sy*p->fontheight*p->next_line+sx;
+	dest = p->screen_base+dy*p->fontheight*p->next_line+dx;
+	for (rows = height*p->fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src += p->next_line;
+	    dest += p->next_line;
+	}
+    } else {
+	src = p->screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx;
+	dest = p->screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx;
+	for (rows = height*p->fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src -= p->next_line;
+	    dest -= p->next_line;
+	}
+    }
+#endif
+}
+
+static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+    u_char *dest;
+    u_int rows;
+
+    dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
+
+    if (sx == 0 && width == p->next_line)
+	if (attr_reverse(p,conp))
+	    mymemset(dest, height*p->fontheight*width);
+	else
+	    mymemclear(dest, height*p->fontheight*width);
+    else
+	for (rows = height*p->fontheight; rows--; dest += p->next_line)
+	    if (attr_reverse(p,conp))
+		mymemset(dest, width);
+	    else
+		mymemclear_small(dest, width);
+}
+
+static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
+{
+    u_char *dest, *cdat;
+    u_int rows, bold, revs, underl;
+    u_char d;
+
+    c &= 0xff;
+
+    dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    cdat = p->fontdata+c*p->fontheight;
+    bold = attr_bold(p,conp);
+    revs = attr_reverse(p,conp);
+    underl = attr_underline(p,conp);
+
+    for (rows = p->fontheight; rows--; dest += p->next_line) {
+	d = *cdat++;
+	if (underl && !rows)
+	    d = 0xff;
+	else if (bold)
+	    d |= d>>1;
+	if (revs)
+	    d = ~d;
+	*dest = d;
+    }
+}
+
+static void putcs_apollofb(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
+{
+    u_char *dest, *dest0, *cdat;
+    u_int rows, bold, revs, underl;
+    u_char c, d;
+
+    dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    bold = attr_bold(p,conp);
+    revs = attr_reverse(p,conp);
+    underl = attr_underline(p,conp);
+
+    while (count--) {
+	c = *s++;
+	dest = dest0++;
+	cdat = p->fontdata+c*p->fontheight;
+	for (rows = p->fontheight; rows--; dest += p->next_line) {
+	    d = *cdat++;
+	    if (underl && !rows)
+		d = 0xff;
+	    else if (bold)
+		d |= d>>1;
+	    if (revs)
+		d = ~d;
+	    *dest = d;
+	}
+    }
+}
+
+static void rev_char_apollofb(struct display *p, int xx, int yy)
+{
+    u_char *dest;
+    u_int rows;
+
+    dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    for (rows = p->fontheight; rows--; dest += p->next_line)
+	*dest = ~*dest;
+}
+
+static struct display_switch dispsw_apollofb = {
+    fbcon_mfb_setup, bmove_apollofb, clear_apollofb,
+    putc_apollofb, putcs_apollofb, rev_char_apollofb
+};
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./drivers/video/dn_fb.c	Wed Oct 27 00:30:00 1999
@@ -0,0 +1,551 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/apollohw.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include "dn_accel.h"
+#include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+
+/* apollo video HW definitions */
+
+/*
+ * Control Registers.   IOBASE + $x
+ *
+ * Note: these are the Memory/IO BASE definitions for a mono card set to the
+ * alternate address
+ *
+ * Control 3A and 3B serve identical functions except that 3A
+ * deals with control 1 and 3b deals with Color LUT reg.
+ */
+
+#define AP_IOBASE       0x3b0          /* Base address of 1 plane board. */
+#define AP_STATUS       isaIO2mem(AP_IOBASE+0) /* Status register.  Read */
+#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */
+#define AP_DEVICE_ID    isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */
+#define AP_ROP_1        isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */
+#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+4) /* Diagnostic Memory Request. Write Word */
+#define AP_CONTROL_0    isaIO2mem(AP_IOBASE+8) /* Control Register 0.  Read/Write */
+#define AP_CONTROL_1    isaIO2mem(AP_IOBASE+0xa) /* Control Register 1.  Read/Write */
+#define AP_CONTROL_3A   isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */
+#define AP_CONTROL_2    isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */
+
+
+#define FRAME_BUFFER_START 0x0FA0000
+#define FRAME_BUFFER_LEN 0x40000
+
+/* CREG 0 */
+#define VECTOR_MODE 0x40 /* 010x.xxxx */
+#define DBLT_MODE   0x80 /* 100x.xxxx */
+#define NORMAL_MODE 0xE0 /* 111x.xxxx */
+#define SHIFT_BITS  0x1F /* xxx1.1111 */
+        /* other bits are Shift value */
+
+/* CREG 1 */
+#define AD_BLT      0x80 /* 1xxx.xxxx */
+#define NORMAL      0x80 /* 1xxx.xxxx */   /* What is happening here ?? */
+#define INVERSE     0x00 /* 0xxx.xxxx */   /* Clearing this reverses the screen */
+#define PIX_BLT     0x00 /* 0xxx.xxxx */
+
+#define AD_HIBIT        0x40 /* xIxx.xxxx */
+
+#define ROP_EN          0x10 /* xxx1.xxxx */
+#define DST_EQ_SRC      0x00 /* xxx0.xxxx */
+#define nRESET_SYNC     0x08 /* xxxx.1xxx */
+#define SYNC_ENAB       0x02 /* xxxx.xx1x */
+
+#define BLANK_DISP      0x00 /* xxxx.xxx0 */
+#define ENAB_DISP       0x01 /* xxxx.xxx1 */
+
+#define NORM_CREG1      (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
+
+/* CREG 2 */
+
+/*
+ * Following 3 defines are common to 1, 4 and 8 plane.
+ */
+
+#define S_DATA_1s   0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
+#define S_DATA_PIX  0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
+#define S_DATA_PLN  0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
+ one plane of image mem */
+
+/* CREG 3A/CREG 3B */
+#       define RESET_CREG 0x80 /* 1000.0000 */
+
+/* ROP REG  -  all one nibble */
+/*      ********* NOTE : this is used r0,r1,r2,r3 *********** */
+#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
+#define DEST_ZERO               0x0
+#define SRC_AND_DEST    0x1
+#define SRC_AND_nDEST   0x2
+#define SRC                             0x3
+#define nSRC_AND_DEST   0x4
+#define DEST                    0x5
+#define SRC_XOR_DEST    0x6
+#define SRC_OR_DEST             0x7
+#define SRC_NOR_DEST    0x8
+#define SRC_XNOR_DEST   0x9
+#define nDEST                   0xA
+#define SRC_OR_nDEST    0xB
+#define nSRC                    0xC
+#define nSRC_OR_DEST    0xD
+#define SRC_NAND_DEST   0xE
+#define DEST_ONE                0xF
+
+#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
+
+#if 0
+#define outb(a,d) *(char *)(a)=(d)
+#define outw(a,d) *(unsigned short *)a=d
+#endif
+
+
+void dn_video_setup(char *options, int *ints);
+
+/* frame buffer operations */
+
+static int dn_fb_open(struct fb_info *info,int user);
+static int dn_fb_release(struct fb_info *info,int user);
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, 
+			 struct fb_info *info);
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive,
+			 struct fb_info *info);
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info);
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		       unsigned long arg, int con, struct fb_info *info);
+
+static int dnfbcon_switch(int con,struct fb_info *info);
+static int dnfbcon_updatevar(int con,struct fb_info *info);
+static void dnfbcon_blank(int blank,struct fb_info *info);
+
+static void dn_fb_set_disp(int con,struct fb_info *info);
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static struct fb_ops dn_fb_ops = { 
+	dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var,
+	dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl
+};
+
+static int currcon=0;
+
+#define NUM_TOTAL_MODES 1
+struct fb_var_screeninfo dn_fb_predefined[] = {
+
+	{ 0, },
+
+};
+
+static char dn_fb_name[]="Apollo ";
+
+/* accel stuff */
+#define USE_DN_ACCEL
+
+static struct display_switch dispsw_apollofb;
+
+static int dn_fb_open(struct fb_info *info,int user)
+{
+        /*
+         * Nothing, only a usage count for the moment
+         */
+
+        MOD_INC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_release(struct fb_info *info,int user)
+{
+        MOD_DEC_USE_COUNT;
+        return(0);
+}
+
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info) {
+
+	strcpy(fix->id,"Apollo Mono");
+	fix->smem_start=(FRAME_BUFFER_START+IO_BASE);
+	fix->smem_len=FRAME_BUFFER_LEN;
+	fix->type=FB_TYPE_PACKED_PIXELS;
+	fix->type_aux=0;
+	fix->visual=FB_VISUAL_MONO10;
+	fix->xpanstep=0;
+	fix->ypanstep=0;
+	fix->ywrapstep=0;
+        fix->line_length=256;
+
+	return 0;
+
+}
+        
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+		
+	var->xres=1280;
+	var->yres=1024;
+	var->xres_virtual=2048;
+	var->yres_virtual=1024;
+	var->xoffset=0;
+	var->yoffset=0;
+	var->bits_per_pixel=1;
+	var->grayscale=0;
+	var->nonstd=0;
+	var->activate=0;
+	var->height=-1;
+	var->width=-1;
+	var->pixclock=0;
+	var->left_margin=0;
+	var->right_margin=0;
+	var->hsync_len=0;
+	var->vsync_len=0;
+	var->sync=0;
+	var->vmode=FB_VMODE_NONINTERLACED;
+
+	return 0;
+
+}
+
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info) {
+
+        printk("fb_set_var\n");
+	if(var->xres!=1280) 
+		return -EINVAL;
+	if(var->yres!=1024)
+		return -EINVAL;
+	if(var->xres_virtual!=2048)
+		return -EINVAL;
+	if(var->yres_virtual!=1024)
+		return -EINVAL;
+	if(var->xoffset!=0)
+		return -EINVAL;
+	if(var->yoffset!=0)
+		return -EINVAL;
+	if(var->bits_per_pixel!=1)
+		return -EINVAL;
+	if(var->grayscale!=0)
+		return -EINVAL;
+	if(var->nonstd!=0)
+		return -EINVAL;
+	if(var->activate!=0)
+		return -EINVAL;
+	if(var->pixclock!=0)
+		return -EINVAL;
+	if(var->left_margin!=0)
+		return -EINVAL;
+	if(var->right_margin!=0)
+		return -EINVAL;
+	if(var->hsync_len!=0)
+		return -EINVAL;
+	if(var->vsync_len!=0)
+		return -EINVAL;
+	if(var->sync!=0)
+		return -EINVAL;
+	if(var->vmode!=FB_VMODE_NONINTERLACED)
+		return -EINVAL;
+
+	return 0;
+
+}
+
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("get cmap not supported\n");
+
+	return -EINVAL;
+}
+
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			  struct fb_info *info) {
+
+	printk("set cmap not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info) {
+
+	printk("panning not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long arg, int con, struct fb_info *info) {
+
+	printk("no IOCTLs as of yet.\n");
+
+	return -EINVAL;
+
+}
+
+static void dn_fb_set_disp(int con, struct fb_info *info) {
+
+  struct fb_fix_screeninfo fix;
+  struct display *display;
+  
+  dn_fb_get_fix(&fix,con, info);
+
+  if (con>=0)
+	display=&fb_display[con];
+  else
+	display=&disp[0];
+
+  if(con==-1) 
+    con=0;
+
+   display->screen_base = (u_char *)fix.smem_start;
+   display->visual = fix.visual;
+   display->type = fix.type;
+   display->type_aux = fix.type_aux;
+   display->ypanstep = fix.ypanstep;
+   display->ywrapstep = fix.ywrapstep;
+   display->can_soft_blank = 1;
+   display->inverse = 0;
+   display->line_length = fix.line_length;
+#ifdef FBCON_HAS_MFB
+   display->dispsw = &fbcon_mfb;
+#else
+   display->dispsw=&fbcon_dummy;
+#endif
+
+}
+  
+unsigned long dnfb_init(unsigned long mem_start) {
+
+	int err;
+       
+
+	fb_info.changevar=NULL;
+	strcpy(&fb_info.modename[0],dn_fb_name);
+	fb_info.fontname[0]=0;
+	fb_info.disp=disp;
+	fb_info.switch_con=&dnfbcon_switch;
+	fb_info.updatevar=&dnfbcon_updatevar;
+	fb_info.blank=&dnfbcon_blank;	
+	fb_info.node = -1;
+	fb_info.fbops = &dn_fb_ops;
+	
+        dn_fb_get_var(&disp[0].var,0, &fb_info);
+
+	dn_fb_set_disp(-1, &fb_info);
+	
+	err=register_framebuffer(&fb_info);
+	if(err < 0) {
+		panic("unable to register apollo frame buffer\n");
+	}
+ 
+	/* now we have registered we can safely setup the hardware */
+
+        outb(RESET_CREG,  AP_CONTROL_3A);
+        outw(0x0,  AP_WRITE_ENABLE);
+        outb(NORMAL_MODE, AP_CONTROL_0); 
+        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1),  AP_CONTROL_1);
+        outb(S_DATA_PLN,  AP_CONTROL_2);
+        outw(SWAP(0x3), AP_ROP_1);
+
+        printk("apollo frame buffer alive and kicking !\n");
+
+
+	return mem_start;
+
+}	
+
+	
+static int dnfbcon_switch(int con,  struct fb_info *info) { 
+
+	currcon=con;
+	
+	return 0;
+
+}
+
+static int dnfbcon_updatevar(int con,  struct fb_info *info) {
+
+	return 0;
+
+}
+
+static void dnfbcon_blank(int blank,  struct fb_info *info) {
+
+	if(blank)  {
+        	outb(0x0,  AP_CONTROL_3A);
+	}
+	else {
+	        outb(0x1,  AP_CONTROL_3A);
+	}
+
+	return ;
+
+}
+
+void dn_video_setup(char *options, int *ints) {
+	
+	return;
+
+}
+
+void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
+               int x_count, int y_count) {
+
+	int incr,y_delta,pre_read=0,x_end,x_word_count;
+	ushort *src,dummy;
+	uint start_mask,end_mask,dest;
+	short i,j;
+
+	incr=(y_dest<=y_src) ? 1 : -1 ;
+
+	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
+	
+	if(incr>0) {
+		y_delta=(p->next_line*8)-x_src-x_count;
+		x_end=x_dest+x_count-1;
+		x_word_count=(x_end>>4) - (x_dest >> 4) + 1;
+		start_mask=0xffff0000 >> (x_dest & 0xf);
+		end_mask=0x7ffff >> (x_end & 0xf);
+		outb((((x_dest & 0xf) - (x_src &0xf))  % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) < (x_src & 0xf))
+			pre_read=1;
+	}
+	else {
+		y_delta=-((p->next_line*8)-x_src-x_count);
+		x_end=x_dest-x_count+1;
+		x_word_count=(x_dest>>4) - (x_end >> 4) + 1;
+		start_mask=0x7ffff >> (x_dest & 0xf);
+		end_mask=0xffff0000 >> (x_end & 0xf);
+		outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0);
+		if((x_dest & 0xf) > (x_src & 0xf))
+			pre_read=1;
+	}
+
+	for(i=0;i<y_count;i++) {
+
+		outb(0xc | (dest >> 16), AP_CONTROL_3A);
+			
+		if(pre_read) {
+			dummy=*src;
+			src+=incr;
+		}
+
+		if(x_word_count) {
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			src+=incr;
+			dest+=incr;
+			outb(0,AP_WRITE_ENABLE);
+
+			for(j=1;j<(x_word_count-1);j++) {
+				*src=dest;
+				src+=incr;	
+				dest+=incr;
+			}
+
+			outb(start_mask,AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		else {
+			outb(start_mask | end_mask, AP_WRITE_ENABLE);
+			*src=dest;
+			dest+=incr;
+			src+=incr;
+		}
+		src+=(y_delta/16);
+		dest+=(y_delta/16);
+	}
+	outb(NORMAL_MODE,AP_CONTROL_0);
+}
+
+static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
+{
+
+    int fontheight,fontwidth;
+
+    fontheight=fontheight(p);
+    fontwidth=fontwidth(p);
+
+#ifdef USE_DN_ACCEL
+    dn_bitblt(p,sx,sy*fontheight,dx,dy*fontheight,width*fontwidth,
+	      height*fontheight);
+#else
+    u_char *src, *dest;
+    u_int rows;
+
+    if (sx == 0 && dx == 0 && width == p->next_line) {
+	src = p->screen_base+sy*fontheight*width;
+	dest = p->screen_base+dy*fontheight*width;
+	mymemmove(dest, src, height*fontheight*width);
+    } else if (dy <= sy) {
+	src = p->screen_base+sy*fontheight*next_line+sx;
+	dest = p->screen_base+dy*fontheight*next_line+dx;
+	for (rows = height*fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src += p->next_line;
+	    dest += p->next_line;
+	}
+    } else {
+	src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
+	dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
+	for (rows = height*fontheight; rows--;) {
+	    mymemmove(dest, src, width);
+	    src -= p->next_line;
+	    dest -= p->next_line;
+	}
+    }
+#endif
+}
+
+static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+	fbcon_mfb_clear(conp,p,sy,sx,height,width);
+}
+
+static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
+{
+	fbcon_mfb_putc(conp,p,c,yy,xx);
+}
+
+static void putcs_apollofb(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
+{
+	fbcon_mfb_putcs(conp,p,s,count,yy,xx);
+}
+
+static void rev_char_apollofb(struct display *p, int xx, int yy)
+{
+	fbcon_mfb_revc(p,xx,yy);
+}
+
+static struct display_switch dispsw_apollofb = {
+    fbcon_mfb_setup, bmove_apollofb, clear_apollofb,
+    putc_apollofb, putcs_apollofb, rev_char_apollofb
+};
--- ../linux-2.3.16/./include/asm-m68k/apollohw.h	Sat Nov  1 19:01:37 1997
+++ ./include/asm-m68k/apollohw.h	Sun Oct 17 01:57:13 1999
@@ -3,6 +3,18 @@
 #ifndef _ASMm68k_APOLLOHW_H_
 #define _ASMm68k_APOLLOHW_H_
 
+/*
+   apollo models 
+*/
+
+extern u_long apollo_model;
+
+#define APOLLO_UNKNOWN (0)
+#define APOLLO_DN3000 (1)
+#define APOLLO_DN3010 (2)
+#define APOLLO_DN3500 (3)
+#define APOLLO_DN4000 (4)
+#define APOLLO_DN4500 (5)
 
 /* 
    see scn2681 data sheet for more info. 
@@ -52,16 +64,42 @@
         unsigned char month, year;
 };
 
+
 #define IO_BASE 0x80000000
 
-#define SIO01_PHYSADDR 0x10400
-#define SIO23_PHYSADDR 0x10500
-#define RTC_PHYSADDR 0x10900
-#define PICA 0x11000
-#define PICB 0x11100
-#define sio01 ((*(volatile struct SCN2681 *)(IO_BASE + SIO01_PHYSADDR)))
-#define sio23 ((*(volatile struct SCN2681 *)(IO_BASE + SIO01_PHYSADDR)))
-#define rtc (((volatile struct mc146818 *)(IO_BASE + RTC_PHYSADDR)))
+extern u_long sio01_physaddr;
+extern u_long sio23_physaddr;
+extern u_long rtc_physaddr;
+extern u_long pica_physaddr;
+extern u_long picb_physaddr;
+extern u_long cpuctrl_physaddr;
+extern u_long timer_physaddr;
+
+#define SAU7_SIO01_PHYSADDR 0x10400
+#define SAU7_SIO23_PHYSADDR 0x10500
+#define SAU7_RTC_PHYSADDR 0x10900
+#define SAU7_PICA 0x11000
+#define SAU7_PICB 0x11100
+#define SAU7_CPUCTRL 0x10100
+#define SAU7_TIMER 0x010800
+
+#define SAU8_SIO01_PHYSADDR 0x8400
+#define SAU8_RTC_PHYSADDR 0x8900
+#define SAU8_PICA 0x9400
+#define SAU8_PICB 0x9500
+#define SAU8_CPUCTRL 0x8100
+#define SAU8_TIMER 0x8800
+
+#define sio01 ((*(volatile struct SCN2681 *)(IO_BASE + sio01_physaddr)))
+#define sio23 ((*(volatile struct SCN2681 *)(IO_BASE + sio23_physaddr)))
+#define rtc (((volatile struct mc146818 *)(IO_BASE + rtc_physaddr)))
+#define cpuctrl (*(volatile unsigned int *)(IO_BASE + cpuctrl_physaddr))
+#define pica (IO_BASE + pica_physaddr)
+#define picb (IO_BASE + picb_physaddr)
+#define timer (IO_BASE + timer_physaddr)	
+#define addr_xlat_map ((unsigned short *)(IO_BASE + 0x17000))
+
+#define isaIO2mem(x) (((((x) & 0x3f8)  << 7) | (((x) & 0xfc00) >> 6) | ((x) & 0x7)) + 0x40000 + IO_BASE)
 
 #define inb(addr) (*((volatile unsigned char *)(addr)))
 #define outb(val,addr) (*((volatile unsigned char *)(addr)) = (val))
--- ../linux-2.3.16/./include/asm-m68k/bootinfo.h	Sun May  9 16:19:02 1999
+++ ./include/asm-m68k/bootinfo.h	Fri Oct 15 06:03:05 1999
@@ -184,6 +184,14 @@
 #endif
 
     /*
+     *  Apollo-specific tags
+     */
+
+#define BI_APOLLO_MODEL         0x8000  /* model (u_long) */
+
+
+
+    /*
      * Stuff for bootinfo interface versioning
      *
      * At the start of kernel code, a 'struct bootversion' is located.
--- /dev/null	Wed Mar  3 18:20:11 1999
+++ ./include/asm-m68k/apollodma.h	Sun Oct 17 01:57:58 1999
@@ -0,0 +1,249 @@
+/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $
+ * linux/include/asm/dma.h: Defines for using and allocating dma channels.
+ * Written by Hennus Bergman, 1992.
+ * High DMA channel support & info by Hannu Savolainen
+ * and John Boyd, Nov. 1992.
+ */
+
+#ifndef _ASM_APOLLO_DMA_H
+#define _ASM_APOLLO_DMA_H
+
+#include <linux/config.h>
+#include <asm/apollohw.h>		/* need byte IO */
+#include <asm/spinlock.h>	/* And spinlocks */
+#include <linux/delay.h>
+
+
+#define dma_outb(val,addr) (*((volatile unsigned char *)(addr+IO_BASE)) = (val))
+#define dma_inb(addr)	   (*((volatile unsigned char *)(addr+IO_BASE)))
+
+/*
+ * NOTES about DMA transfers:
+ *
+ *  controller 1: channels 0-3, byte operations, ports 00-1F
+ *  controller 2: channels 4-7, word operations, ports C0-DF
+ *
+ *  - ALL registers are 8 bits only, regardless of transfer size
+ *  - channel 4 is not used - cascades 1 into 2.
+ *  - channels 0-3 are byte - addresses/counts are for physical bytes
+ *  - channels 5-7 are word - addresses/counts are for physical words
+ *  - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
+ *  - transfer count loaded to registers is 1 less than actual count
+ *  - controller 2 offsets are all even (2x offsets for controller 1)
+ *  - page registers for 5-7 don't use data bit 0, represent 128K pages
+ *  - page registers for 0-3 use bit 0, represent 64K pages
+ *
+ * DMA transfers are limited to the lower 16MB of _physical_ memory.  
+ * Note that addresses loaded into registers must be _physical_ addresses,
+ * not logical addresses (which may differ if paging is active).
+ *
+ *  Address mapping for channels 0-3:
+ *
+ *   A23 ... A16 A15 ... A8  A7 ... A0    (Physical addresses)
+ *    |  ...  |   |  ... |   |  ... |
+ *    |  ...  |   |  ... |   |  ... |
+ *    |  ...  |   |  ... |   |  ... |
+ *   P7  ...  P0  A7 ... A0  A7 ... A0   
+ * |    Page    | Addr MSB | Addr LSB |   (DMA registers)
+ *
+ *  Address mapping for channels 5-7:
+ *
+ *   A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0    (Physical addresses)
+ *    |  ...  |   \   \   ... \  \  \  ... \  \
+ *    |  ...  |    \   \   ... \  \  \  ... \  (not used)
+ *    |  ...  |     \   \   ... \  \  \  ... \
+ *   P7  ...  P1 (0) A7 A6  ... A0 A7 A6 ... A0   
+ * |      Page      |  Addr MSB   |  Addr LSB  |   (DMA registers)
+ *
+ * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
+ * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
+ * the hardware level, so odd-byte transfers aren't possible).
+ *
+ * Transfer count (_not # bytes_) is limited to 64K, represented as actual
+ * count - 1 : 64K => 0xFFFF, 1 => 0x0000.  Thus, count is always 1 or more,
+ * and up to 128K bytes may be transferred on channels 5-7 in one operation. 
+ *
+ */
+
+#define MAX_DMA_CHANNELS	8
+
+/* The maximum address that we can perform a DMA transfer to on this platform */#define MAX_DMA_ADDRESS      (PAGE_OFFSET+0x1000000)
+
+/* 8237 DMA controllers */
+#define IO_DMA1_BASE	0x10C00	/* 8 bit slave DMA, channels 0..3 */
+#define IO_DMA2_BASE	0x10D00	/* 16 bit master DMA, ch 4(=slave input)..7 */
+
+/* DMA controller registers */
+#define DMA1_CMD_REG		(IO_DMA1_BASE+0x08) /* command register (w) */
+#define DMA1_STAT_REG		(IO_DMA1_BASE+0x08) /* status register (r) */
+#define DMA1_REQ_REG            (IO_DMA1_BASE+0x09) /* request register (w) */
+#define DMA1_MASK_REG		(IO_DMA1_BASE+0x0A) /* single-channel mask (w) */
+#define DMA1_MODE_REG		(IO_DMA1_BASE+0x0B) /* mode register (w) */
+#define DMA1_CLEAR_FF_REG	(IO_DMA1_BASE+0x0C) /* clear pointer flip-flop (w) */
+#define DMA1_TEMP_REG           (IO_DMA1_BASE+0x0D) /* Temporary Register (r) */
+#define DMA1_RESET_REG		(IO_DMA1_BASE+0x0D) /* Master Clear (w) */
+#define DMA1_CLR_MASK_REG       (IO_DMA1_BASE+0x0E) /* Clear Mask */
+#define DMA1_MASK_ALL_REG       (IO_DMA1_BASE+0x0F) /* all-channels mask (w) */
+
+#define DMA2_CMD_REG		(IO_DMA2_BASE+0x10) /* command register (w) */
+#define DMA2_STAT_REG		(IO_DMA2_BASE+0x10) /* status register (r) */
+#define DMA2_REQ_REG            (IO_DMA2_BASE+0x12) /* request register (w) */
+#define DMA2_MASK_REG		(IO_DMA2_BASE+0x14) /* single-channel mask (w) */
+#define DMA2_MODE_REG		(IO_DMA2_BASE+0x16) /* mode register (w) */
+#define DMA2_CLEAR_FF_REG	(IO_DMA2_BASE+0x18) /* clear pointer flip-flop (w) */
+#define DMA2_TEMP_REG           (IO_DMA2_BASE+0x1A) /* Temporary Register (r) */
+#define DMA2_RESET_REG		(IO_DMA2_BASE+0x1A) /* Master Clear (w) */
+#define DMA2_CLR_MASK_REG       (IO_DMA2_BASE+0x1C) /* Clear Mask */
+#define DMA2_MASK_ALL_REG       (IO_DMA2_BASE+0x1E) /* all-channels mask (w) */
+
+#define DMA_ADDR_0              (IO_DMA1_BASE+0x00) /* DMA address registers */
+#define DMA_ADDR_1              (IO_DMA1_BASE+0x02)
+#define DMA_ADDR_2              (IO_DMA1_BASE+0x04)
+#define DMA_ADDR_3              (IO_DMA1_BASE+0x06)
+#define DMA_ADDR_4              (IO_DMA2_BASE+0x00)
+#define DMA_ADDR_5              (IO_DMA2_BASE+0x04)
+#define DMA_ADDR_6              (IO_DMA2_BASE+0x08)
+#define DMA_ADDR_7              (IO_DMA2_BASE+0x0C)
+
+#define DMA_CNT_0               (IO_DMA1_BASE+0x01)   /* DMA count registers */
+#define DMA_CNT_1               (IO_DMA1_BASE+0x03)
+#define DMA_CNT_2               (IO_DMA1_BASE+0x05)
+#define DMA_CNT_3               (IO_DMA1_BASE+0x07)
+#define DMA_CNT_4               (IO_DMA2_BASE+0x02)
+#define DMA_CNT_5               (IO_DMA2_BASE+0x06)
+#define DMA_CNT_6               (IO_DMA2_BASE+0x0A)
+#define DMA_CNT_7               (IO_DMA2_BASE+0x0E)
+
+#define DMA_MODE_READ	0x44	/* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE	0x48	/* memory to I/O, no autoinit, increment, single mode */
+#define DMA_MODE_CASCADE 0xC0   /* pass thru DREQ->HRQ, DACK<-HLDA only */
+
+#define DMA_AUTOINIT	0x10
+
+#define DMA_8BIT 0
+#define DMA_16BIT 1
+#define DMA_BUSMASTER 2
+
+extern spinlock_t  dma_spin_lock;
+
+static __inline__ unsigned long claim_dma_lock(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dma_spin_lock, flags);
+	return flags;
+}
+
+static __inline__ void release_dma_lock(unsigned long flags)
+{
+	spin_unlock_irqrestore(&dma_spin_lock, flags);
+}
+
+/* enable/disable a specific DMA channel */
+static __inline__ void enable_dma(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		dma_outb(dmanr,  DMA1_MASK_REG);
+	else
+		dma_outb(dmanr & 3,  DMA2_MASK_REG);
+}
+
+static __inline__ void disable_dma(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		dma_outb(dmanr | 4,  DMA1_MASK_REG);
+	else
+		dma_outb((dmanr & 3) | 4,  DMA2_MASK_REG);
+}
+
+/* Clear the 'DMA Pointer Flip Flop'.
+ * Write 0 for LSB/MSB, 1 for MSB/LSB access.
+ * Use this once to initialize the FF to a known state.
+ * After that, keep track of it. :-)
+ * --- In order to do that, the DMA routines below should ---
+ * --- only be used while holding the DMA lock ! ---
+ */
+static __inline__ void clear_dma_ff(unsigned int dmanr)
+{
+	if (dmanr<=3)
+		dma_outb(0,  DMA1_CLEAR_FF_REG);
+	else
+		dma_outb(0,  DMA2_CLEAR_FF_REG);
+}
+
+/* set mode (above) for a specific DMA channel */
+static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
+{
+	if (dmanr<=3)
+		dma_outb(mode | dmanr,  DMA1_MODE_REG);
+	else
+		dma_outb(mode | (dmanr&3),  DMA2_MODE_REG);
+}
+
+/* Set transfer address & page bits for specific DMA channel.
+ * Assumes dma flipflop is clear.
+ */
+static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
+{
+	if (dmanr <= 3)  {
+	    dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
+            dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
+	}  else  {
+	    dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+	    dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+	}
+}
+
+
+/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
+ * a specific DMA channel.
+ * You must ensure the parameters are valid.
+ * NOTE: from a manual: "the number of transfers is one more
+ * than the initial word count"! This is taken into account.
+ * Assumes dma flip-flop is clear.
+ * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
+ */
+static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
+{
+        count--;
+	if (dmanr <= 3)  {
+	    dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+	    dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+        } else {
+	    dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+	    dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+        }
+}
+
+
+/* Get DMA residue count. After a DMA transfer, this
+ * should return zero. Reading this while a DMA transfer is
+ * still in progress will return unpredictable results.
+ * If called before the channel has been used, it may return 1.
+ * Otherwise, it returns the number of _bytes_ left to transfer.
+ *
+ * Assumes DMA flip-flop is clear.
+ */
+static __inline__ int get_dma_residue(unsigned int dmanr)
+{
+	unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
+					 : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
+
+	/* using short to get 16-bit wrap around */
+	unsigned short count;
+
+	count = 1 + dma_inb(io_port);
+	count += dma_inb(io_port) << 8;
+	
+	return (dmanr<=3)? count : (count<<1);
+}
+
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr, const char * device_id);	/* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr);	/* release it again */
+
+/* These are in arch/m68k/apollo/dma.c: */
+extern unsigned short dma_map_page(unsigned long phys_addr,int count,int type);
+extern void dma_unmap_page(unsigned short dma_addr);
+
+#endif /* _ASM_APOLLO_DMA_H */
--- ../linux-2.3.16/./init/main.c	Fri Sep  3 16:18:32 1999
+++ ./init/main.c	Sat Oct 16 01:40:41 1999
@@ -452,6 +452,7 @@
 {
 	char * command_line;
 
+
 #ifdef __SMP__
 	static int boot_cpu = 1;
 	/* "current" has been set up, we need to load it now */

--huq684BweRXVnRxX--

