diff -u --recursive --new-file v2.1.63/linux/CREDITS linux/CREDITS --- v2.1.63/linux/CREDITS Wed Nov 12 13:34:25 1997 +++ linux/CREDITS Fri Nov 14 10:55:07 1997 @@ -981,7 +981,7 @@ N: John A. Martin E: jam@acm.org -W: http://linux.wauug.org/~jam/ +W: http://www.tux.org/~jam/ P: 1024/04456D53 9D A3 6C 6B 88 80 8A 61 D7 06 22 4F 95 40 CE D2 P: 1024/3B986635 5A61 7EE6 9E20 51FB 59FB 2DA5 3E18 DD55 3B98 6635 D: FSSTND contributor @@ -1145,9 +1145,10 @@ S: Germany N: David C. Niemi -E: niemi@erols.com +E: niemi@tux.org +W: http://www.tux.org/~niemi/ D: Assistant maintainer of Mtools, fdutils, and floppy driver -D: Administrator of WAUUG Linux Server, http://linux.wauug.org +D: Administrator of Tux.Org Linux Server, http://www.tux.org S: 2364 Old Trail Drive S: Reston, Virginia 20191 S: USA diff -u --recursive --new-file v2.1.63/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.63/linux/Documentation/Configure.help Wed Nov 12 13:34:25 1997 +++ linux/Documentation/Configure.help Thu Nov 13 12:56:48 1997 @@ -2367,13 +2367,6 @@ your EPP chipset is from the SMC series, you are likely to have to set this value greater than 0. -EPP Timing -CONFIG_SCSI_PPA_EPP_TIME - This is the "reset time period", a delay time. The lower the value, - the faster the access to the ZIP drive; too low a value may - cause all sorts of mid-level SCSI problems however. If unsure, go - with the default. - Network device support? CONFIG_NETDEVICES You can say N here in case you don't intend to connect to any other @@ -5260,7 +5253,7 @@ CONFIG_MISC_RADIO If you have a radio card (you will probably know if you do!), then you will want to say "y" here and make a character device file - (usually /dev/radio) with major number 10 and minor 129 using mknod + (usually /dev/radio) with major number 10 and minor 152 using mknod ("man mknod"). And then, don't forget to pick up some useful tools to use said device (you _might_ find something at ftp.lmh.ox.ac.uk: /users/weejock/linux/radio/, but I haven't written anything too diff -u --recursive --new-file v2.1.63/linux/Makefile linux/Makefile --- v2.1.63/linux/Makefile Wed Nov 12 13:34:25 1997 +++ linux/Makefile Wed Nov 12 12:11:41 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 63 +SUBLEVEL = 64 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.63/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.1.63/linux/arch/i386/kernel/head.S Mon Aug 11 14:47:04 1997 +++ linux/arch/i386/kernel/head.S Fri Nov 14 17:52:12 1997 @@ -289,7 +289,7 @@ movw %dx,%ax /* selector = 0x0010 = cs */ movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ - lea SYMBOL_NAME(idt),%edi + lea SYMBOL_NAME(idt_table),%edi mov $256,%ecx rp_sidt: movl %eax,(%edi) @@ -328,23 +328,33 @@ iret /* - * The interrupt descriptor table has room for 256 idt's + * The interrupt descriptor table has room for 256 idt's, + * the global descriptor table is dependent on the number + * of tasks we can have.. */ +#define IDT_ENTRIES 256 +#ifdef CONFIG_APM +#define GDT_ENTRIES (11+2*NR_TASKS) +#else +#define GDT_ENTRIES (8+2*NR_TASKS) +#endif + + +.globl SYMBOL_NAME(idt) +.globl SYMBOL_NAME(gdt) + ALIGN -.word 0 + .word 0 idt_descr: - .word 256*8-1 # idt contains 256 entries - .long SYMBOL_NAME(idt) + .word IDT_ENTRIES*8-1 # idt contains 256 entries +SYMBOL_NAME(idt): + .long SYMBOL_NAME(idt_table) - ALIGN -.word 0 + .word 0 gdt_descr: -#ifdef CONFIG_APM - .word (11+2*NR_TASKS)*8-1 -#else - .word (8+2*NR_TASKS)*8-1 -#endif - .long SYMBOL_NAME(gdt) + .word GDT_ENTRIES*8-1 +SYMBOL_NAME(gdt): + .long SYMBOL_NAME(gdt_table) /* * This is initialized to create a identity-mapping at 0-4M (for bootup @@ -515,7 +525,7 @@ ALIGN /* 256 quadwords - 2048 bytes of idt */ -ENTRY(idt) +ENTRY(idt_table) .fill 256,8,0 # idt is uninitialized /* @@ -528,7 +538,7 @@ * NOTE! Make sure the gdt descriptor in head.S matches this if you * change anything. */ -ENTRY(gdt) +ENTRY(gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* not used */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ diff -u --recursive --new-file v2.1.63/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.1.63/linux/arch/i386/kernel/setup.c Wed Nov 12 13:34:25 1997 +++ linux/arch/i386/kernel/setup.c Fri Nov 14 18:54:43 1997 @@ -360,7 +360,7 @@ "fdiv_bug\t: %s\n" "hlt_bug\t\t: %s\n" "sep_bug\t\t: %s\n" - "pentium_f00f_bug\t\t: %s\n" + "f00f_bug\t: %s\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid\t\t: %s\n" diff -u --recursive --new-file v2.1.63/linux/arch/i386/kernel/trampoline.S linux/arch/i386/kernel/trampoline.S --- v2.1.63/linux/arch/i386/kernel/trampoline.S Tue May 13 22:41:01 1997 +++ linux/arch/i386/kernel/trampoline.S Fri Nov 14 18:30:40 1997 @@ -62,7 +62,7 @@ gdt_48: .word 0x0800 # gdt limit = 2048, 256 GDT entries - .long gdt-0xc0000000 # gdt base = gdt (first SMP CPU) + .long gdt_table-0xc0000000 # gdt base = gdt (first SMP CPU) .globl SYMBOL_NAME(trampoline_end) SYMBOL_NAME_LABEL(trampoline_end) diff -u --recursive --new-file v2.1.63/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.1.63/linux/arch/i386/kernel/traps.c Wed Nov 12 13:34:25 1997 +++ linux/arch/i386/kernel/traps.c Fri Nov 14 18:50:36 1997 @@ -413,47 +413,26 @@ #endif /* CONFIG_MATH_EMULATION */ -static struct -{ - short limit __attribute__((packed)); - void * addr __attribute__((packed)); - short __pad __attribute__((packed)); -} idt_d; - -void * idt2; - __initfunc(void trap_init_f00f_bug(void)) { - pgd_t * pgd; - pmd_t * pmd; - pte_t * pte; - unsigned long twopage; - - printk("moving IDT ... "); - - twopage = (unsigned long) vmalloc (2*PAGE_SIZE); - - idt2 = (void *)(twopage + 4096-7*8); - - memcpy(idt2,&idt,sizeof(idt)); - - idt_d.limit = 256*8-1; - idt_d.addr = idt2; - idt_d.__pad = 0; - - __asm__ __volatile__("\tlidt %0": "=m" (idt_d)); + unsigned long page; /* - * Unmap lower page: + * Allocate a new page in virtual address space, + * and move the IDT to have entry #7 starting at + * the beginning of the page. We'll force a page + * fault for IDT entries #0-#6.. */ - pgd = pgd_offset(current->mm, twopage); - pmd = pmd_offset(pgd, twopage); - pte = pte_offset(pmd, twopage); + page = (unsigned long) vmalloc(PAGE_SIZE); + memcpy((void *) page, idt_table + 7, (256-7)*8); - pte_clear(pte); - flush_tlb_all(); - - printk(" ... done\n"); + /* + * "idt" is magic - it overlaps the idt_descr + * variable so that updating idt will automatically + * update the idt descriptor.. + */ + idt = (struct desc_struct *)(page - 7*8); + __asm__ __volatile__("lidt %0": "=m" (idt_descr)); } diff -u --recursive --new-file v2.1.63/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.63/linux/arch/i386/mm/fault.c Wed Nov 12 13:34:25 1997 +++ linux/arch/i386/mm/fault.c Thu Nov 13 22:13:55 1997 @@ -74,14 +74,6 @@ return 0; } -asmlinkage void divide_error(void); -asmlinkage void debug(void); -asmlinkage void nmi(void); -asmlinkage void int3(void); -asmlinkage void overflow(void); -asmlinkage void bounds(void); -asmlinkage void invalid_op(void); - asmlinkage void do_divide_error (struct pt_regs *, unsigned long); asmlinkage void do_debug (struct pt_regs *, unsigned long); asmlinkage void do_nmi (struct pt_regs *, unsigned long); @@ -90,7 +82,6 @@ asmlinkage void do_bounds (struct pt_regs *, unsigned long); asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); -extern int * idt2; extern int pentium_f00f_bug; /* @@ -105,18 +96,46 @@ */ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) { - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; + struct task_struct *tsk; + struct mm_struct *mm; struct vm_area_struct * vma; unsigned long address; unsigned long page; unsigned long fixup; int write; - lock_kernel(); - /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); + + /* + * Pentium F0 0F C7 C8 bug workaround. Do this first, + * to make sure we don't have locking problems with + * asynchronous traps (ie NMI). + */ + if ( !(error_code & 7) && pentium_f00f_bug ) { + unsigned long nr; + + nr = (address - (unsigned long) idt) >> 3; + + if (nr < 7) { + static void (*handler[])(struct pt_regs *, unsigned long) = { + do_divide_error, /* 0 - divide overflow */ + do_debug, /* 1 - debug trap */ + do_nmi, /* 2 - NMI */ + do_int3, /* 3 - int 3 */ + do_overflow, /* 4 - overflow */ + do_bounds, /* 5 - bound range */ + do_invalid_op }; /* 6 - invalid opcode */ + handler[nr](regs, 0); + return; + } + } + + lock_kernel(); + + tsk = current; + mm = tsk->mm; + down(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -186,46 +205,6 @@ tsk->tss.error_code = error_code; tsk->tss.trap_no = 14; force_sig(SIGSEGV, tsk); - goto out; - } - - printk("<%p/%p>\n", idt2, (void *)address); - /* - * Pentium F0 0F C7 C8 bug workaround: - */ - if ( pentium_f00f_bug && (address >= (unsigned long)idt2) && - (address < (unsigned long)idt2+256*8) ) { - - void (*handler) (void); - int nr = (address-(unsigned long)idt2)/8; - unsigned long low, high; - - low = idt[nr].a; - high = idt[nr].b; - - handler = (void (*) (void)) ((low&0x0000ffff) | (high&0xffff0000)); - printk("\n"); goto out; } diff -u --recursive --new-file v2.1.63/linux/drivers/char/atixlmouse.c linux/drivers/char/atixlmouse.c --- v2.1.63/linux/drivers/char/atixlmouse.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/char/atixlmouse.c Wed Nov 12 20:28:26 1997 @@ -136,16 +136,16 @@ } -static long write_mouse(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) +static ssize_t write_mouse(struct file * file, const char * buffer, + size_t count, loff_t *ppos) { return -EINVAL; } -static long read_mouse(struct inode * inode, struct file * file, - char * buffer, unsigned long count) +static ssize_t read_mouse(struct file * file, char * buffer, + size_t count, loff_t *ppos) { - int i; + ssize_t i; if (count < 3) return -EINVAL; diff -u --recursive --new-file v2.1.63/linux/drivers/char/msbusmouse.c linux/drivers/char/msbusmouse.c --- v2.1.63/linux/drivers/char/msbusmouse.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/char/msbusmouse.c Thu Nov 13 07:29:28 1997 @@ -129,15 +129,14 @@ return 0; } - -static long write_mouse(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) +static ssize_t write_mouse(struct file * file, + const char * buffer, size_t count, loff_t *ppos) { return -EINVAL; } -static long read_mouse(struct inode * inode, struct file * file, - char * buffer, unsigned long count) +static ssize_t read_mouse(struct file * file, + char * buffer, size_t count, loff_t *ppos) { int i, dx, dy; diff -u --recursive --new-file v2.1.63/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c --- v2.1.63/linux/drivers/misc/parport_pc.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/misc/parport_pc.c Fri Nov 14 11:25:32 1997 @@ -889,9 +889,9 @@ } #ifdef MODULE -static int io[PC_MAX_PORTS+1] = { 0, }; -static int dma[PC_MAX_PORTS] = { PARPORT_DMA_AUTO, }; -static int irq[PC_MAX_PORTS] = { PARPORT_IRQ_AUTO, }; +static int io[PC_MAX_PORTS+1] = { [0 ... PC_MAX_PORTS] = 0 }; +static int dma[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_DMA_AUTO }; +static int irq[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_IRQ_AUTO }; MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i"); MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i"); diff -u --recursive --new-file v2.1.63/linux/drivers/net/README.de4x5 linux/drivers/net/README.de4x5 --- v2.1.63/linux/drivers/net/README.de4x5 Sun Feb 2 05:02:05 1997 +++ linux/drivers/net/README.de4x5 Thu Nov 13 21:24:45 1997 @@ -1,7 +1,23 @@ -This driver has been upgraded to include generic DECchip support through the -use of the on-board SROM that is found on all DECchip cards except for the -DC21040. The driver will work with the following set of cards and probably -more: + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: + + DE425 TP/COAX EISA + DE434 TP PCI + DE435 TP/COAX/AUI PCI + DE450 TP/COAX/AUI PCI + DE500 10/100 PCI Fasternet + + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 + + So far the driver is known to work with the following cards: KINGSTON Linksys @@ -9,34 +25,119 @@ SMC8432 SMC9332 (w/new SROM) ZNYX31[45] - DIGITAL EtherWORKS PCI/EISA (DE425, DE434, DE435, DE450, DE500) - -Auto media detection is provided so that the media choice isn't compiled in -and is flexible enough to be able to reconfigure on-the-fly. + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) -The ability to load this driver as a loadable module has been included and -will now load (and unload) as many DECchip cards as it can find and -configure with just one invocation of 'insmod'. - -The performance we've achieved so far has been measured through the 'ttcp' -tool at 1.06MB/s for TCP and 1.17MB/s for UDP. This measures the total -stack performance which includes the card, so don't expect to get much -nearer the 1.25MB/s theoretical ethernet rate. + The driver has been tested on a relatively busy network using the DE425, + DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred + 16M of data to a DECstation 5000/200 as follows: TCP UDP TX RX TX RX - DE425 1030k 997k 1170k 1128k (EISA on a Dell 433DE) - DE434 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2) - DE435 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2) - DE500 1063k 998k 1170k 1125k in 10Mb/s mode (PCI: DECpc XL 466d2) - -All values are typical (in kBytes/sec) from a sample of 4 for each -measurement. Their error is approx +/-20k on a quiet (private) network and -also depend on what load the CPU has, CPU speed etc. - -I've had reports that Alphas can get 80+Mb/s when using 100BASE-TX and -similar figures for 133MHz Pentium Pros. - -Enjoy! + DE425 1030k 997k 1170k 1128k + DE434 1063k 995k 1170k 1125k + DE435 1063k 995k 1170k 1125k + DE500 1063k 998k 1170k 1125k in 10Mb/s mode + + All values are typical (in kBytes/sec) from a sample of 4 for each + measurement. Their error is +/-20k on a quiet (private) network and also + depend on what load the CPU has. + + ========================================================================= + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). Loadable module support under PCI and EISA has been + achieved by letting the driver autoprobe as if it were compiled into the + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! + + To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy de4x5.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) for fixed autoprobes (not recommended), edit the source code near + line 5537 to reflect the I/O address you're using, or assign these when + loading by: + + insmod de4x5 io=0xghh where g = bus number + hh = device number + + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. + 3) compile de4x5.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the de4x5 configuration turned off and reboot. + 5) insmod de4x5 [io=0xghh] + 6) run the net startup bits for your new eth?? interface(s) manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + To unload a module, turn off the associated interface(s) + 'ifconfig eth?? down' then 'rmmod de4x5'. + + Automedia detection is included so that in principal you can disconnect + from, e.g. TP, reconnect to BNC and things will still work (after a + pause whilst the driver figures out where its media went). My tests + using ping showed that it appears to work.... + + By default, the driver will now autodetect any DECchip based card. + Should you have a need to restrict the driver to DIGITAL only cards, you + can compile with a DEC_ONLY define, or if loading as a module, use the + 'dec_only=1' parameter. + + The SMC9332 card has a non-compliant SROM which needs fixing - I have + patched this driver to detect it because the SROM format used complies + to a previous DEC-STD format. + + I have removed the buffer copies needed for receive on Intels. I cannot + remove them for Alphas since the Tulip hardware only does longword + aligned DMA transfers and the Alphas get alignment traps with non + longword aligned data copies (which makes them really slow). No comment. + + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless de4x5_full_duplex is set at compile + time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This + is because there is no way to automatically detect full duplex links + except through autonegotiation. When I include the autonegotiation + feature in the SROM autoconf code, this detection will occur + automatically. -Dave diff -u --recursive --new-file v2.1.63/linux/drivers/net/README.multicast linux/drivers/net/README.multicast --- v2.1.63/linux/drivers/net/README.multicast Sun Feb 2 05:18:36 1997 +++ linux/drivers/net/README.multicast Thu Nov 13 21:24:45 1997 @@ -26,7 +26,7 @@ at1700 PROMISC PROMISC YES Software atp PROMISC PROMISC YES Software cs89x0 YES YES YES Software -de4x5 YES NO YES Hardware +de4x5 YES YES YES Hardware de600 NO NO NO N/A de620 PROMISC PROMISC YES Software depca YES PROMISC YES Hardware diff -u --recursive --new-file v2.1.63/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.1.63/linux/drivers/net/de4x5.c Tue May 13 22:41:09 1997 +++ linux/drivers/net/de4x5.c Thu Nov 13 21:24:45 1997 @@ -41,12 +41,13 @@ Digital Semiconductor SROM Specification. The driver currently recognises the following chips: - DC21040 (no SROM) - DC21041[A] - DC21140[A] + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 - I plan to add DC2114[23] support ASAP, time permitting. So far the - driver is known to work with the following cards: + So far the driver is known to work with the following cards: KINGSTON Linksys @@ -101,7 +102,7 @@ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. 2) for fixed autoprobes (not recommended), edit the source code near - line 5005 to reflect the I/O address you're using, or assign these when + line 5539 to reflect the I/O address you're using, or assign these when loading by: insmod de4x5 io=0xghh where g = bus number @@ -179,9 +180,27 @@ INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not run on the same interrupt. PCMCIA/CardBus is another can of worms... + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless de4x5_full_duplex is set at compile + time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This + is because there is no way to automatically detect full duplex links + except through autonegotiation. When I include the autonegotiation + feature in the SROM autoconf code, this detection will occur + automatically. + TO DO: ------ + o check what revision numbers the 21142 and 21143 have + o Revision History ---------------- @@ -300,11 +319,19 @@ Added byte counters from Added SA_INTERRUPT temporary fix from . + 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during + module load: bug reported by + + Fix multi-MAC, one SROM, to work with 2114x chips: + bug reported by . + Make above search independent of BIOS device scan + direction. + Completed DC2114[23] autosense functions. ========================================================================= */ -static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.53 1997/11/12 davies@maniac.ultranet.com\n"; #include @@ -319,7 +346,6 @@ #include #include #include -#include #include #include #include @@ -340,25 +366,28 @@ #define c_char const char #include -#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8)) -#define net_device_stats enet_statistics -#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) -#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) -#define le16_to_cpu(a) cpu_to_le16(a) -#define le32_to_cpu(a) cpu_to_le32(a) -#ifdef __powerpc__ -#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) -#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ - (((a) & 0x0000ff00U) << 8) |\ - (((a) & 0x00ff0000U) >> 8) |\ - (((a) & 0xff000000U) >> 24)) -#else -#define cpu_to_le16(a) (a) -#define cpu_to_le32(a) (a) -#endif /* __powerpc__ */ -#include +#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) +# define __initfunc(__arginit) __arginit +# define test_and_set_bit set_bit +# define net_device_stats enet_statistics +# define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +# define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +# define le16_to_cpu(a) cpu_to_le16(a) +# define le32_to_cpu(a) cpu_to_le32(a) +# ifdef __powerpc__ +# define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) +# define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ + (((a) & 0x0000ff00U) << 8) |\ + (((a) & 0x00ff0000U) >> 8) |\ + (((a) & 0xff000000U) >> 24)) +# else +# define cpu_to_le16(a) (a) +# define cpu_to_le32(a) (a) +# endif /* __powerpc__ */ +# include #else -#include +# include +# include #endif /* LINUX_VERSION_CODE */ #define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) @@ -392,6 +421,7 @@ u_int ana; /* NWay Advertisement */ u_int fdx; /* Full DupleX capabilites for each media */ u_int ttm; /* Transmit Threshold Mode for each media */ + u_int mci; /* 21142 MII Connector Interrupt info */ }; #define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ @@ -444,7 +474,8 @@ #ifdef DE4X5_DEBUG static int de4x5_debug = DE4X5_DEBUG; #else -static int de4x5_debug = (0); +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); #endif #ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */ @@ -503,6 +534,7 @@ #define PCI_MAX_BUS_NUM 8 #define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ #define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ +#define NO_MORE_PCI -2 /* PCI bus search all done */ /* ** Memory Alignment. Each descriptor is 4 longwords long. To force a @@ -785,11 +817,13 @@ static int dc21040_autoconf(struct device *dev); static int dc21041_autoconf(struct device *dev); static int dc21140m_autoconf(struct device *dev); +static int dc2114x_autoconf(struct device *dev); static int srom_autoconf(struct device *dev); static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *)); static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int)); static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); -static int test_sym_link(struct device *dev, int msec); +static int test_for_100Mb(struct device *dev, int msec); +static int wait_for_link(struct device *dev); static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec); static int is_spd_100(struct device *dev); static int is_100_up(struct device *dev); @@ -823,7 +857,7 @@ /*static void srom_busy(u_int command, u_long address);*/ static void sendto_srom(u_int command, u_long addr); static int getfrom_srom(u_long addr); -static void srom_map_media(struct device *dev); +static int srom_map_media(struct device *dev); static int srom_infoleaf_info(struct device *dev); static void srom_init(struct device *dev); static void srom_exec(struct device *dev, u_char *p); @@ -845,16 +879,15 @@ static void eisa_probe(struct device *dev, u_long iobase); static void pci_probe(struct device *dev, u_long iobase); -static struct device *alloc_device(struct device *dev, u_long iobase); -static struct device *insert_device(struct device *dev, u_long iobase, - int (*init)(struct device *)); +static void srom_search(int index); static char *build_setup_frame(struct device *dev, int mode); static void disable_ast(struct device *dev); static void enable_ast(struct device *dev, u32 time_out); static long de4x5_switch_mac_port(struct device *dev); +static int gep_rd(struct device *dev); +static void gep_wr(s32 data, struct device *dev); static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); static void yawn(struct device *dev, int state); -static int de4x5_dev_index(char *s); static void link_modules(struct device *dev, struct device *tmp); static void de4x5_dbg_open(struct device *dev); static void de4x5_dbg_mii(struct device *dev, int k); @@ -878,15 +911,25 @@ int init_module(void); void cleanup_module(void); static struct device *unlink_modules(struct device *p); -static int autoprobed = 0, loading_module = 1; +static struct device *insert_device(struct device *dev, u_long iobase, + int (*init)(struct device *)); +static int count_adapters(void); +static int loading_module = 1; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(de4x5_debug, "i"); +MODULE_PARM(de4x5_full_duplex, "i"); +MODULE_PARM(dec_only, "i"); +#endif /* LINUX_VERSION_CODE */ # else -static int autoprobed = 0, loading_module = 0; +static int loading_module = 0; #endif /* MODULE */ static char name[DE4X5_NAME_LENGTH + 1]; static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; -static int num_de4x5s = 0, num_eth = 0; +static int num_de4x5s = 0; static int cfrv = 0, useSROM = 0; +static int lastEISA = 0, lastPCI = -1; +static struct device *lastModule = NULL; /* ** List the SROM infoleaf functions and chipsets @@ -943,28 +986,17 @@ /* ** Autoprobing in modules is allowed here. See the top of the file for -** more info. Until I fix (un)register_netdevice() we won't be able to use it -** though. +** more info. */ __initfunc(int de4x5_probe(struct device *dev)) { - int status = -ENODEV; u_long iobase = dev->base_addr; eisa_probe(dev, iobase); pci_probe(dev, iobase); - /* - ** Walk the device list to check that at least one device - ** initialised OK - */ - for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); - - if (dev->priv) status = 0; - if (iobase == 0) autoprobed = 1; - - return status; + return (dev->priv ? 0 : -ENODEV); } __initfunc(static int @@ -1140,7 +1172,6 @@ /* Initialise the SROM pointers if possible */ if (lp->useSROM) { lp->state = INITIALISED; - de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); if (srom_infoleaf_info(dev)) { return -ENXIO; } @@ -1191,7 +1222,7 @@ u_long iobase = dev->base_addr; int i, status = 0; s32 omr; - + /* Allocate the RX buffers */ for (i=0; irxRingSize; i++) { if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { @@ -1230,6 +1261,7 @@ printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); } } + dev->tbusy = 0; dev->start = 1; dev->interrupt = UNMASK_INTERRUPTS; @@ -1267,7 +1299,7 @@ de4x5_init(struct device *dev) { /* Lock out other processes whilst setting up the hardware */ - set_bit(0, (void *)&dev->tbusy); + test_and_set_bit(0, (void *)&dev->tbusy); de4x5_sw_reset(dev); @@ -1301,8 +1333,9 @@ ** without these values. Cache align 16 long. */ bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN; + bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); outl(bmr, DE4X5_BMR); - + omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ if (lp->chipset == DC21140) { omr |= (OMR_SDP | OMR_SB); @@ -1323,13 +1356,13 @@ } barrier(); - + /* Build the setup frame depending on filtering mode */ SetMulticastFilter(dev); load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL); outl(omr|OMR_ST, DE4X5_OMR); - + /* Poll for setup frame completion (adapter interrupts are disabled now) */ sti(); /* Ensure timer interrupts */ for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ @@ -1337,7 +1370,7 @@ if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; } outl(omr, DE4X5_OMR); /* Stop everything! */ - + if (j == 0) { printk("%s: Setup frame timed out, status %08x\n", dev->name, inl(DE4X5_STS)); @@ -1346,7 +1379,7 @@ lp->tx_new = (++lp->tx_new) % lp->txRingSize; lp->tx_old = lp->tx_new; - + return status; } @@ -1365,7 +1398,7 @@ return 0; } - set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */ + test_and_set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */ if (lp->tx_enable == NO) { /* Cannot send for now */ return -1; } @@ -1380,7 +1413,8 @@ sti(); /* Test if cache is already locked - requeue skb if so */ - if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1; + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) + return -1; /* Transmit descriptor ring full or stale skb */ if (dev->tbusy || lp->tx_skb[lp->tx_new]) { @@ -1401,7 +1435,7 @@ while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) { cli(); - set_bit(0, (void*)&dev->tbusy); + test_and_set_bit(0, (void*)&dev->tbusy); load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); #if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) lp->stats.tx_bytes += skb->len; @@ -1897,15 +1931,15 @@ eisa_probe(struct device *dev, u_long ioaddr)) { int i, maxSlots, status, device; + u_char irq; u_short vendor; u32 cfid; u_long iobase; struct bus_type *lp = &bus; char name[DE4X5_STRLEN]; - struct device *tmp; - - if (autoprobed) return; /* Been here before ! */ + if (lastEISA == MAX_EISA_SLOTS) return; /* No more EISA devices to search */ + lp->bus = EISA; if (ioaddr == 0) { /* Autoprobing */ @@ -1918,7 +1952,7 @@ maxSlots = i + 1; } - for (status= -ENODEV;(iirq = inb(EISA_REG0); - dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03]; + irq = inb(EISA_REG0); + irq = de4x5_irq[(irq >> 1) & 0x03]; - if (is_DC2114x) device |= (cfrv & 0x00f0); + if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - DevicePresent(DE4X5_APROM); + /* Write the PCI Configuration Registers */ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); outl(0x00006000, PCI_CFLT); outl(iobase, PCI_CBIO); + DevicePresent(DE4X5_APROM); if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) { - if ((tmp = alloc_device(dev, iobase)) != NULL) { - if ((status = de4x5_hw_init(tmp, iobase)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(dev, tmp); - } else if (loading_module && (tmp != dev)) { - kfree(tmp); - } + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase)) == 0) { + num_de4x5s++; + if (loading_module) link_modules(lastModule, dev); + lastEISA = i; + return; } - } else if (autoprobed) { + } else if (ioaddr != 0) { printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase); } } } - + + if (ioaddr == 0) lastEISA = i; + return; } @@ -1973,20 +2009,23 @@ __initfunc(static void pci_probe(struct device *dev, u_long ioaddr)) { - u_char irq; + u_char irq, timer; u_char pb, pbus, dev_num, dnum, dev_fn; u_short dev_id, vendor, index, status; u_int class = DE4X5_CLASS_CODE; u_int device, iobase; struct bus_type *lp = &bus; - struct device *tmp; - if (autoprobed) return; - - if (!pcibios_present()) return; /* No PCI bus in this machine! */ + if (lastPCI == NO_MORE_PCI) return; + + if (!pcibios_present()) { + lastPCI = NO_MORE_PCI; + return; /* No PCI bus in this machine! */ + } lp->bus = PCI; - + lp->bus_num = 0; + if ((ioaddr < 0x1000) && loading_module) { pbus = (u_short)(ioaddr >> 8); dnum = (u_short)(ioaddr & 0xff); @@ -1995,7 +2034,7 @@ dnum = 0; } - for (index=0; + for (index=lastPCI+1; (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); @@ -2010,6 +2049,12 @@ continue; } + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { + lp->bus_num = pb; + srom_search(index); + } + /* Get the chip configuration revision register */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); @@ -2018,14 +2063,9 @@ lp->bus_num = pb; /* Set the chipset information */ - if (is_DC2114x) device |= (cfrv & 0x00f0); + if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - if (is_DC21142 || is_DC21143) { - printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143")); - continue; - } - /* Get the board I/O address */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); iobase &= CBIO_MASK; @@ -2044,127 +2084,106 @@ } if (!(status & PCI_COMMAND_MASTER)) continue; + /* Check the latency timer for values > 0x60 */ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60); + } + DevicePresent(DE4X5_APROM); if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { - if ((tmp = alloc_device(dev, iobase)) != NULL) { - tmp->irq = irq; - if ((status = de4x5_hw_init(tmp, iobase)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(dev, tmp); - } else if (loading_module && (tmp != dev)) { - kfree(tmp); + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase)) == 0) { + num_de4x5s++; + if (loading_module) { + link_modules(lastModule, dev); + lastPCI = index; } + return; } - } else if (autoprobed) { + } else if (ioaddr != 0) { printk("%s: region already allocated at 0x%04x.\n", dev->name, (u_short)iobase); } } } - + + if (loading_module) lastPCI = NO_MORE_PCI; + return; } /* -** Search the entire 'eth' device list for a fixed probe. If a match isn't -** found then check for an autoprobe or unused device location. If they -** are not available then insert a new device structure at the end of -** the current list. +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... */ -__initfunc(static struct device * -alloc_device(struct device *dev, u_long iobase)) +__initfunc(static void +srom_search(int index)) { - struct device *adev = NULL; - int fixed = 0, new_dev = 0; - - if (!dev) return dev; - num_eth = de4x5_dev_index(dev->name); + u_char irq, pb, dev_fn; + u_short dev_id, dev_num, vendor, status; + u_int class = DE4X5_CLASS_CODE; + u_int device, iobase; + int i, j; + struct bus_type *lp = &bus; - if (loading_module) { - if (dev->priv) { - dev = insert_device(dev, iobase, de4x5_probe); - } - num_eth++; - return dev; - } + for (; + (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); + index++) { - while (1) { - if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) { - adev=dev; - } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { - fixed = 1; - } else { - if (dev->next == NULL) { - new_dev = 1; - } else if (strncmp(dev->next->name, "eth", 3) != 0) { - new_dev = 1; - } + if (lp->bus_num != pb) return; + dev_num = PCI_SLOT(dev_fn); + device = 0; + pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); + device = dev_id; + device <<= 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { + continue; } - if ((dev->next == NULL) || new_dev || fixed) break; - dev = dev->next; - num_eth++; - } - if (adev && !fixed) { - dev = adev; - num_eth = de4x5_dev_index(dev->name); - new_dev = 0; - } - - if (((dev->next == NULL) && - ((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) || - new_dev) { - num_eth++; /* New device */ - dev = insert_device(dev, iobase, de4x5_probe); - } - - return dev; -} - -/* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ -__initfunc(static struct device * -insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))) -{ - struct device *new; - new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); - if (new == NULL) { - printk("eth%d: Device not initialised, insufficient memory\n",num_eth); - return NULL; - } else { - memset((char *)new, 0, sizeof(struct device)+8); - new->name = (char *)(new + 1); - new->base_addr = iobase; /* assign the io address */ - new->init = init; /* initialisation routine */ - if (!loading_module) { - new->next = dev->next; - dev->next = new; - if (num_eth > 9999) { - sprintf(new->name,"eth????");/* New device name */ - } else { - sprintf(new->name,"eth%d", num_eth);/* New device name */ + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) device |= (cfrv & CFRV_RN); + lp->chipset = device; + + /* Get the board I/O address */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); + iobase &= CBIO_MASK; + + /* Fetch the IRQ to be used */ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq); + if ((irq == 0) || (irq == (u_char) 0xff)) continue; + + /* Check if I/O accesses are enabled */ + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) continue; + + /* Search for a valid SROM attached to this DECchip */ + DevicePresent(DE4X5_APROM); + for (j=0, i=0; isrom + SROM_HWADD + i); + } + if ((j != 0) && (j != 0x5fa)) { + last.chipset = device; + last.bus = pb; + last.irq = irq; + for (i=0; isrom + SROM_HWADD + i); } + return; } } - return new; -} - -__initfunc(static int -de4x5_dev_index(char *s)) -{ - int i=0, j=0; - - for (;*s; s++) { - if (isdigit(*s)) { - j=1; - i = (i * 10) + (*s - '0'); - } else if (j) break; - } - - return i; + return; } __initfunc(static void @@ -2199,13 +2218,15 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS;; - + int next_tick = DE4X5_AUTOSENSE_MS; + lp->linkOK = 0; lp->c_media = AUTO; /* Bogus last media */ disable_ast(dev); inl(DE4X5_MFC); /* Zero the lost frames counter */ lp->media = INIT; + lp->tcount = 0; + if (lp->useSROM) { next_tick = srom_autoconf(dev); } else if (lp->chipset == DC21040) { @@ -2609,7 +2630,10 @@ next_tick &= ~TIMER_CB; } else { if (lp->useSROM) { - srom_map_media(dev); + if (srom_map_media(dev) < 0) { + lp->tcount++; + return next_tick; + } srom_exec(dev, lp->phy[lp->active].gep); if (lp->infoblock_media == ANS) { ana = lp->phy[lp->active].ana | MII_ANA_CSMA; @@ -2691,11 +2715,11 @@ case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ if (lp->timeout < 0) { - lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : - (~inl(DE4X5_GEP) & GEP_LNP)); + lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : + (~gep_rd(dev) & GEP_LNP)); SET_100Mb_PDET; } - if ((slnk = test_sym_link(dev, 6200)) < 0) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { next_tick = slnk & ~TIMER_CB; } else { if (is_spd_100(dev) && is_100_up(dev)) { @@ -2716,7 +2740,7 @@ de4x5_init_connection(dev); } else { if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!(is_spd_100(dev) && is_100_up(dev))) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { lp->media = INIT; lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; @@ -2732,7 +2756,7 @@ de4x5_init_connection(dev); } else { if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!(!is_spd_100(dev) && is_10_up(dev))) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { lp->media = INIT; lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; @@ -2754,131 +2778,378 @@ return next_tick; } -static int -srom_autoconf(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - return lp->infoleaf_fn(dev); -} - /* -** This mapping keeps the original media codes and FDX flag unchanged. -** While it isn't strictly necessary, it helps me for the moment... +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. */ -static void -srom_map_media(struct device *dev) +static int +dc2114x_autoconf(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int iobase = dev->base_addr; + s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; + int next_tick = DE4X5_AUTOSENSE_MS; - lp->fdx = 0; - switch(lp->infoblock_media) { - case SROM_10BASETF: - lp->fdx = TRUE; - case SROM_10BASET: - if (lp->chipset == DC21140) { - lp->media = _10Mb; - } else { - lp->media = TP; + switch (lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } + lp->local_state = 0; + next_tick = dc2114x_autoconf(dev); + } break; - - case SROM_10BASE2: - lp->media = BNC; - break; - - case SROM_10BASE5: - lp->media = AUI; - break; - - case SROM_100BASETF: - lp->fdx = TRUE; - case SROM_100BASET: - lp->media = _100Mb; - break; - - case SROM_100BASET4: - lp->media = _100Mb; - break; - - case SROM_100BASEFF: - lp->fdx = TRUE; - case SROM_100BASEF: - lp->media = _100Mb; - break; - + case ANS: - lp->media = ANS; - break; - - default: - printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, - lp->infoblock_media); - break; - } - - return; -} - -static void -de4x5_init_connection(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; /* Stop scrolling media messages */ - } - - cli(); - de4x5_restore_skbs(dev); - de4x5_setup_intr(dev); - lp->tx_enable = YES; - dev->tbusy = 0; - sti(); - outl(POLL_DEMAND, DE4X5_TPD); - mark_bh(NET_BH); - - return; -} - -/* -** General PHY reset function. Some MII devices don't reset correctly -** since their MII address pins can float at voltages that are dependent -** on the signal pin use. Do a double reset to ensure a reset. -*/ -static int -de4x5_reset_phy(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = 0; - - if ((lp->useSROM) || (lp->phy[lp->active].id)) { - if (lp->timeout < 0) { - if (lp->useSROM) { - if (lp->phy[lp->active].rst) { /* MII device specific reset */ - srom_exec(dev, lp->phy[lp->active].rst); - srom_exec(dev, lp->phy[lp->active].rst); - } else if (lp->rst) { /* Type 5 infoblock reset */ - srom_exec(dev, lp->rst); - srom_exec(dev, lp->rst); - } + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; } else { - PHY_HARD_RESET; + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc2114x_autoconf(dev); } - if (lp->useMII) { - mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - } - if (lp->useMII) { - next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); - } - } else if (lp->chipset == DC21140) { - PHY_HARD_RESET; - } - + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc2114x_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc2114x_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->tcount++; + lp->media = INIT; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (srom_map_media(dev) < 0) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + if (lp->media == _100Mb) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + lp->media = SPD_DET; + return (slnk & ~TIMER_CB); + } + } else { + if (wait_for_link(dev) < 0) { + lp->media = SPD_DET; + return PDET_LINK_WAIT; + } + } + if (((lp->media == _100Mb) && is_100_up(dev)) || + ((lp->media == _10Mb) && is_10_up(dev)) || + (lp->media == BNC) || (lp->media == AUI)) { + next_tick = dc2114x_autoconf(dev); + } else { + lp->tcount++; + lp->media = INIT; + } + break; + + case _10Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case _100Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + default: + lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); + lp->media = INIT; + break; + } + + return next_tick; +} + +static int +srom_autoconf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + if (lp->infoblock_media == lp->media) + return 0; + + switch(lp->infoblock_media) { + case SROM_10BASETF: + if (!de4x5_full_duplex) return -1; + lp->fdx = TRUE; + case SROM_10BASET: + if (de4x5_full_duplex && !lp->fdx) return -1; + if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + if (!de4x5_full_duplex) return -1; + lp->fdx = TRUE; + case SROM_100BASET: + if (de4x5_full_duplex && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + if (!de4x5_full_duplex) return -1; + lp->fdx = TRUE; + case SROM_100BASEF: + if (de4x5_full_duplex && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + return -1; + break; + } + + return 0; +} + +static void +de4x5_init_connection(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; /* Stop scrolling media messages */ + } + + cli(); + de4x5_restore_skbs(dev); + de4x5_setup_intr(dev); + lp->tx_enable = YES; + dev->tbusy = 0; + sti(); + outl(POLL_DEMAND, DE4X5_TPD); + mark_bh(NET_BH); + + return; +} + +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ +static int +de4x5_reset_phy(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = 0; + + if ((lp->useSROM) || (lp->phy[lp->active].id)) { + if (lp->timeout < 0) { + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); + } + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; + } + return next_tick; } @@ -2891,7 +3162,9 @@ if (lp->timeout < 0) { lp->timeout = msec/100; - reset_init_sia(dev, csr13, csr14, csr15); + if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ + reset_init_sia(dev, csr13, csr14, csr15); + } /* set up the interrupt mask */ outl(irq_mask, DE4X5_IMR); @@ -2901,7 +3174,7 @@ outl(sts, DE4X5_STS); /* clear csr12 NRA and SRA bits */ - if (lp->chipset == DC21041) { + if ((lp->chipset == DC21041) || lp->useSROM) { csr12 = inl(DE4X5_SISR); outl(csr12, DE4X5_SISR); } @@ -2940,24 +3213,37 @@ return sisr; } +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500 /* ms */ +#define SAMPLE_DELAY 2000 /* ms */ static int -test_sym_link(struct device *dev, int msec) +test_for_100Mb(struct device *dev, int msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; - int gep = 0; - + int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + if (lp->timeout < 0) { - lp->timeout = msec/100; + if ((msec/SAMPLE_INTERVAL) <= 0) return 0; + if (msec > SAMPLE_DELAY) { + lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; + gep = SAMPLE_DELAY | TIMER_CB; + return gep; + } else { + lp->timeout = msec/SAMPLE_INTERVAL; + } } if (lp->phy[lp->active].id || lp->useSROM) { - gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0); + gep = is_100_up(dev) | is_spd_100(dev); } else { - gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP)); + gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); } - if (!(gep & GEP_SLNK) && --lp->timeout) { - gep = 100 | TIMER_CB; + if (!(gep & ret) && --lp->timeout) { + gep = SAMPLE_INTERVAL | TIMER_CB; } else { lp->timeout = -1; } @@ -2965,6 +3251,24 @@ return gep; } +static int +wait_for_link(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->timeout < 0) { + lp->timeout = 1; + } + + if (lp->timeout--) { + return TIMER_CB; + } else { + lp->timeout = -1; + } + + return 0; +} + /* ** ** @@ -2999,16 +3303,18 @@ u_long iobase = dev->base_addr; int spd; - if (lp->useSROM && !lp->useMII) { - spd = (lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); spd = ~(spd ^ lp->phy[lp->active].spd.value); spd &= lp->phy[lp->active].spd.mask; + } else if (!lp->useSROM) { /* de500-xa */ + spd = ((~gep_rd(dev)) & GEP_SLNK); } else { - spd = ((~inl(DE4X5_GEP)) & GEP_SLNK); + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); } return spd; @@ -3020,16 +3326,18 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->useSROM && !lp->useMII) { - return ((lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_SLNK); } else { - return ((~inl(DE4X5_GEP)) & GEP_SLNK); + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); } } @@ -3039,16 +3347,20 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->useSROM && !lp->useMII) { - return ((lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_LNP); } else { - return ((~inl(DE4X5_GEP)) & GEP_LNP); + if ((lp->ibn == 2) || !lp->asBitValid) + return (((lp->chipset & ~0x00ff) == DC2114x) ? + (~inl(DE4X5_SISR)&SISR_LS10): + 0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); } } @@ -3060,6 +3372,8 @@ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SISR) & SISR_LPN) >> 11; } else { return 0; } @@ -3271,11 +3585,6 @@ lp->cache.csr0 = inl(DE4X5_BMR); lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); lp->cache.csr7 = inl(DE4X5_IMR); - if (lp->chipset != DC21140) { - lp->cache.csr13 = inl(DE4X5_SICR); - lp->cache.csr14 = inl(DE4X5_STRR); - lp->cache.csr15 = inl(DE4X5_SIGR); - } break; case DE4X5_RESTORE_STATE: @@ -3283,8 +3592,8 @@ outl(lp->cache.csr6, DE4X5_OMR); outl(lp->cache.csr7, DE4X5_IMR); if (lp->chipset == DC21140) { - outl(lp->cache.gepc, DE4X5_GEP); - outl(lp->cache.gep, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); } else { reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); @@ -3392,15 +3701,32 @@ ** */ static void -reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr) +reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - + RESET_SIA; - outl(sigr, DE4X5_SIGR); - outl(strr, DE4X5_STRR); - outl(sicr, DE4X5_SICR); + if (lp->useSROM) { + if (lp->ibn == 3) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].gep); + outl(1, DE4X5_SICR); + return; + } else { + csr15 = lp->cache.csr15; + csr14 = lp->cache.csr14; + csr13 = lp->cache.csr13; + outl(csr15 | lp->cache.gepc, DE4X5_SIGR); + outl(csr15 | lp->cache.gep, DE4X5_SIGR); + } + } else { + outl(csr15, DE4X5_SIGR); + } + outl(csr14, DE4X5_STRR); + outl(csr13, DE4X5_SICR); + + de4x5_ms_delay(10); return; } @@ -3523,7 +3849,10 @@ if (lp->chipset != DC21041) { useSROM = TRUE; /* card is not recognisably DEC */ } + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + useSROM = TRUE; } + return status; } @@ -3531,17 +3860,31 @@ /* ** Set up the Ethernet PROM counter to the start of the Ethernet address on ** the DC21040, else read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). */ static void DevicePresent(u_long aprom_addr) { - int i; + int i, j=0; struct bus_type *lp = &bus; if (lp->chipset == DC21040) { outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ } else { /* Read new srom */ - u_short tmp, *p = (short *)&lp->srom; + u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); + for (i=0; i<(ETH_ALEN>>1); i++) { + tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); + *p = le16_to_cpu(tmp); + j += *p++; + } + if ((j == 0) || (j == 0x2fffd)) { + return; + } + + p=(short *)&lp->srom; for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { tmp = srom_rd(aprom_addr, i); *p++ = le16_to_cpu(tmp); @@ -3567,6 +3910,7 @@ struct bus_type *lp = &bus; broken = de4x5_bad_srom(lp); + for (i=0,k=0,j=0;j<3;j++) { k <<= 1; if (k > 0xffff) k-=0xffff; @@ -3673,6 +4017,10 @@ return; } +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ static int test_bad_enet(struct device *dev, int status) { @@ -3689,10 +4037,8 @@ if (dev->dev_addr[i] != 0) break; } for (i=0; idev_addr[i]; - if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) && - (lp->chipset == DC21040)) { - dev->irq = last.irq; - } + dev->irq = last.irq; + status = 0; } } else if (!status) { @@ -3753,9 +4099,6 @@ de4x5_us_delay(1); i = (getfrom_srom(addr) >> 3) & 0x01; - if (i != 0) { - printk("Bad SROM address phase.....\n"); - } return; } @@ -3868,14 +4211,13 @@ srom_init(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; u_char count; p+=2; if (lp->chipset == DC21140) { lp->cache.gepc = (*p++ | GEP_CTRL); - outl(lp->cache.gepc, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); } /* Block count */ @@ -3888,9 +4230,13 @@ } else if (*(p+1) == 5) { type5_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 4) { + p += ((*p & BLOCK_LEN) + 1); } else if (*(p+1) == 3) { type3_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 2) { + p += ((*p & BLOCK_LEN) + 1); } else if (*(p+1) == 1) { type1_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); @@ -3902,20 +4248,33 @@ return; } +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ static void srom_exec(struct device *dev, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; u_char count = (p ? *p++ : 0); + u_short *w = (u_short *)p; + if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + + if (lp->chipset != DC21140) RESET_SIA; + while (count--) { - if (lp->chipset == DC21140) { - outl(*p++, DE4X5_GEP); - } + gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? + *p++ : TWIDDLE(w++)), dev); udelay(2000); /* 2ms per action */ } + if (lp->chipset != DC21140) { + outl(lp->cache.csr14, DE4X5_STRR); + outl(lp->cache.csr13, DE4X5_SICR); + } + return; } @@ -3971,15 +4330,70 @@ static int dc21142_infoleaf(struct device *dev) { -printk("dc21142_infoleaf()\n"); - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; } static int dc21143_infoleaf(struct device *dev) { -printk("dc21143_infoleaf()\n"); - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; } /* @@ -3990,7 +4404,6 @@ compact_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char flags, csr6; /* Recursively figure out the info blocks */ @@ -4003,7 +4416,9 @@ } if ((lp->media == INIT) && (lp->timeout < 0)) { - outl(lp->cache.gepc, DE4X5_GEP); + lp->ibn = COMPACT; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); lp->infoblock_media = (*p++) & COMPACT_MC; lp->cache.gep = *p++; csr6 = *p++; @@ -4013,7 +4428,7 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); lp->useMII = FALSE; de4x5_switch_mac_port(dev); @@ -4024,12 +4439,11 @@ /* ** This block describes non MII media for the DC21140[A] only. - */ +*/ static int type0_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char flags, csr6, len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ @@ -4042,7 +4456,9 @@ } if ((lp->media == INIT) && (lp->timeout < 0)) { - outl(lp->cache.gepc, DE4X5_GEP); + lp->ibn = 0; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); p+=2; lp->infoblock_media = (*p++) & BLOCK0_MC; lp->cache.gep = *p++; @@ -4053,7 +4469,7 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); lp->useMII = FALSE; de4x5_switch_mac_port(dev); @@ -4081,7 +4497,6 @@ p += 2; if (lp->state == INITIALISED) { - lp->ibn = 1; lp->active = *p++; lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); @@ -4093,9 +4508,10 @@ } else if ((lp->media == INIT) && (lp->timeout < 0)) { lp->ibn = 1; lp->active = *p; - lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->infoblock_csr6 = OMR_MII_100; lp->useMII = TRUE; lp->infoblock_media = ANS; + lp->media = ANS; de4x5_switch_mac_port(dev); } @@ -4105,14 +4521,49 @@ static int type2_infoblock(struct device *dev, u_char count, u_char *p) { - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 2; + lp->active = 0; + p += 2; + lp->infoblock_media = (*p) & MEDIA_CODE; + + if ((*p++) & EXT_FIELD) { + lp->cache.csr13 = TWIDDLE(p); p += 2; + lp->cache.csr14 = TWIDDLE(p); p += 2; + lp->cache.csr15 = TWIDDLE(p); p += 2; + } else { + lp->cache.csr13 = CSR13; + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + } + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); + lp->infoblock_csr6 = OMR_SIA; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); } static int type3_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + u_char len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ if (--count > lp->tcount) { @@ -4123,20 +4574,55 @@ } } + p += 2; if (lp->state == INITIALISED) { - lp->ibn = 3; p += 2; lp->active = *p++; - lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); lp->phy[lp->active].mc = TWIDDLE(p); p += 2; lp->phy[lp->active].ana = TWIDDLE(p); p += 2; lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); + lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; + lp->phy[lp->active].mci = *p; return 0; - } else if (lp->media == INIT) { + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 3; + lp->active = *p; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type4_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 4; + lp->active = 0; p+=2; - lp->infoblock_media = (*p++) & BLOCK0_MC; - lp->cache.gep = *p++; + lp->infoblock_media = (*p++) & MEDIA_CODE; + lp->cache.csr13 = CSR13; /* Hard coded defaults */ + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; csr6 = *p++; flags = *p++; @@ -4144,19 +4630,13 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; - lp->useMII = TRUE; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; de4x5_switch_mac_port(dev); } - return dc21140m_autoconf(dev); -} - -static int -type4_infoblock(struct device *dev, u_char count, u_char *p) -{ - return DE4X5_AUTOSENSE_MS; + return dc2114x_autoconf(dev); } /* @@ -4167,8 +4647,7 @@ type5_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char i, j, len = (*p & BLOCK_LEN)+1; + u_char len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ if (--count > lp->tcount) { @@ -4183,19 +4662,7 @@ if ((lp->state == INITIALISED) || (lp->media == INIT)) { p+=2; lp->rst = p; - i = *p++; - for (j=0;i;--i) { - if (lp->chipset == DC21140) { - if (!j) { - outl(*p++ | GEP_CTRL, DE4X5_GEP); - j++; - } - outl(*p++, DE4X5_GEP); - } else if (lp->chipset == DC21142) { - } else if (lp->chipset == DC21143) { - } - } - + srom_exec(dev, lp->rst); } return DE4X5_AUTOSENSE_MS; @@ -4327,8 +4794,7 @@ } /* -** Here's 3 ways to calculate the OUI from the ID registers. One's a brain -** dead approach, 2 aren't (clue: mine isn't!). +** Here's 3 ways to calculate the OUI from the ID registers. */ static int mii_get_oui(u_char phyaddr, u_long ioaddr) @@ -4394,7 +4860,7 @@ if (i==0) n++; /* Count cycles */ while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ id = mii_get_oui(i, DE4X5_MII); - if ((id == 0) || (id == -1)) continue; /* Valid ID? */ + if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); @@ -4405,11 +4871,18 @@ lp->mii_cnt++; lp->active++; } else { - i = DE4X5_MAX_MII; /* Stop the search */ - j = limit; + goto purgatory; /* Stop the search */ } + break; + } + if ((j == limit) && (i < DE4X5_MAX_MII)) { + printk("%s: Found MII device not currently supported. Please mail the following dump to\nthe author:\n", dev->name); + de4x5_debug |= DEBUG_MII; + de4x5_dbg_mii(dev, i); + printk("\n"); } } + purgatory: lp->active = 0; if (lp->phy[0].id) { /* Reset the PHY devices */ for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ @@ -4419,7 +4892,8 @@ de4x5_dbg_mii(dev, k); } } - + if (!lp->mii_cnt) lp->useMII = FALSE; + return lp->mii_cnt; } @@ -4480,6 +4954,8 @@ int iobase = dev->base_addr; s32 omr; + STOP_DE4X5; + /* Assert the OMR_PS bit in CSR6 */ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX)); @@ -4490,10 +4966,12 @@ /* Soft Reset */ RESET_DE4X5; - /* Restore the GEP */ + /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ if (lp->chipset == DC21140) { - outl(lp->cache.gepc, DE4X5_GEP); - outl(lp->cache.gep, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else if ((lp->chipset & ~0x0ff) == DC2114x) { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); } /* Restore CSR6 */ @@ -4506,6 +4984,36 @@ } static void +gep_wr(s32 data, struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + outl(data, DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); + } + + return; +} + +static int +gep_rd(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + return inl(DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SIGR) & 0x000fffff); + } + + return 0; +} + +static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -4660,25 +5168,18 @@ if (lp->media != lp->c_media) { if (de4x5_debug & DEBUG_MEDIA) { - if (lp->chipset != DC21140) { - printk("%s: media is %s\n", dev->name, - (lp->media == NC ? "unconnected!" : - (lp->media == TP ? "TP." : - (lp->media == ANS ? "TP/Nway." : - (lp->media == BNC ? "BNC." : - (lp->media == AUI ? "AUI." : - (lp->media == BNC_AUI ? "BNC/AUI." : - (lp->media == EXT_SIA ? "EXT SIA." : - "???." - )))))))); - } else { - printk("%s: mode is %s\n", dev->name, - (lp->media == NC ? "link down or incompatible connection.": - (lp->media == _100Mb ? "100Mb/s." : - (lp->media == _10Mb ? "10Mb/s." : - "\?\?\?" - )))); - } + printk("%s: media is %s%s\n", dev->name, + (lp->media == NC ? "unconnected, link down or incompatible connection" : + (lp->media == TP ? "TP" : + (lp->media == ANS ? "TP/Nway" : + (lp->media == BNC ? "BNC" : + (lp->media == AUI ? "AUI" : + (lp->media == BNC_AUI ? "BNC/AUI" : + (lp->media == EXT_SIA ? "EXT SIA" : + (lp->media == _100Mb ? "100Mb/s" : + (lp->media == _10Mb ? "10Mb/s" : + "???" + ))))))))), (lp->fdx?" full duplex.":".")); } lp->c_media = lp->media; } @@ -4750,7 +5251,8 @@ /* ** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. */ static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) @@ -4792,7 +5294,7 @@ } build_setup_frame(dev, PHYS_ADDR_ONLY); /* Set up the descriptor and give ownership to the card */ - while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/ + while (test_and_set_bit(0, (void *)&dev->tbusy) != 0); load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | SETUP_FRAME_LEN, NULL); lp->tx_new = (++lp->tx_new) % lp->txRingSize; @@ -4805,6 +5307,7 @@ omr = inl(DE4X5_OMR); omr |= OMR_PR; outl(omr, DE4X5_OMR); + dev->flags |= IFF_PROMISC; } else { status = -EPERM; } @@ -4815,6 +5318,7 @@ omr = inl(DE4X5_OMR); omr &= ~OMR_PR; outb(omr, DE4X5_OMR); + dev->flags &= ~IFF_PROMISC; } else { status = -EPERM; } @@ -4824,13 +5328,7 @@ printk("%s: Boo!\n", dev->name); break; - case DE4X5_GET_MCA: /* Get the multicast address table */ - break; - case DE4X5_SET_MCA: /* Set a multicast address */ - break; - case DE4X5_CLR_MCA: /* Clear all multicast addresses */ - break; - case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/ + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PM; @@ -4895,9 +5393,8 @@ } break; -/* -#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * / - +#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* case DE4X5_DUMP: j = 0; tmp.addr[j++] = dev->irq; @@ -4951,7 +5448,7 @@ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; tmp.lval[j>>2] = lp->chipset; j+=4; if (lp->chipset == DC21140) { - tmp.lval[j>>2] = inl(DE4X5_GEP); j+=4; + tmp.lval[j>>2] = gep_rd(dev); j+=4; } else { tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; @@ -5004,21 +5501,33 @@ #define LP(a) ((struct de4x5_private *)(a)) static struct device *mdev = NULL; static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(io, "i"); +#endif /* LINUX_VERSION_CODE */ int init_module(void) { + int i, num, status = -EIO; struct device *p; - if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) - return -ENOMEM; + num = count_adapters(); + + for (i=0; ipriv)->next_module) { - if (register_netdev(p) != 0) - return -EIO; + if (register_netdev(p) != 0) { + kfree(p); + } else { + status = 0; /* At least one adapter will work */ + lastModule = p; + } } - return 0; + return status; } void @@ -5054,6 +5563,59 @@ return next; } +static int +count_adapters(void) +{ + int i, j; + char name[DE4X5_STRLEN]; + u_char pb, dev_fn, dev_num; + u_short dev_id, vendor; + u_int class = DE4X5_CLASS_CODE; + u_int device, iobase = 0x1000; + + for (j=0, i=1; iname = (char *)(new + 1); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + } + + return new; +} + #endif /* MODULE */ @@ -5064,3 +5626,10 @@ * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c" * End: */ + + + + + + + diff -u --recursive --new-file v2.1.63/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h --- v2.1.63/linux/drivers/net/de4x5.h Tue May 13 22:41:09 1997 +++ linux/drivers/net/de4x5.h Thu Nov 13 21:24:45 1997 @@ -120,7 +120,7 @@ #define DC21140 DC21140_DID #define DC2114x DC2114x_DID #define DC21142 (DC2114x_DID | 0x0010) -#define DC21143 (DC2114x_DID | 0x0020) +#define DC21143 (DC2114x_DID | 0x0030) #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) @@ -360,6 +360,12 @@ #define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ #define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ +#define OMR_DEF (OMR_SDP) +#define OMR_SIA (OMR_SDP | OMR_TTM) +#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_SCR | OMR_HBD | OMR_PS) + /* ** DC21040 Interrupt Mask Register (DE4X5_IMR) */ @@ -606,48 +612,60 @@ ** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) */ /* Valid ONLY for DE500 hardware */ -#define GEP_LNP 0x00000080 /* Link Pass (input) */ -#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ -#define GEP_SDET 0x00000020 /* Signal Detect (input) */ -#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ -#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ -#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ -#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ -#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ -#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ -#define GEP_CTRL 0x00000100 /* GEP control bit */ +#define GEP_LNP 0x00000080 /* Link Pass (input) */ +#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ +#define GEP_SDET 0x00000020 /* Signal Detect (input) */ +#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ +#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ +#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ +#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ +#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ +#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ +#define GEP_CTRL 0x00000100 /* GEP control bit */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f /* Autonegotiation disabled */ +#define CSR15 0x00000008 /* ** SIA Status Register (DE4X5_SISR) */ -#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ -#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ -#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ -#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ -#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ +#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ +#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ +#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ #define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ -#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ -#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ -#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ -#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ -#define SISR_DAO 0x00000080 /* PLL All One */ -#define SISR_DAZ 0x00000040 /* PLL All Zero */ -#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ -#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ -#define SISR_APS 0x00000008 /* Auto Polarity State */ -#define SISR_LKF 0x00000004 /* Link Fail Status */ -#define SISR_NCR 0x00000002 /* Network Connection Error */ -#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ - -#define ANS_NDIS 0x00000000 /* Nway disable */ -#define ANS_TDIS 0x00001000 /* Transmit Disable */ -#define ANS_ADET 0x00002000 /* Ability Detect */ -#define ANS_ACK 0x00003000 /* Acknowledge */ -#define ANS_CACK 0x00004000 /* Complete Acknowledge */ -#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ -#define ANS_LCHK 0x00006000 /* Link Check */ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ +#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ +#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ +#define SISR_DAO 0x00000080 /* PLL All One */ +#define SISR_DAZ 0x00000040 /* PLL All Zero */ +#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ +#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ +#define SISR_APS 0x00000008 /* Auto Polarity State */ +#define SISR_LKF 0x00000004 /* Link Fail Status */ +#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ +#define SISR_NCR 0x00000002 /* Network Connection Error */ +#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ +#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ + +#define ANS_NDIS 0x00000000 /* Nway disable */ +#define ANS_TDIS 0x00001000 /* Transmit Disable */ +#define ANS_ADET 0x00002000 /* Ability Detect */ +#define ANS_ACK 0x00003000 /* Acknowledge */ +#define ANS_CACK 0x00004000 /* Complete Acknowledge */ +#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ +#define ANS_LCHK 0x00006000 /* Link Check */ + +#define SISR_RST 0x00000301 /* CSR12 reset */ +#define SISR_ANR 0x00001301 /* Autonegotiation restart */ /* ** SIA Connectivity Register (DE4X5_SICR) @@ -828,6 +846,7 @@ #define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ #define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ #define DEBUG_PCICFG 0x0100 +#define DEBUG_ALL 0x01ff /* ** Miscellaneous @@ -884,6 +903,12 @@ #define OPEN 2 /* Running */ /* +** Various wait times +*/ +#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ +#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ + +/* ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since ** the vendors seem split 50-50 on how to calculate the OUI register values ** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. @@ -908,7 +933,7 @@ } else if (lp->useSROM && !lp->useMII) {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ + outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ @@ -936,7 +961,7 @@ } else if (lp->useSROM && !lp->useMII) {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ @@ -991,3 +1016,5 @@ #define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ #define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ #define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) diff -u --recursive --new-file v2.1.63/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.1.63/linux/drivers/pci/pci.c Sat Oct 25 02:44:16 1997 +++ linux/drivers/pci/pci.c Fri Nov 14 11:03:40 1997 @@ -162,6 +162,7 @@ DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), + DEVICE( PROMISE, PROMISE_IDE_UDMA,"IDE Ultra DMA/33"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.63/linux/drivers/scsi/Config.in Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/Config.in Thu Nov 13 12:56:48 1997 @@ -92,7 +92,6 @@ dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT if [ "$CONFIG_SCSI_PPA" != "n" ]; then int ' Pedantic EPP-checking' CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3 - int ' EPP timeout' CONFIG_SCSI_PPA_EPP_TIME 128 fi fi dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c --- v2.1.63/linux/drivers/scsi/ppa.c Mon Nov 3 13:04:26 1997 +++ linux/drivers/scsi/ppa.c Thu Nov 13 12:56:48 1997 @@ -6,369 +6,291 @@ * (c) 1995,1996 Grant R. Guenther, grant@torque.net, * under the terms of the GNU Public License. * - */ - -/* This driver was developed without the benefit of any technical - * specifications for the interface. Instead, a modified version of - * DOSemu was used to monitor the protocol used by the DOS driver - * for this adapter. I have no idea how my programming model relates - * to IOMEGA's design. - * - * IOMEGA's driver does not generate linked commands. I've never - * observed a SCSI message byte in the protocol transactions, so - * I am assuming that as long as linked commands are not used - * we won't see any. - * - * For more information, see the file drivers/scsi/README.ppa. - * - */ - -/* - * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu) - * to support EPP and scatter-gather. [0.26-athena] - * - * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au) - * in response to this driver "mis-behaving" on his machine. - * Fixed EPP to handle "software" changing of EPP port data direction. - * Chased down EPP timeouts - * Made this driver "kernel version friendly" [0.28-athena] - * - * Really hacked it out of existance (David Campbell) - * EPP timeouts no longer occur (yet better handling) - * Probes known parallel ports - * Autodetects EPP / ECP / PS2 / NIBBLE - * Support for multiple devices (does anyone need this??) - * [0.29-Curtin] - * [ Stuff removed ] - * - * Modified PEDANTIC for less PEDANTIC drivers as people - * were complaining about speed (I received a report indicating - * that PEDANTIC is necessary for WinBond chipsets. - * Updated config_ppa and Makefile - * [0.36b-Curtin] - * - * First round of cleanups - * Remove prior 1.3.34 kernel support - * SMC support changed - * ECP+EPP detection always invoked. - * If compat mode => PS/2 - * else ecp_sync() called (ECP+EPP uses FIFO). - * Added routine to detect interrupt channel for ECP (not used) - * Changed version numbering - * 1 Major number - * 00 Minor revision number - * ALPHA Expected stability (alpha, beta, stable) - * [Curtin-1-00-ALPHA] - * Second round of cleanups - * Clean up timer queues - * Fixed problem with non-detection of PS/2 ports - * SMC ECP+EPP confirmed to work, remove option from config_ppa - * [Curtin-1-01-ALPHA] - * - * Parport hits with a vengance!! - * Several internal revisions have been made with huge amounts of - * fixes including: - * ioport_2_hostno removed (unique_id is quicker) - * SMC compat option is history - * Driver name / info hardwired - * Played with inlines and saved 4k on module - * Parport support - * Using PnP Parport allows use of printer attached to - * ZIP drive. - * Numerous fixups for device registration and to allow - * proper aborts. - * Version jumps a few numbers here - considered BETA - * Shipping Parport with monolithic driver :) - * [Curtin-1-05-BETA] - * - * Fixed code to ensure SCSI abort will release the SCSI command - * if the driver is STILL trying to claim the parport (PNP ver) - * Now I have to fix the lp driver then there will NEVER be a - * problem. - * Got around to doing the ppa_queuecommand() clean up - * Fixed bug relating to SMC EPP+ECP and monolithic driver - * [Curtin-1-06-BETA] - * - * Where did the ppa_setup() code disappear to ?? - * Back in now... - * Distribution of ppa now independent of parport (less work for me). - * Also cleaned up the port detection to allow for variations on - * IO aliasing (in an attempt to fix a few problems with some - * machines...) - * [Curtin-1-07-BETA] - * - * Rewrote detection code for monolithic driver and ported changes to - * parport driver. Result is more stable detection of hardware and - * better immunity to port aliasing (old XT cards). - * Parport 0.16 (or better) is required for parport operation and - * ECP+EPP modes, otherwise the latest parport edition is recommended. - * - * When using EPP and writing to disk CPU usage > 40%, while reading <10%. - * This is due to ZIP drive IO scheduling, the drive does a verify after - * write to ensure data integrity (removable media is ALWAYS questionable - * since you never know where it has been). - * Some fancy programing *MAY* fix the problem but at 30 Mb/min is just - * over 10 sectors per jiffy. - * - * Hmm... I think I know a way but it will send the driver into - * ALPHA state again. - * [Curtin-1-08-STABLE] + * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800) + * campbell@gear.torque.net + * dcampbel@p01.as17.honeywell.com.au */ #include /* The following #define is to avoid a clash with hosts.c */ #define PPA_CODE 1 -#include "ppa.h" -/* batteries not included :-) */ -/* - * modes in which the driver can operate - */ -#define PPA_AUTODETECT 0 /* Autodetect mode */ -#define PPA_NIBBLE 1 /* work in standard 4 bit mode */ -#define PPA_PS2 2 /* PS/2 byte mode */ -#define PPA_EPP_8 3 /* EPP mode, 8 bit */ -#define PPA_EPP_16 4 /* EPP mode, 16 bit */ -#define PPA_EPP_32 5 /* EPP mode, 32 bit */ -#define PPA_UNKNOWN 6 /* Just in case... */ - -static char *PPA_MODE_STRING[] = -{ - "Autodetect", - "SPP", - "PS/2", - "EPP 8 bit", - "EPP 16 bit", - "EPP 32 bit", - "Unknown"}; +#include +#include +#include +#include "sd.h" +#include "hosts.h" +int ppa_release(struct Scsi_Host *); typedef struct { - struct pardevice *dev; /* Parport device entry */ - int speed; /* General PPA delay constant */ - int speed_fast; /* Const for nibble/byte modes */ - int epp_speed; /* Reset time period */ - int mode; /* Transfer mode */ - int timeout; /* Number of timeouts */ - int host; /* Host number (for proc) */ - int abort_flag; /* Abort flag */ - int error_code; /* Error code */ - int ppa_failed; /* Failure flag */ - Scsi_Cmnd *cur_cmd; /* Current queued command */ - void (*done) (Scsi_Cmnd *); /* Done func for queuecommand */ - struct tq_struct ppa_tq; /* Polling interupt stuff */ - struct wait_queue *ppa_wait_q; /* Used for PnP stuff */ + struct pardevice *dev; /* Parport device entry */ + int base; /* Actual port address */ + int mode; /* Transfer mode */ + int host; /* Host number (for proc) */ + Scsi_Cmnd *cur_cmd; /* Current queued command */ + struct tq_struct ppa_tq; /* Polling interupt stuff */ + unsigned long jstart; /* Jiffies at start */ + unsigned int failed:1; /* Failure flag */ + unsigned int p_busy:1; /* Parport sharing busy flag */ } ppa_struct; -static void ppa_interrupt(void *data); -/* I know that this is a mess but it works!! */ +#define PPA_EMPTY \ +{NULL, /* dev */ \ +-1, /* base */ \ +PPA_AUTODETECT, /* mode */ \ +-1, /* host */ \ +NULL, /* cur_cmd */ \ +{0, 0, ppa_interrupt, NULL}, \ +0, /* jstart */ \ +0, /* failed */ \ +0 /* p_busy */ \ +} + +#include "ppa.h" +#include + +#ifdef CONFIG_KERNELD +#include +#ifndef PARPORT_MODULES +#define PARPORT_MODULES "parport_pc" +#endif +#endif + #define NO_HOSTS 4 static ppa_struct ppa_hosts[NO_HOSTS] = +{PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY}; + +#define PPA_BASE(x) ppa_hosts[(x)].base + +void ppa_wakeup(void *ref) { - {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL, - {0, 0, ppa_interrupt, NULL}, NULL}, - {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL, - {0, 0, ppa_interrupt, NULL}, NULL}, - {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL, - {0, 0, ppa_interrupt, NULL}, NULL}, - {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL, - {0, 0, ppa_interrupt, NULL}, NULL} -}; - -/* This is a global option */ -static int ppa_speed = -1; /* Set to >0 to act as a global value */ -static int ppa_speed_fast = -1; /* ditto.. */ -static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */ - -/* other options */ -#define PPA_CAN_QUEUE 1 /* use "queueing" interface */ -#define PPA_BURST_SIZE 512 /* block size for bulk transfers */ -#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */ -#define PPA_SPIN_TMO 500000 /* ppa_wait loop limiter */ - -#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32) - -/* args to ppa_connect */ -#define CONNECT_EPP_MAYBE 1 -#define CONNECT_NORMAL 0 - -#define PPA_BASE(x) ppa_hosts[(x)].dev->port->base - -/* Port IO - Sorry Grant but I prefer the following symbols */ -#define r_dtr(x) inb(PPA_BASE(x)) -#define r_str(x) inb(PPA_BASE(x)+1) -#define r_ctr(x) inb(PPA_BASE(x)+2) -#define r_epp(x) inb(PPA_BASE(x)+4) -#define r_fifo(x) inb(PPA_BASE(x)+0x400) -#define r_ecr(x) inb(PPA_BASE(x)+0x402) - -#define w_dtr(x,y) outb(y, PPA_BASE(x)) -#define w_str(x,y) outb(y, PPA_BASE(x)+1) -#define w_ctr(x,y) outb(y, PPA_BASE(x)+2);\ - udelay( ppa_hosts[(x)].speed) -#define w_epp(x,y) outb(y, PPA_BASE(x)+4) -#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400) -#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402) - -static void ppa_wakeup(void *ref) -{ - ppa_struct *ppa_dev = (ppa_struct *) ref; - - if (!ppa_dev->ppa_wait_q) - return; /* Wake up whom ? */ - - /* Claim the Parport */ - if (parport_claim(ppa_dev->dev)) - return; /* Shouldn't happen */ + ppa_struct *ppa_dev = (ppa_struct *) ref; - wake_up(&ppa_dev->ppa_wait_q); + if (!ppa_dev->p_busy) return; + + if (parport_claim(ppa_dev->dev)) { + printk("ppa: bug in ppa_wakeup\n"); + return; + } + + ppa_dev->p_busy = 0; + ppa_dev->base = ppa_dev->dev->port->base; + if (ppa_dev->cur_cmd) + ppa_dev->cur_cmd->SCp.phase++; + return; } int ppa_release(struct Scsi_Host *host) { - int host_no = host->unique_id; + int host_no = host->unique_id; - printk("Releasing ppa%i\n", host_no); - parport_unregister_device(ppa_hosts[host_no].dev); - return 0; + printk("Releasing ppa%i\n", host_no); + parport_unregister_device(ppa_hosts[host_no].dev); + return 0; } static int ppa_pb_claim(int host_no) { - if (parport_claim(ppa_hosts[host_no].dev)) { - sleep_on(&ppa_hosts[host_no].ppa_wait_q); - ppa_hosts[host_no].ppa_wait_q = NULL; - - /* Check to see if we were aborted or reset */ - if (ppa_hosts[host_no].dev->port->cad != - ppa_hosts[host_no].dev) { - printk("Abort detected on ppa%i\n", host_no); - return 1; - } - } - return 0; -} + if (parport_claim(ppa_hosts[host_no].dev)) { + ppa_hosts[host_no].p_busy = 1; + return 1; + } -static void ppa_pb_release(int host_no) -{ - parport_release(ppa_hosts[host_no].dev); + PPA_BASE(host_no) = ppa_hosts[host_no].dev->port->base; + if (ppa_hosts[host_no].cur_cmd) + ppa_hosts[host_no].cur_cmd->SCp.phase++; + return 0; } +#define ppa_pb_release(x) parport_release(ppa_hosts[(x)].dev) -/* Placed here so everyone knows what ecp_sync does.. */ -static void ecp_sync(int host_no) -{ - int i, r; - - r = r_ecr(host_no); - if ((r & 0xe0) != 0x80) - return; +/*************************************************************************** + * Parallel port probing routines * + ***************************************************************************/ - for (i = 0; i < 100; i++) { - r = r_ecr(host_no); - if (r & 0x01) - return; - udelay(5); - } +#ifdef MODULE +Scsi_Host_Template driver_template = PPA; +#include "scsi_module.c" +#endif - printk("ppa: ECP sync failed as data still present in FIFO.\n"); -} +/* + * Start of Chipset kludges + */ -static inline void ppa_d_pulse(int host_no, char b) +int ppa_detect(Scsi_Host_Template * host) { - w_dtr(host_no, b); - w_ctr(host_no, 0xc); - w_ctr(host_no, 0xe); - w_ctr(host_no, 0xc); - w_ctr(host_no, 0x4); - w_ctr(host_no, 0xc); -} + struct Scsi_Host *hreg; + int ports; + int i, nhosts, try_again; + struct parport *pb = parport_enumerate(); + + printk("ppa: Version %s\n", PPA_VERSION); + nhosts = 0; + try_again = 0; + +#ifdef CONFIG_KERNELD + if (!pb) { + request_module(PARPORT_MODULES); + pb = parport_enumerate(); + } +#endif -static void ppa_disconnect(int host_no) -{ - ppa_d_pulse(host_no, 0); - ppa_d_pulse(host_no, 0x3c); - ppa_d_pulse(host_no, 0x20); - ppa_d_pulse(host_no, 0xf); + if (!pb) { + printk("ppa: parport reports no devices.\n"); + return 0; + } + retry_entry: + for (i = 0; pb; i++, pb = pb->next) { + int modes, ppb; + + ppa_hosts[i].dev = + parport_register_device(pb, "ppa", NULL, ppa_wakeup, + NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]); - ppa_pb_release(host_no); -} + /* Claim the bus so it remembers what we do to the control + * registers. [ CTR and ECP ] + */ + if (ppa_pb_claim(i)) + while (ppa_hosts[i].p_busy) + schedule(); /* Whe can safe schedule() here */ + ppb = PPA_BASE(i); + w_ctr(ppb, 0x0c); + modes = ppa_hosts[i].dev->port->modes; -static inline void ppa_c_pulse(int host_no, char b) -{ - w_dtr(host_no, b); - w_ctr(host_no, 0x4); - w_ctr(host_no, 0x6); - w_ctr(host_no, 0x4); - w_ctr(host_no, 0xc); -} + /* Mode detection works up the chain of speed + * This avoids a nasty if-then-else-if-... tree + */ + ppa_hosts[i].mode = PPA_NIBBLE; -static int ppa_connect(int host_no, int flag) -{ - int retv = ppa_pb_claim(host_no); + if (modes & PARPORT_MODE_PCPS2) + ppa_hosts[i].mode = PPA_PS2; - ppa_c_pulse(host_no, 0); - ppa_c_pulse(host_no, 0x3c); - ppa_c_pulse(host_no, 0x20); - if ((flag == CONNECT_EPP_MAYBE) && - IN_EPP_MODE(ppa_hosts[host_no].mode)) - ppa_c_pulse(host_no, 0xcf); - else - ppa_c_pulse(host_no, 0x8f); + if (modes & PARPORT_MODE_PCECPPS2) { + w_ecr(ppb, 0x20); + ppa_hosts[i].mode = PPA_PS2; + } + if (modes & PARPORT_MODE_PCECPEPP) + w_ecr(ppb, 0x80); - return retv; -} + /* Done configuration */ + ppa_pb_release(i); -static void ppa_do_reset(int host_no) -{ - /* - * SCSI reset taken from ppa_init and checked with - * Iomega document that Grant has (via email :( - */ - ppa_pb_claim(host_no); - ppa_disconnect(host_no); + if (ppa_init(i)) { + parport_unregister_device(ppa_hosts[i].dev); + continue; + } + /* now the glue ... */ + switch (ppa_hosts[i].mode) { + case PPA_NIBBLE: + ports = 3; + break; + case PPA_PS2: + ports = 3; + break; + case PPA_EPP_8: + case PPA_EPP_16: + case PPA_EPP_32: + ports = 8; + break; + default: /* Never gets here */ + continue; + } + + host->can_queue = PPA_CAN_QUEUE; + host->sg_tablesize = ppa_sg; + hreg = scsi_register(host, 0); + hreg->io_port = pb->base; + hreg->n_io_port = ports; + hreg->dma_channel = -1; + hreg->unique_id = i; + ppa_hosts[i].host = hreg->host_no; + nhosts++; + } + if (nhosts == 0) { + if (try_again == 1) + return 0; + try_again = 1; + goto retry_entry; + } else + return 1; /* return number of hosts detected */ +} - ppa_connect(host_no, CONNECT_NORMAL); +/* This is to give the ppa driver a way to modify the timings (and other + * parameters) by writing to the /proc/scsi/ppa/0 file. + * Very simple method really... (To simple, no error checking :( ) + * Reason: Kernel hackers HATE having to unload and reload modules for + * testing... + * Also gives a method to use a script to obtain optimum timings (TODO) + */ - w_ctr(host_no, 0x6); - w_ctr(host_no, 0x4); - w_dtr(host_no, 0x40); - w_ctr(host_no, 0x8); - udelay(50); - w_ctr(host_no, 0xc); +static inline int ppa_strncmp(const char *a, const char *b, int len) +{ + int loop; + for (loop = 0; loop < len; loop++) + if (a[loop] != b[loop]) + return 1; - ppa_disconnect(host_no); + return 0; } -static char ppa_select(int host_no, int initiator, int target) +static inline int ppa_proc_write(int hostno, char *buffer, int length) { - char r; - int k; + unsigned long x; - r = r_str(host_no); /* TODO */ + if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) { + x = simple_strtoul(buffer + 5, NULL, 0); + ppa_hosts[hostno].mode = x; + return length; + } + printk("ppa /proc: invalid variable\n"); + return (-EINVAL); +} - w_dtr(host_no, (1 << target)); - w_ctr(host_no, 0xe); - w_ctr(host_no, 0xc); - w_dtr(host_no, (1 << initiator)); - w_ctr(host_no, 0x8); +int ppa_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +{ + int i; + int len = 0; - k = 0; - while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO)) - barrier(); + for (i = 0; i < 4; i++) + if (ppa_hosts[i].host == hostno) + break; + + if (inout) + return ppa_proc_write(i, buffer, length); + + len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION); + len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name); + len += sprintf(buffer + len, "Mode : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]); - if (k >= PPA_SELECT_TMO) - return 0; + /* Request for beyond end of buffer */ + if (offset > length) + return 0; - return r; + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + return len; } +/* end of ppa.c */ +static int device_check(int host_no); -static void ppa_fail(int host_no, int error_code) +#if PPA_DEBUG > 0 +#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\ + y, __FUNCTION__, __LINE__); ppa_fail_func(x,y); +static inline void ppa_fail_func(int host_no, int error_code) +#else +static inline void ppa_fail(int host_no, int error_code) +#endif { - ppa_hosts[host_no].error_code = error_code; - ppa_hosts[host_no].ppa_failed = 1; - ppa_disconnect(host_no); + /* If we fail a device then we trash status / message bytes */ + if (ppa_hosts[host_no].cur_cmd) { + ppa_hosts[host_no].cur_cmd->result = error_code << 16; + ppa_hosts[host_no].failed = 1; + } } /* @@ -378,483 +300,539 @@ * doesn't appear to be designed to support interrupts. We spin on * the 0x80 ready bit. */ -static char ppa_wait(int host_no) +static unsigned char ppa_wait(int host_no) { - int k; - char r; - - k = 0; - while (!((r = r_str(host_no)) & 0x80) - && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag) - barrier(); - - /* check if we were interrupted */ - if (ppa_hosts[host_no].abort_flag) { - if (ppa_hosts[host_no].abort_flag == 1) - ppa_fail(host_no, DID_ABORT); - else { - ppa_do_reset(host_no); - ppa_fail(host_no, DID_RESET); - } - return 0; - } - /* check if timed out */ - if (k >= PPA_SPIN_TMO) { - ppa_fail(host_no, DID_TIME_OUT); - return 0; /* command timed out */ - } - /* - * return some status information. - * Semantics: 0xc0 = ZIP wants more data - * 0xd0 = ZIP wants to send more data - * 0xf0 = end of transfer, ZIP is sending status - */ + int k; + unsigned short ppb = PPA_BASE(host_no); + unsigned char r; + + k = PPA_SPIN_TMO; + do { + r = r_str(ppb); + k--; + udelay(1); + } + while (!(r & 0x80) && (k)); + + /* + * return some status information. + * Semantics: 0xc0 = ZIP wants more data + * 0xd0 = ZIP wants to send more data + * 0xe0 = ZIP is expecting SCSI command data + * 0xf0 = end of transfer, ZIP is sending status + */ + if (k) return (r & 0xf0); -} + /* Counter expired - Time out occured */ + ppa_fail(host_no, DID_TIME_OUT); + printk("ppa timeout in ppa_wait\n"); + return 0; /* command timed out */ +} -/* - * This is based on a trace of what the Iomega DOS 'guest' driver does. - * I've tried several different kinds of parallel ports with guest and - * coded this to react in the same ways that it does. - * - * The return value from this function is just a hint about where the - * handshaking failed. - * +/* + * output a string, in whatever mode is available, according to the + * PPA protocol. */ -static int ppa_init(int host_no) +static inline void epp_reset(unsigned short ppb) { - if (ppa_pb_claim(host_no)) - return 1; - ppa_disconnect(host_no); + int i; - ppa_connect(host_no, CONNECT_NORMAL); + i = r_str(ppb); + w_str(ppb, i); + w_str(ppb, i & 0xfe); +} - w_ctr(host_no, 0x6); - if ((r_str(host_no) & 0xf0) != 0xf0) { - ppa_pb_release(host_no); - return 2; - } - w_ctr(host_no, 0x4); - if ((r_str(host_no) & 0xf0) != 0x80) { - ppa_pb_release(host_no); - return 3; - } - /* This is a SCSI reset signal */ - w_dtr(host_no, 0x40); - w_ctr(host_no, 0x8); - udelay(50); - w_ctr(host_no, 0xc); +static inline void ecp_sync(unsigned short ppb) +{ + int i; - ppa_disconnect(host_no); + if ((r_ecr(ppb) & 0xe0) != 0x80) + return; - return 0; + for (i = 0; i < 100; i++) { + if (r_ecr(ppb) & 0x01) + return; + udelay(5); + } + printk("ppa: ECP sync failed as data still present in FIFO.\n"); } -/* - * check the epp status. After a EPP transfer, it should be true that - * 1) the TIMEOUT bit (SPP_STR.0) is clear - * 2) the READY bit (SPP_STR.7) is set +/* + * Here is the asm code for the SPP/PS2 protocols for the i386. + * This has been optimised for speed on 386/486 machines. There will + * be very little improvement on the current 586+ machines as it is the + * IO statements which will limit throughput. */ -static int ppa_check_epp_status(int host_no) -{ - char r; - r = r_str(host_no); +#ifdef __i386__ +#define BYTE_OUT(reg) \ + " movb " #reg ",%%al\n" \ + " outb %%al,(%%dx)\n" \ + " addl $2,%%edx\n" \ + " movb $0x0e,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " movb $0x0c,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " subl $2,%%edx\n" + +static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len) +{ + /* + * %eax scratch + * %ebx Data to transfer + * %ecx Counter (Don't touch!!) + * %edx Port + * %esi Source buffer (mem pointer) + * + * In case you are wondering what the last line of the asm does... + * : : + */ + asm("shr $2,%%ecx\n" \ + " jz .no_more_bulk_bo\n" \ + " .align 4\n" \ + ".loop_bulk_bo:\n" \ + " movl (%%esi),%%ebx\n" \ + BYTE_OUT(%%bl) \ + BYTE_OUT(%%bh) \ + " rorl $16,%%ebx\n" \ + BYTE_OUT(%%bl) \ + BYTE_OUT(%%bh) \ + " addl $4,%%esi\n" \ + " loop .loop_bulk_bo\n" \ + " .align 4\n" \ + ".no_more_bulk_bo:" \ + : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx"); + + asm("andl $3,%%ecx\n" \ + " jz .no_more_loose_bo\n" \ + " .align 4\n" \ + ".loop_loose_bo:\n" \ + BYTE_OUT((%%esi)) \ + " incl %%esi\n" \ + " loop .loop_loose_bo\n" \ + ".no_more_loose_bo:\n" \ + : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx"); + return 1; /* All went well - we hope! */ +} + +#define BYTE_IN(reg) \ + " inb (%%dx),%%al\n" \ + " movb %%al," #reg "\n" \ + " addl $2,%%edx\n" \ + " movb $0x27,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " movb $0x25,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " subl $2,%%edx\n" + +static inline int ppa_byte_in(unsigned short base, char *buffer, int len) +{ + /* + * %eax scratch + * %ebx Data to transfer + * %ecx Counter (Don't touch!!) + * %edx Port + * %esi Source buffer (mem pointer) + * + * In case you are wondering what the last line of the asm does... + * : : + */ + asm("shr $2,%%ecx\n" \ + " jz .no_more_bulk_bi\n" \ + " .align 4\n" \ + ".loop_bulk_bi:\n" \ + BYTE_IN(%%bl) \ + BYTE_IN(%%bh) \ + " rorl $16,%%ebx\n" \ + BYTE_IN(%%bl) \ + BYTE_IN(%%bh) \ + " rorl $16,%%ebx\n" \ + " movl %%ebx,(%%esi)\n" \ + " addl $4,%%esi\n" \ + " loop .loop_bulk_bi\n" \ + " .align 4\n" \ + ".no_more_bulk_bi:" \ + : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx"); + + asm("andl $3,%%ecx\n" \ + " jz .no_more_loose_bi\n" \ + " .align 4\n" \ + ".loop_loose_bi:\n" \ + BYTE_IN((%%esi)) \ + " incl %%esi\n" \ + " loop .loop_loose_bi\n" \ + ".no_more_loose_bi:\n" \ + : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx"); + return 1; /* All went well - we hope! */ +} + +#define NIBBLE_IN(reg) \ + " incl %%edx\n" \ + " movb $0x04,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " decl %%edx\n" \ + " inb (%%dx),%%al\n" \ + " andb $0xf0,%%al\n" \ + " movb %%al," #reg "\n" \ + " incl %%edx\n" \ + " movb $0x06,%%al\n" \ + " outb %%al,(%%dx)\n" \ + " decl %%edx\n" \ + " inb (%%dx),%%al\n" \ + " shrb $4,%%al\n" \ + " orb %%al," #reg "\n" + +static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len) +{ + /* + * %eax scratch + * %ebx Data to transfer + * %ecx Counter (Don't touch!!) + * %edx Port + * %esi Source buffer (mem pointer) + * + * In case you are wondering what the last line of the asm does... + * : : + */ + asm("shr $2,%%ecx\n" \ + " jz .no_more_bulk_ni\n" \ + " .align 4\n" \ + ".loop_bulk_ni:\n" \ + NIBBLE_IN(%%bl) \ + NIBBLE_IN(%%bh) \ + " rorl $16,%%ebx\n" \ + NIBBLE_IN(%%bl) \ + NIBBLE_IN(%%bh) \ + " rorl $16,%%ebx\n" \ + " movl %%ebx,(%%esi)\n" \ + " addl $4,%%esi\n" \ + " loop .loop_bulk_ni\n" \ + " .align 4\n" \ + ".no_more_bulk_ni:" \ + : "=S"(buffer): "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx"); + + asm("andl $3,%%ecx\n" \ + " jz .no_more_loose_ni\n" \ + " .align 4\n" \ + ".loop_loose_ni:\n" \ + NIBBLE_IN((%%esi)) \ + " incl %%esi\n" \ + " loop .loop_loose_ni\n" \ + ".no_more_loose_ni:\n" \ + : /* no output */ : "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx"); + return 1; /* All went well - we hope! */ +} +#else /* Old style C routines */ + +static inline int ppa_byte_out(unsigned short base, const char *buffer, int len) +{ + unsigned short ctr_p = base + 2; + int i; + + for (i = len; i; i--) { + outb(*buffer++, base); + outb(0xe, ctr_p); + outb(0xc, ctr_p); + } + return 1; /* All went well - we hope! */ +} + +static inline int ppa_byte_in(unsigned short base, char *buffer, int len) +{ + unsigned short ctr_p = base + 2; + int i; + + for (i = len; i; i--) { + *buffer++ = inb(base); + outb(0x27, ctr_p); + outb(0x25, ctr_p); + } + return 1; /* All went well - we hope! */ +} + +static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len) +{ + unsigned short ctr_p = str_p + 1; + unsigned char h, l; + int i; + + for (i = len; i; i--) { + outb(0x4, ctr_p); + h = inb(str_p); + outb(0x6, ctr_p); + l = inb(str_p); + *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4); + } + return 1; /* All went well - we hope! */ +} +#endif - if (r & 1) { - /* EPP timeout, according to the PC87332 manual */ - /* Semantics of clearing EPP timeout bit. - * PC87332 - reading SPP_STR does it... - * SMC - write 1 to EPP timeout bit - * Others - (???) write 0 to EPP timeout bit - */ - w_str(host_no, r); - w_str(host_no, r & 0xfe); - ppa_hosts[host_no].timeout++; - printk("PPA: EPP timeout on port 0x%04x\n", - PPA_BASE(host_no)); - ppa_fail(host_no, DID_BUS_BUSY); - return 0; - } - if (!(r & 0x80)) { - ppa_fail(host_no, DID_ERROR); - return 0; - } - return 1; +static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len) +{ + int i; + for (i = len; i; i--) { + outb(*buffer++, epp_p); +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2 + if (inb(str_p) & 0x01) + return 0; +#endif + } + return 1; } -static inline int ppa_force_epp_byte(int host_no, char x) +static int ppa_out(int host_no, char *buffer, int len) { -/* This routine forces a byte down the EPP data port whether the - * device is ready or not... - */ - char r; + int r; + unsigned short ppb = PPA_BASE(host_no); - w_epp(host_no, x); + r = ppa_wait(host_no); - r = r_str(host_no); - if (!(r & 1)) - return 1; - - if (ppa_hosts[host_no].epp_speed > 0) { - /* EPP timeout, according to the PC87332 manual */ - /* Semantics of clearing EPP timeout bit. - * PC87332 - reading SPP_STR does it... - * SMC - write 1 to EPP timeout bit - * Others - (???) write 0 to EPP timeout bit - */ - w_str(host_no, r); - w_str(host_no, r & 0xfe); + if ((r & 0x50) != 0x40) { + ppa_fail(host_no, DID_ERROR); + return 0; + } + switch (ppa_hosts[host_no].mode) { + case PPA_NIBBLE: + case PPA_PS2: + /* 8 bit output, with a loop */ + r = ppa_byte_out(ppb, buffer, len); + break; + + case PPA_EPP_32: + case PPA_EPP_16: + case PPA_EPP_8: + epp_reset(ppb); + w_ctr(ppb, 0x4); +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1 + r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len); +#else +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0 + if (!(((long) buffer | len) & 0x03)) + outsl(ppb + 4, buffer, len >> 2); + else +#endif + outsb(ppb + 4, buffer, len); + w_ctr(ppb, 0xc); + r = !(r_str(ppb) & 0x01); +#endif + w_ctr(ppb, 0xc); + ecp_sync(ppb); + break; + + default: + printk("PPA: bug in ppa_out()\n"); + r = 0; + } + return r; +} + +static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len) +{ + int i; + for (i = len; i; i--) { + *buffer++ = inb(epp_p); +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2 + if (inb(str_p) & 0x01) + return 0; +#endif + } + return 1; +} - /* Take a deep breath, count to 10 and then... */ - udelay(ppa_hosts[host_no].epp_speed); +static int ppa_in(int host_no, char *buffer, int len) +{ + int r; + unsigned short ppb = PPA_BASE(host_no); - /* Second time around */ - w_epp(host_no, x); + r = ppa_wait(host_no); - r = r_str(host_no); - } - if (r & 1) { - w_str(host_no, r); - w_str(host_no, r & 0xfe); - ppa_hosts[host_no].timeout++; - printk("PPA: warning: EPP timeout\n"); - ppa_fail(host_no, DID_BUS_BUSY); - return 0; - } else - return 1; -} - -static inline int ppa_send_command_epp(Scsi_Cmnd * cmd) -{ - int host_no = cmd->host->unique_id; - int k; - - w_ctr(host_no, 0x4); - for (k = 0; k < cmd->cmd_len; k++) { /* send the command */ - if (!ppa_force_epp_byte(host_no, cmd->cmnd[k])) - return 0; - } - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return 1; + if ((r & 0x50) != 0x50) { + ppa_fail(host_no, DID_ERROR); + return 0; + } + switch (ppa_hosts[host_no].mode) { + case PPA_NIBBLE: + /* 4 bit input, with a loop */ + r = ppa_nibble_in(ppb + 1, buffer, len); + w_ctr(ppb, 0xc); + break; + + case PPA_PS2: + /* 8 bit input, with a loop */ + w_ctr(ppb, 0x25); + r = ppa_byte_in(ppb, buffer, len); + w_ctr(ppb, 0x4); + w_ctr(ppb, 0xc); + break; + + case PPA_EPP_32: + case PPA_EPP_16: + case PPA_EPP_8: + epp_reset(ppb); + w_ctr(ppb, 0x24); +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1 + r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len); +#else +#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0 + if (!(((long) buffer | len) & 0x03)) + insl(ppb + 4, buffer, len >> 2); + else +#endif + insb(ppb + 4, buffer, len); + w_ctr(ppb, 0x2c); + r = !(r_str(ppb) & 0x01); +#endif + w_ctr(ppb, 0x2c); + ecp_sync(ppb); + break; + + default: + printk("PPA: bug in ppa_ins()\n"); + r = 0; + break; + } + return r; +} + +/* end of ppa_io.h */ +static inline void ppa_d_pulse(unsigned short ppb, unsigned char b) +{ + w_dtr(ppb, b); + w_ctr(ppb, 0xc); + w_ctr(ppb, 0xe); + w_ctr(ppb, 0xc); + w_ctr(ppb, 0x4); + w_ctr(ppb, 0xc); } -static inline int ppa_send_command_normal(Scsi_Cmnd * cmd) +static void ppa_disconnect(int host_no) { - int host_no = cmd->host->unique_id; - int k; + unsigned short ppb = PPA_BASE(host_no); - w_ctr(host_no, 0xc); - - for (k = 0; k < cmd->cmd_len; k++) { /* send the command */ - if (!ppa_wait(host_no)) - return 0; - w_dtr(host_no, cmd->cmnd[k]); - w_ctr(host_no, 0xe); - w_ctr(host_no, 0xc); - } - return 1; + ppa_d_pulse(ppb, 0); + ppa_d_pulse(ppb, 0x3c); + ppa_d_pulse(ppb, 0x20); + ppa_d_pulse(ppb, 0xf); } -static int ppa_start(Scsi_Cmnd * cmd) +static inline void ppa_c_pulse(unsigned short ppb, unsigned char b) { - int host_no = cmd->host->unique_id; + w_dtr(ppb, b); + w_ctr(ppb, 0x4); + w_ctr(ppb, 0x6); + w_ctr(ppb, 0x4); + w_ctr(ppb, 0xc); +} - /* - * by default, the command failed, - * unless explicitly completed in ppa_completion(). - */ - ppa_hosts[host_no].error_code = DID_ERROR; - ppa_hosts[host_no].abort_flag = 0; - ppa_hosts[host_no].ppa_failed = 0; - - if (cmd->target == PPA_INITIATOR) { - ppa_hosts[host_no].error_code = DID_BAD_TARGET; - ppa_hosts[host_no].ppa_failed = 1; - return 0; - } - if (ppa_connect(host_no, CONNECT_EPP_MAYBE)) - return 0; +static inline void ppa_connect(int host_no, int flag) +{ + unsigned short ppb = PPA_BASE(host_no); - if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) { - ppa_fail(host_no, DID_NO_CONNECT); - return 0; - } - if (IN_EPP_MODE(ppa_hosts[host_no].mode)) - return ppa_send_command_epp(cmd); - else - return ppa_send_command_normal(cmd); + ppa_c_pulse(ppb, 0); + ppa_c_pulse(ppb, 0x3c); + ppa_c_pulse(ppb, 0x20); + if ((flag == CONNECT_EPP_MAYBE) && + IN_EPP_MODE(ppa_hosts[host_no].mode)) + ppa_c_pulse(ppb, 0xcf); + else + ppa_c_pulse(ppb, 0x8f); } -/* - * output a string, in whatever mode is available, according to the - * PPA protocol. - */ -static inline int ppa_outs(int host_no, char *buffer, int len) +static int ppa_select(int host_no, int target) { - int k; -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0 - int r; -#endif - - switch (ppa_hosts[host_no].mode) { - case PPA_NIBBLE: - case PPA_PS2: - /* 8 bit output, with a loop */ - for (k = len; k; k--) { - w_dtr(host_no, *buffer++); - w_ctr(host_no, 0xe); - w_ctr(host_no, 0xc); - } - return 1; /* assume transfer went OK */ + int k; + unsigned short ppb = PPA_BASE(host_no); -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0 - case PPA_EPP_32: -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2 - w_ctr(host_no, 0x4); - for (k = len; k; k -= 4) { - w_epp(host_no, *buffer++); - w_epp(host_no, *buffer++); - w_epp(host_no, *buffer++); - w_epp(host_no, *buffer++); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return 1; -#endif - case PPA_EPP_16: -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3 - w_ctr(host_no, 0x4); - for (k = len; k; k -= 2) { - w_epp(host_no, *buffer++); - w_epp(host_no, *buffer++); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return 1; -#endif - case PPA_EPP_8: - w_ctr(host_no, 0x4); - for (k = len; k; k--) { - w_epp(host_no, *buffer++); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return 1; -#else - case PPA_EPP_32: - case PPA_EPP_16: - case PPA_EPP_8: - w_ctr(host_no, 0x4); - switch (ppa_hosts[host_no].mode) { - case PPA_EPP_8: - outsb(PPA_BASE(host_no) + 0x04, - buffer, len); - break; - case PPA_EPP_16: - outsw(PPA_BASE(host_no) + 0x04, - buffer, len / 2); - break; - case PPA_EPP_32: - outsl(PPA_BASE(host_no) + 0x04, - buffer, len / 4); - break; - } - k = ppa_check_epp_status(host_no); - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return k; -#endif + /* + * Bit 6 (0x40) is the device selected bit. + * First we must wait till the current device goes off line... + */ + k = PPA_SELECT_TMO; + do { + k--; + } while ((r_str(ppb) & 0x40) && (k)); + if (!k) + return 0; - default: - printk("PPA: bug in ppa_outs()\n"); - } + w_dtr(ppb, (1 << target)); + w_ctr(ppb, 0xe); + w_ctr(ppb, 0xc); + w_dtr(ppb, 0x80); /* This is NOT the initator */ + w_ctr(ppb, 0x8); + + k = PPA_SELECT_TMO; + do { + k--; + } + while (!(r_str(ppb) & 0x40) && (k)); + if (!k) return 0; + + return 1; } -static inline int ppa_outb(int host_no, char d) +/* + * This is based on a trace of what the Iomega DOS 'guest' driver does. + * I've tried several different kinds of parallel ports with guest and + * coded this to react in the same ways that it does. + * + * The return value from this function is just a hint about where the + * handshaking failed. + * + */ +static int ppa_init(int host_no) { - int k; + int retv; + unsigned short ppb = PPA_BASE(host_no); - switch (ppa_hosts[host_no].mode) { - case PPA_NIBBLE: - case PPA_PS2: - w_dtr(host_no, d); - w_ctr(host_no, 0xe); - w_ctr(host_no, 0xc); - return 1; /* assume transfer went OK */ +#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE) + if (ppa_pb_claim(host_no)) + while (ppa_hosts[host_no].p_busy) + schedule(); /* Whe can safe schedule() here */ +#endif - case PPA_EPP_8: - case PPA_EPP_16: - case PPA_EPP_32: - w_ctr(host_no, 0x4); - w_epp(host_no, d); - k = ppa_check_epp_status(host_no); - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return k; + ppa_disconnect(host_no); + ppa_connect(host_no, CONNECT_NORMAL); - default: - printk("PPA: bug in ppa_outb()\n"); - } - return 0; -} + retv = 2; /* Failed */ -static inline int ppa_ins(int host_no, char *buffer, int len) -{ - int k, h, l; -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0 - int r; -#endif + w_ctr(ppb, 0xe); + if ((r_str(ppb) & 0x08) == 0x08) + retv--; - switch (ppa_hosts[host_no].mode) { - case PPA_NIBBLE: - /* 4 bit input, with a loop */ - for (k = len; k; k--) { - w_ctr(host_no, 0x4); - h = r_str(host_no); - w_ctr(host_no, 0x6); - l = r_str(host_no); - *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0); - } - w_ctr(host_no, 0xc); - return 1; /* assume transfer went OK */ + w_ctr(ppb, 0xc); + if ((r_str(ppb) & 0x08) == 0x00) + retv--; - case PPA_PS2: - /* 8 bit input, with a loop */ - for (k = len; k; k--) { - w_ctr(host_no, 0x25); - *buffer++ = r_dtr(host_no); - w_ctr(host_no, 0x27); - } - w_ctr(host_no, 0x5); - w_ctr(host_no, 0x4); - w_ctr(host_no, 0xc); - return 1; /* assume transfer went OK */ + /* This is a SCSI BUS reset signal */ + if (!retv) { + w_dtr(ppb, 0x40); + w_ctr(ppb, 0x08); + udelay(30); + w_ctr(ppb, 0x0c); + udelay(1000); /* Allow devices to settle down */ + } + ppa_disconnect(host_no); + udelay(1000); /* Another delay to allow devices to settle */ -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0 - case PPA_EPP_32: -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2 - w_ctr(host_no, 0x24); - for (k = len; k; k -= 4) { - *buffer++ = r_epp(host_no); - *buffer++ = r_epp(host_no); - *buffer++ = r_epp(host_no); - *buffer++ = r_epp(host_no); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0x2c); - ecp_sync(host_no); - return 1; -#endif - case PPA_EPP_16: -#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3 - w_ctr(host_no, 0x24); - for (k = len; k; k -= 2) { - *buffer++ = r_epp(host_no); - *buffer++ = r_epp(host_no); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0x2c); - ecp_sync(host_no); - return 1; -#endif - case PPA_EPP_8: - w_ctr(host_no, 0x24); - for (k = len; k; k--) { - *buffer++ = r_epp(host_no); - r = ppa_check_epp_status(host_no); - if (!r) - return r; - } - w_ctr(host_no, 0x2c); - ecp_sync(host_no); - return 1; - break; -#else - case PPA_EPP_8: - case PPA_EPP_16: - case PPA_EPP_32: - w_ctr(host_no, 0x24); - switch (ppa_hosts[host_no].mode) { - case PPA_EPP_8: - insb(PPA_BASE(host_no) + 0x04, - buffer, len); - break; - case PPA_EPP_16: - insw(PPA_BASE(host_no) + 0x04, - buffer, len / 2); - break; - case PPA_EPP_32: - insl(PPA_BASE(host_no) + 0x04, - buffer, len / 4); - break; - } - k = ppa_check_epp_status(host_no); - w_ctr(host_no, 0x2c); - ecp_sync(host_no); - return k; -#endif + if (!retv) + retv = device_check(host_no); - default: - printk("PPA: bug in ppa_ins()\n"); - } - return 0; + ppa_pb_release(host_no); + return retv; } -static int ppa_inb(int host_no, char *buffer) +static inline int ppa_send_command(Scsi_Cmnd * cmd) { - int h, l, k; - - switch (ppa_hosts[host_no].mode) { - case PPA_NIBBLE: - /* 4 bit input */ - w_ctr(host_no, 0x4); - h = r_str(host_no); - w_ctr(host_no, 0x6); - l = r_str(host_no); - *buffer = ((l >> 4) & 0x0f) + (h & 0xf0); - w_ctr(host_no, 0xc); - return 1; /* assume transfer went OK */ + int host_no = cmd->host->unique_id; + int k; - case PPA_PS2: - /* 8 bit input */ - w_ctr(host_no, 0x25); - *buffer++ = r_dtr(host_no); - w_ctr(host_no, 0x27); - w_ctr(host_no, 0x5); - w_ctr(host_no, 0x4); - w_ctr(host_no, 0xc); - return 1; /* assume transfer went OK */ + w_ctr(PPA_BASE(host_no), 0x0c); - case PPA_EPP_8: - case PPA_EPP_16: - case PPA_EPP_32: - w_ctr(host_no, 0x24); - *buffer = r_epp(host_no); - k = ppa_check_epp_status(host_no); - w_ctr(host_no, 0xc); - ecp_sync(host_no); - return k; - - default: - printk("PPA: bug in ppa_inb()\n"); - } - return 0; + for (k = 0; k < cmd->cmd_len; k++) + if (!ppa_out(host_no, &cmd->cmnd[k], 1)) + return 0; + return 1; } /* @@ -867,115 +845,107 @@ */ static int ppa_completion(Scsi_Cmnd * cmd) { - int host_no = cmd->host->unique_id; - - char r, l, h, v; - int dir, cnt, blen, fast, bulk, status; - char *buffer; - struct scatterlist *sl; - int current_segment, nsegment; - - v = cmd->cmnd[0]; - bulk = ((v == READ_6) || - (v == READ_10) || - (v == WRITE_6) || - (v == WRITE_10)); - - /* code for scatter/gather: */ - if (cmd->use_sg) { - /* if many buffers are available, start filling the first */ - sl = (struct scatterlist *) cmd->request_buffer; - blen = sl->length; - buffer = sl->address; - } else { - /* else fill the only available buffer */ - sl = NULL; - buffer = cmd->request_buffer; - blen = cmd->request_bufflen; - } - current_segment = 0; - nsegment = cmd->use_sg; - - cnt = 0; - - /* detect transfer direction */ - dir = 0; - if (!(r = ppa_wait(host_no))) - return 0; - if (r == (char) 0xc0) - dir = 1; /* d0 = read c0 = write f0 = status */ - - while (r != (char) 0xf0) { - if (((r & 0xc0) != 0xc0) || (cnt >= blen)) { - ppa_fail(host_no, DID_ERROR); - return 0; - } - /* determine if we should use burst I/O */ - fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) && - ((((long)buffer + cnt)) & 0x3) == 0); - - if (fast) { - if (dir) - status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE); - else - status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE); - cnt += PPA_BURST_SIZE; - } else { - if (dir) - status = ppa_outb(host_no, buffer[cnt]); - else - status = ppa_inb(host_no, &buffer[cnt]); - cnt++; - } + /* Return codes: + * -1 Error + * 0 Told to schedule + * 1 Finished data transfer + */ + int host_no = cmd->host->unique_id; + unsigned short ppb = PPA_BASE(host_no); + unsigned long start_jiffies = jiffies; + + unsigned char r, v; + int fast, bulk, status; + + v = cmd->cmnd[0]; + bulk = ((v == READ_6) || + (v == READ_10) || + (v == WRITE_6) || + (v == WRITE_10)); + + /* + * We only get here if the drive is ready to comunicate, + * hence no need for a full ppa_wait. + */ + r = (r_str(ppb) & 0xf0); - if (!status || !(r = ppa_wait(host_no))) - return 0; + while (r != (unsigned char) 0xf0) { + /* + * If we have been running for more than a full timer tick + * then take a rest. + */ + if (jiffies > start_jiffies + 1) + return 0; - if (sl && cnt == blen) { - /* if scatter/gather, advance to the next segment */ - if (++current_segment < nsegment) { - ++sl; - blen = sl->length; - buffer = sl->address; - cnt = 0; - } - /* - * the else case will be captured by the (cnt >= blen) - * test above. - */ - } + if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) { + ppa_fail(host_no, DID_ERROR); + return -1; /* ERROR_RETURN */ } + /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE)) + ? PPA_BURST_SIZE : 1; - /* read status and message bytes */ - if (!ppa_inb(host_no, &l)) /* read status byte */ - return 0; - if (!(ppa_wait(host_no))) - return 0; - if (!ppa_inb(host_no, &h)) /* read message byte */ - return 0; + if (r == (unsigned char) 0xc0) + status = ppa_out(host_no, cmd->SCp.ptr, fast); + else + status = ppa_in(host_no, cmd->SCp.ptr, fast); - ppa_disconnect(host_no); + cmd->SCp.ptr += fast; + cmd->SCp.this_residual -= fast; - ppa_hosts[host_no].error_code = DID_OK; - return (h << 8) | (l & STATUS_MASK); + if (!status) { + ppa_fail(host_no, DID_BUS_BUSY); + return -1; /* ERROR_RETURN */ + } + if (cmd->SCp.buffer && !cmd->SCp.this_residual) { + /* if scatter/gather, advance to the next segment */ + if (cmd->SCp.buffers_residual--) { + cmd->SCp.buffer++; + cmd->SCp.this_residual = cmd->SCp.buffer->length; + cmd->SCp.ptr = cmd->SCp.buffer->address; + } + } + /* Now check to see if the drive is ready to comunicate */ + r = (r_str(ppb) & 0xf0); + /* If not, drop back down to the scheduler and wait a timer tick */ + if (!(r & 0x80)) + return 0; + } + return 1; /* FINISH_RETURN */ } /* deprecated synchronous interface */ - int ppa_command(Scsi_Cmnd * cmd) { - int host_no = cmd->host->unique_id; - int s; + static int first_pass = 1; + int host_no = cmd->host->unique_id; - sti(); - s = 0; - if (ppa_start(cmd)) - if (ppa_wait(host_no)) - s = ppa_completion(cmd); - return s + (ppa_hosts[host_no].error_code << 16); + if (first_pass) { + printk("ppa: using non-queuing interface\n"); + first_pass = 0; + } + if (ppa_hosts[host_no].cur_cmd) { + printk("PPA: bug in ppa_command\n"); + return 0; + } + ppa_hosts[host_no].failed = 0; + ppa_hosts[host_no].jstart = jiffies; + ppa_hosts[host_no].cur_cmd = cmd; + cmd->result = DID_ERROR << 16; /* default return code */ + cmd->SCp.phase = 0; + + ppa_pb_claim(host_no); + + while (ppa_engine(&ppa_hosts[host_no], cmd)) + schedule(); + + if (cmd->SCp.phase) /* Only disconnect if we have connected */ + ppa_disconnect(cmd->host->unique_id); + + ppa_pb_release(host_no); + ppa_hosts[host_no].cur_cmd = 0; + return cmd->result; } -/* pseudo-interrupt queueing interface */ /* * Since the PPA itself doesn't generate interrupts, we use * the scheduler's task queue to generate a stream of call-backs and @@ -983,82 +953,198 @@ */ static void ppa_interrupt(void *data) { - ppa_struct *tmp = (ppa_struct *) data; - Scsi_Cmnd *cmd = tmp->cur_cmd; - void (*done) (Scsi_Cmnd *) = tmp->done; - int host_no = cmd->host->unique_id; - - if (!cmd) { - printk("PPA: bug in ppa_interrupt\n"); - return; - } - /* First check for any errors that may of occured - * Here we check for internal errors - */ - if (tmp->ppa_failed) { - printk("PPA: ppa_failed bug: ppa_error_code = %d\n", - tmp->error_code); - cmd->result = DID_ERROR << 16; - tmp->cur_cmd = 0; - done(cmd); - return; - } - /* Occasionally the mid level driver will abort a SCSI - * command because we are taking to long, if this occurs - * we should abort the command. - */ - if (tmp->abort_flag) { - ppa_disconnect(host_no); - if (tmp->abort_flag == 1) - cmd->result = DID_ABORT << 16; - else { - ppa_do_reset(host_no); - cmd->result = DID_RESET << 16; + ppa_struct *tmp = (ppa_struct *) data; + Scsi_Cmnd *cmd = tmp->cur_cmd; + + if (!cmd) { + printk("PPA: bug in ppa_interrupt\n"); + return; + } + if (ppa_engine(tmp, cmd)) { + tmp->ppa_tq.data = (void *) tmp; + tmp->ppa_tq.sync = 0; + queue_task(&tmp->ppa_tq, &tq_timer); + return; + } + /* Command must of completed hence it is safe to let go... */ +#if PPA_DEBUG > 0 + switch ((cmd->result >> 16) & 0xff) { + case DID_OK: + break; + case DID_NO_CONNECT: + printk("ppa: no device at SCSI ID %i\n", cmd->target); + break; + case DID_BUS_BUSY: + printk("ppa: BUS BUSY - EPP timeout detected\n"); + break; + case DID_TIME_OUT: + printk("ppa: unknown timeout\n"); + break; + case DID_ABORT: + printk("ppa: told to abort\n"); + break; + case DID_PARITY: + printk("ppa: parity error (???)\n"); + break; + case DID_ERROR: + printk("ppa: internal driver error\n"); + break; + case DID_RESET: + printk("ppa: told to reset device\n"); + break; + case DID_BAD_INTR: + printk("ppa: bad interrupt (???)\n"); + break; + default: + printk("ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff); + } +#endif + + ppa_disconnect(cmd->host->unique_id); + ppa_pb_release(cmd->host->unique_id); + tmp->cur_cmd = 0; + cmd->scsi_done(cmd); + return; +} + +static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd) +{ + int host_no = cmd->host->unique_id; + unsigned short ppb = PPA_BASE(host_no); + unsigned char l = 0, h = 0; + int retv; + + /* First check for any errors that may of occured + * Here we check for internal errors + */ + if (tmp->failed) + return 0; + + switch (cmd->SCp.phase) { + case 0: /* Phase 0 - Waiting for parport */ + if ((jiffies - tmp->jstart) > HZ) { + /* + * We waited more than a second + * for parport to call us + */ + ppa_fail(host_no, DID_BUS_BUSY); + return 0; + } + return 1; /* wait that ppa_wakeup claims parport */ + case 1: /* Phase 1 - Connected */ + { /* Perform a sanity check for cable unplugged */ + int retv = 2; /* Failed */ + + ppa_connect(host_no, CONNECT_EPP_MAYBE); + + w_ctr(ppb, 0xe); + if ((r_str(ppb) & 0x08) == 0x08) + retv--; + + w_ctr(ppb, 0xc); + if ((r_str(ppb) & 0x08) == 0x00) + retv--; + + if (retv) + if ((jiffies - tmp->jstart) > (1 * HZ)) { + printk("ppa: Parallel port cable is unplugged!!\n"); + ppa_fail(host_no, DID_BUS_BUSY); + return 0; + } else { + ppa_disconnect(host_no); + return 1; /* Try again in a jiffy */ } - tmp->cur_cmd = 0; - done(cmd); - return; + cmd->SCp.phase++; } - /* Check to see if the device is now free, if not - * then throw this function onto the scheduler queue - * to be called back in a jiffy. - * (i386: 1 jiffy = 0.01 seconds) - */ - if (!(r_str(host_no) & 0x80)) { - tmp->ppa_tq.data = (void *) tmp; - queue_task(&tmp->ppa_tq, &tq_scheduler); - return; + + case 2: /* Phase 2 - We are now talking to the scsi bus */ + if (!ppa_select(host_no, cmd->target)) { + ppa_fail(host_no, DID_NO_CONNECT); + return 0; } - /* Device is now free and no errors have occured so - * it is safe to do the data phase - */ - cmd->result = ppa_completion(cmd) + (tmp->error_code << 16); - tmp->cur_cmd = 0; - done(cmd); - return; + cmd->SCp.phase++; + + case 3: /* Phase 3 - Ready to accept a command */ + w_ctr(ppb, 0x0c); + if (!(r_str(ppb) & 0x80)) + return 1; + + if (!ppa_send_command(cmd)) + return 0; + cmd->SCp.phase++; + + case 4: /* Phase 4 - Setup scatter/gather buffers */ + if (cmd->use_sg) { + /* if many buffers are available, start filling the first */ + cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer; + cmd->SCp.this_residual = cmd->SCp.buffer->length; + cmd->SCp.ptr = cmd->SCp.buffer->address; + } else { + /* else fill the only available buffer */ + cmd->SCp.buffer = NULL; + cmd->SCp.this_residual = cmd->request_bufflen; + cmd->SCp.ptr = cmd->request_buffer; + } + cmd->SCp.buffers_residual = cmd->use_sg; + cmd->SCp.phase++; + + case 5: /* Phase 5 - Data transfer stage */ + w_ctr(ppb, 0x0c); + if (!(r_str(ppb) & 0x80)) + return 1; + + retv = ppa_completion(cmd); + if (retv == -1) + return 0; + if (retv == 0) + return 1; + cmd->SCp.phase++; + + case 6: /* Phase 6 - Read status/message */ + cmd->result = DID_OK << 16; + /* Check for data overrun */ + if (ppa_wait(host_no) != (unsigned char) 0xf0) { + ppa_fail(host_no, DID_ERROR); + return 0; + } + if (ppa_in(host_no, &l, 1)) { /* read status byte */ + /* Check for optional message byte */ + if (ppa_wait(host_no) == (unsigned char) 0xf0) + ppa_in(host_no, &h, 1); + cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK); + } + return 0; /* Finished */ + break; + + default: + printk("ppa: Invalid scsi phase\n"); + } + return 0; } int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { - int host_no = cmd->host->unique_id; - - if (ppa_hosts[host_no].cur_cmd) { - printk("PPA: bug in ppa_queuecommand\n"); - return 0; - } - sti(); - ppa_hosts[host_no].cur_cmd = cmd; - ppa_hosts[host_no].done = done; - - if (!ppa_start(cmd)) { - cmd->result = ppa_hosts[host_no].error_code << 16; - ppa_hosts[host_no].cur_cmd = 0; - done(cmd); - return 0; - } - ppa_interrupt(ppa_hosts + host_no); + int host_no = cmd->host->unique_id; + if (ppa_hosts[host_no].cur_cmd) { + printk("PPA: bug in ppa_queuecommand\n"); return 0; + } + ppa_hosts[host_no].failed = 0; + ppa_hosts[host_no].jstart = jiffies; + ppa_hosts[host_no].cur_cmd = cmd; + cmd->scsi_done = done; + cmd->result = DID_ERROR << 16; /* default return code */ + cmd->SCp.phase = 0; /* bus free */ + + ppa_pb_claim(host_no); + + ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no; + ppa_hosts[host_no].ppa_tq.sync = 0; + queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return 0; } /* @@ -1069,263 +1155,166 @@ */ int ppa_biosparam(Disk * disk, kdev_t dev, int ip[]) { - ip[0] = 0x40; - ip[1] = 0x20; + ip[0] = 0x40; + ip[1] = 0x20; + ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]); + if (ip[2] > 1024) { + ip[0] = 0xff; + ip[1] = 0x3f; ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]); - if (ip[2] > 1024) { - ip[0] = 0xff; - ip[1] = 0x3f; - ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]); - if (ip[2] > 1023) - ip[2] = 1023; - } - return 0; + if (ip[2] > 1023) + ip[2] = 1023; + } + return 0; } int ppa_abort(Scsi_Cmnd * cmd) { - int host_no = cmd->host->unique_id; - - ppa_hosts[host_no].abort_flag = 1; - ppa_hosts[host_no].error_code = DID_ABORT; - if (ppa_hosts[host_no].ppa_wait_q) - wake_up(&ppa_hosts[host_no].ppa_wait_q); - - return SCSI_ABORT_SNOOZE; + /* + * There is no method for aborting commands since Iomega + * have tied the SCSI_MESSAGE line high in the interface + */ + + switch (cmd->SCp.phase) { + case 0: /* Do not have access to parport */ + case 1: /* Have not connected to interface */ + cmd->result = DID_ABORT; + cmd->done(cmd); + return SCSI_ABORT_SUCCESS; + break; + default: /* SCSI command sent, can not abort */ + return SCSI_ABORT_BUSY; + break; + } } int ppa_reset(Scsi_Cmnd * cmd, unsigned int x) { - int host_no = cmd->host->unique_id; - - ppa_hosts[host_no].abort_flag = 2; - ppa_hosts[host_no].error_code = DID_RESET; - if (ppa_hosts[host_no].ppa_wait_q) - wake_up(&ppa_hosts[host_no].ppa_wait_q); - - return SCSI_RESET_PUNT; -} - - - -/*************************************************************************** - * Parallel port probing routines * - ***************************************************************************/ - + int host_no = cmd->host->unique_id; + int ppb = PPA_BASE(host_no); -#ifdef MODULE -Scsi_Host_Template driver_template = PPA; -#include "scsi_module.c" -#endif - -/* - * Start of Chipset kludges - */ - -int ppa_detect(Scsi_Host_Template * host) -{ - struct Scsi_Host *hreg; - int rs; - int ports; - int i, nhosts; - struct parport *pb = parport_enumerate(); - - printk("PPA driver version: %s\n", PPA_VERSION); - nhosts = 0; - - for (i = 0; pb; i++, pb=pb->next) { - int modes = pb->modes; - - /* We only understand PC-style ports */ - if (modes & PARPORT_MODE_PCSPP) { - - /* transfer global values here */ - if (ppa_speed >= 0) - ppa_hosts[i].speed = ppa_speed; - if (ppa_speed_fast >= 0) - ppa_hosts[i].speed_fast = ppa_speed_fast; - - ppa_hosts[i].dev = parport_register_device(pb, "ppa", - NULL, ppa_wakeup, NULL, - PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]); - - /* Claim the bus so it remembers what we do to the - * control registers. [ CTR and ECP ] - */ - ppa_pb_claim(i); - w_ctr(i, 0x0c); - - ppa_hosts[i].mode = PPA_NIBBLE; - if (modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP)) { - ppa_hosts[i].mode = PPA_EPP_32; - printk("PPA: Parport [ PCEPP ]\n"); - } else if (modes & PARPORT_MODE_PCECP) { - w_ecr(i, 0x20); - ppa_hosts[i].mode = PPA_PS2; - printk("PPA: Parport [ PCECP in PS2 submode ]\n"); - } else if (modes & PARPORT_MODE_PCPS2) { - ppa_hosts[i].mode = PPA_PS2; - printk("PPA: Parport [ PCPS2 ]\n"); - } - /* Done configuration */ - ppa_pb_release(i); - - rs = ppa_init(i); - if (rs) { - parport_unregister_device(ppa_hosts[i].dev); - continue; - } - /* now the glue ... */ - switch (ppa_hosts[i].mode) { - case PPA_NIBBLE: - case PPA_PS2: - ports = 3; - break; - case PPA_EPP_8: - case PPA_EPP_16: - case PPA_EPP_32: - ports = 8; - break; - default: /* Never gets here */ - continue; - } - - host->can_queue = PPA_CAN_QUEUE; - host->sg_tablesize = ppa_sg; - hreg = scsi_register(host, 0); - hreg->io_port = pb->base; - hreg->n_io_port = ports; - hreg->dma_channel = -1; - hreg->unique_id = i; - ppa_hosts[i].host = hreg->host_no; - nhosts++; - } + /* + * PHASE1: + * Bring the interface crashing down on whatever is running + * hopefully this will kill the request. + * Bring back up the interface, reset the drive (and anything + * attached for that manner) + */ + if (cmd) + if (cmd->SCp.phase) + ppa_disconnect(cmd->host->unique_id); + + ppa_connect(host_no, CONNECT_NORMAL); + w_dtr(ppb, 0x40); + w_ctr(ppb, 0x8); + udelay(30); + w_ctr(ppb, 0xc); + udelay(1000); /* delay for devices to settle down */ + ppa_disconnect(host_no); + udelay(1000); /* Additional delay to allow devices to settle down */ + + /* + * PHASE2: + * Sanity check for the sake of mid-level driver + */ + if (!cmd) { + printk("ppa bus reset called for invalid command.\n"); + return SCSI_RESET_NOT_RUNNING; + } + /* + * PHASE3: + * Flag the current command as having died due to reset + */ + ppa_connect(host_no, CONNECT_NORMAL); + ppa_fail(host_no, DID_RESET); + + /* Since the command was already on the timer queue ppa_interrupt + * will be called shortly. + */ + return SCSI_RESET_PENDING; +} + +static int device_check(int host_no) +{ + /* This routine looks for a device and then attempts to use EPP + to send a command. If all goes as planned then EPP is available. */ + + static char cmd[6] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + int loop, old_mode, status, k, ppb = PPA_BASE(host_no); + unsigned char l; + + old_mode = ppa_hosts[host_no].mode; + for (loop = 0; loop < 8; loop++) { + /* Attempt to use EPP for Test Unit Ready */ + if ((ppb & 0x0007) == 0x0000) + ppa_hosts[host_no].mode = PPA_EPP_32; + + second_pass: + ppa_connect(host_no, CONNECT_EPP_MAYBE); + /* Select SCSI device */ + if (!ppa_select(host_no, loop)) { + ppa_disconnect(host_no); + continue; + } + printk("ppa: Found device at ID %i, Attempting to use %s\n", loop, + PPA_MODE_STRING[ppa_hosts[host_no].mode]); + + /* Send SCSI command */ + status = 1; + w_ctr(ppb, 0x0c); + for (l = 0; (l < 6) && (status); l++) + status = ppa_out(host_no, cmd, 1); + + if (!status) { + ppa_disconnect(host_no); + ppa_connect(host_no, CONNECT_EPP_MAYBE); + w_dtr(ppb, 0x40); + w_ctr(ppb, 0x08); + udelay(30); + w_ctr(ppb, 0x0c); + udelay(1000); + ppa_disconnect(host_no); + udelay(1000); + if (ppa_hosts[host_no].mode == PPA_EPP_32) { + ppa_hosts[host_no].mode = old_mode; + goto second_pass; + } + printk("ppa: Unable to establish communication, aborting driver load.\n"); + return 1; + } + w_ctr(ppb, 0x0c); + k = 1000000; /* 1 Second */ + do { + l = r_str(ppb); + k--; + udelay(1); + } while (!(l & 0x80) && (k)); + + l &= 0xf0; + + if (l != 0xf0) { + ppa_disconnect(host_no); + ppa_connect(host_no, CONNECT_EPP_MAYBE); + w_dtr(ppb, 0x40); + w_ctr(ppb, 0x08); + udelay(30); + w_ctr(ppb, 0x0c); + udelay(1000); + ppa_disconnect(host_no); + udelay(1000); + if (ppa_hosts[host_no].mode == PPA_EPP_32) { + ppa_hosts[host_no].mode = old_mode; + goto second_pass; + } + printk("ppa: Unable to establish communication, aborting driver load.\n"); + return 1; } - if (nhosts == 0) - return 0; - else - return 1; /* return number of hosts detected */ -} - -/* This is to give the ppa driver a way to modify the timings (and other - * parameters) by writing to the /proc/scsi/ppa/0 file. - * Very simple method really... (To simple, no error checking :( ) - * Reason: Kernel hackers HATE having to unload and reload modules for - * testing... - * Also gives a method to use a script to obtain optimum timings (TODO) - */ - -static int ppa_strncmp(const char *a, const char *b, int len) -{ - int loop; - for (loop = 0; loop < len; loop++) - if (a[loop] != b[loop]) - return 1; - + ppa_disconnect(host_no); + printk("ppa: Communication established with ID %i using %s\n", loop, + PPA_MODE_STRING[ppa_hosts[host_no].mode]); return 0; + } + printk("ppa: No devices found, aborting driver load.\n"); + return 1; } - -static int ppa_proc_write(int hostno, char *buffer, int length) -{ - unsigned long x; - const char *inv_num = "ppa /proc entry passed invalid number\n"; - - if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) { - x = simple_strtoul(buffer + 15, NULL, 0); - if (x <= ppa_hosts[hostno].speed) - ppa_hosts[hostno].speed_fast = x; - else - printk(inv_num); - return length; - } - if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) { - x = simple_strtoul(buffer + 10, NULL, 0); - if (x >= ppa_hosts[hostno].speed_fast) - ppa_hosts[hostno].speed = x; - else - printk(inv_num); - return length; - } - if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) { - x = simple_strtoul(buffer + 10, NULL, 0); - ppa_hosts[hostno].epp_speed = x; - return length; - } - if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) { - x = simple_strtoul(buffer + 12, NULL, 0); - ppa_hosts[hostno].timeout = x; - return length; - } - if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) { - x = simple_strtoul(buffer + 5, NULL, 0); - ppa_hosts[hostno].mode = x; - return length; - } - printk("ppa /proc: invalid variable\n"); - return (-EINVAL); -} - -int ppa_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout) -{ - int i; - int size, len = 0; - off_t begin = 0; - off_t pos = 0; - - for (i = 0; i < 4; i++) - if (ppa_hosts[i].host == hostno) - break; - - if (inout) - return ppa_proc_write(i, buffer, length); - - size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION); - len += size; - pos = begin + len; - size = sprintf(buffer + len, "Parport : %s\n", - ppa_hosts[i].dev->port->name); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "Mode : %s\n", - PPA_MODE_STRING[ppa_hosts[i].mode]); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "\nTiming Parameters\n"); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "ppa_speed %i\n", - ppa_hosts[i].speed); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "ppa_speed_fast %i\n", - ppa_hosts[i].speed_fast); - len += size; - pos = begin + len; - - if (IN_EPP_MODE(ppa_hosts[i].mode)) { - size = sprintf(buffer + len, "epp_speed %i\n", - ppa_hosts[i].epp_speed); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "\nInternal Counters\n"); - len += size; - pos = begin + len; - - size = sprintf(buffer + len, "epp_timeout %i\n", - ppa_hosts[i].timeout); - len += size; - pos = begin + len; - } - *start = buffer + (offset + begin); - len -= (offset - begin); - if (len > length) - len = length; - return len; -} -/* end of ppa.c */ diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h --- v2.1.63/linux/drivers/scsi/ppa.h Mon Nov 3 13:04:26 1997 +++ linux/drivers/scsi/ppa.h Fri Nov 14 18:41:30 1997 @@ -2,30 +2,62 @@ * the Iomega ZIP drive * * (c) 1996 Grant R. Guenther grant@torque.net + * David Campbell campbell@torque.net + * + * All comments to David. */ #ifndef _PPA_H #define _PPA_H -#define PPA_VERSION "Curtin 1-08-BETA" - -/* This driver reqires a 1.3.37 kernel or higher!! */ +#define PPA_VERSION "1.39" /* Use the following to enable certain chipset support * Default is PEDANTIC = 3 */ - -#include - #ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC #define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3 #endif -#ifndef CONFIG_SCSI_PPA_EPP_TIME -#define CONFIG_SCSI_PPA_EPP_TIME 64 -#endif +/* + * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu) + * to support EPP and scatter-gather. [0.26-athena] + * + * additional hacks by David Campbell + * in response to this driver "mis-behaving" on his machine. + * Fixed EPP to handle "software" changing of EPP port data direction. + * Chased down EPP timeouts + * Made this driver "kernel version friendly" [0.28-athena] + * + * [ Stuff removed ] + * + * Compiled against 2.1.53. + * Rebuilt ppa_abort() function, should handle unplugged cable. + * [1.35s] + * + * PPA now auto probes for EPP on base address which are aligned on + * 8 byte boundaries (0x278 & 0x378) using the attached devices. + * This hopefully avoids the nasty problem of trying to detect EPP. + * Tested on 2.1.53 [1.36] + * + * The id_probe utility no longer performs read/write tests. + * Additional code included for checking the Intel ECP bug + * (Bit 0 of STR stuck low which fools the EPP detection routine) + * [1.37] + * + * Oops! Got the bit sign mixed up for the Intel bug check. + * Found that an additional delay is required during SCSI resets + * to allow devices to settle down. + * [1.38] + * + * Fixed all problems in the parport sharing scheme. Now ppa can be safe + * used with lp or other parport devices on the same parallel port. + * 1997 by Andrea Arcangeli + * [1.39] + */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ +#ifdef PPA_CODE #include #include #include @@ -35,14 +67,76 @@ #include #include #include +#include +#include #include #include "sd.h" #include "hosts.h" -#include /* batteries not included :-) */ -#define PPA_INITIATOR 7 +/* + * modes in which the driver can operate + */ +#define PPA_AUTODETECT 0 /* Autodetect mode */ +#define PPA_NIBBLE 1 /* work in standard 4 bit mode */ +#define PPA_PS2 2 /* PS/2 byte mode */ +#define PPA_EPP_8 3 /* EPP mode, 8 bit */ +#define PPA_EPP_16 4 /* EPP mode, 16 bit */ +#define PPA_EPP_32 5 /* EPP mode, 32 bit */ +#define PPA_UNKNOWN 6 /* Just in case... */ + +static char *PPA_MODE_STRING[] = +{ + "Autodetect", + "SPP", + "PS/2", + "EPP 8 bit", + "EPP 16 bit", + "EPP 32 bit", + "Unknown"}; + +/* This is a global option */ +int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */ + +/* other options */ +#define PPA_CAN_QUEUE 0 /* use "queueing" interface */ +#define PPA_BURST_SIZE 512 /* data burst size */ +#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */ +#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */ +#define PPA_DEBUG 0 /* debuging option */ +#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32) + +/* args to ppa_connect */ +#define CONNECT_EPP_MAYBE 1 +#define CONNECT_NORMAL 0 + +#define r_dtr(x) (unsigned char)inb((x)) +#define r_str(x) (unsigned char)inb((x)+1) +#define r_ctr(x) (unsigned char)inb((x)+2) +#define r_epp(x) (unsigned char)inb((x)+4) +#define r_fifo(x) (unsigned char)inb((x)+0x400) +#define r_ecr(x) (unsigned char)inb((x)+0x402) + +#define w_dtr(x,y) outb(y, (x)) +#define w_str(x,y) outb(y, (x)+1) +#define w_ctr(x,y) outb(y, (x)+2) +#define w_epp(x,y) outb(y, (x)+4) +#define w_fifo(x,y) outb(y, (x)+0x400) +#define w_ecr(x,y) outb(y, (x)+0x402) + +static int ppa_engine(ppa_struct *, Scsi_Cmnd *); +static int ppa_in(int, char *, int); +static int ppa_init(int); +static void ppa_interrupt(void *); +static int ppa_out(int, char *, int); + +struct proc_dir_entry proc_scsi_ppa = +{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2}; +#else +extern struct proc_dir_entry proc_scsi_ppa; +#define ppa_release 0 +#endif int ppa_detect(Scsi_Host_Template *); const char *ppa_info(struct Scsi_Host *); @@ -51,25 +145,13 @@ int ppa_abort(Scsi_Cmnd *); int ppa_reset(Scsi_Cmnd *, unsigned int); int ppa_proc_info(char *, char **, off_t, int, int, int); -int ppa_biosparam(Disk *, kdev_t, int*); -int ppa_release(struct Scsi_Host *); - -#ifndef MODULE -#ifdef PPA_CODE -#define SKIP_PROC_DIR -#endif -#endif - -#ifndef SKIP_PROC_DIR -struct proc_dir_entry proc_scsi_ppa = -{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2}; -#endif /* !PPA_CODE => hosts.c */ +int ppa_biosparam(Disk *, kdev_t, int *); #define PPA { /* next */ 0, \ /* usage_count */ 0, \ /* proc_dir */ &proc_scsi_ppa, \ /* proc_info */ ppa_proc_info, \ - /* name */ "Iomega ZIP/JAZ Traveller", \ + /* name */ "Iomega parport ZIP drive", \ /* detect */ ppa_detect, \ /* release */ ppa_release, \ /* info */ 0, \ @@ -80,7 +162,7 @@ /* slave_attach */ 0, \ /* bios_param */ ppa_biosparam, \ /* can_queue */ 0, \ - /* this_id */ PPA_INITIATOR, \ + /* this_id */ -1, \ /* sg_tablesize */ SG_ALL, \ /* cmd_per_lun */ 1, \ /* present */ 0, \ diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/psi_chip.h linux/drivers/scsi/psi_chip.h --- v2.1.63/linux/drivers/scsi/psi_chip.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/psi_chip.h Wed Nov 12 13:36:53 1997 @@ -192,3 +192,4 @@ } SETUP, *PSETUP; #endif + diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v2.1.63/linux/drivers/scsi/seagate.c Tue May 13 22:41:13 1997 +++ linux/drivers/scsi/seagate.c Fri Nov 14 10:36:16 1997 @@ -72,6 +72,13 @@ * x is some number, It will let you specify a default * transfer rate if handshaking isn't working correctly. * + * -DOLDCNTDATASCEME There is a new sceme to set the CONTROL + * and DATA reigsters which complies more closely + * with the SCSI2 standard. This hopefully eliminates + * the need to swap the order these registers are + * 'messed' with. It makes the following two options + * obsolete. To reenable the old sceme define this. + * * The following to options are patches from the SCSI.HOWTO * * -DSWAPSTAT This will swap the definitions for STAT_MSG and STAT_CD. @@ -794,6 +801,10 @@ unsigned char message = 0; register unsigned char status_read; +#ifndef OLDCNTDATASCEME + volatile unsigned char tmp_data; + volatile unsigned char tmp_control; +#endif unsigned transfersize = 0, underflow = 0; incommand = 0; @@ -1029,6 +1040,7 @@ * try this with a SCSI protocol or logic analyzer to see what is * going on. */ +#ifdef OLDCNTDATASCEME #ifdef SWAPCNTDATA cli(); WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | @@ -1044,6 +1056,16 @@ (reselect ? CMD_ATTN : 0)); sti (); #endif +#else + tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE +? 0x80 : 0x40)); + tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | + (reselect ? CMD_ATTN : 0) | CMD_BSY; + WRITE_CONTROL(tmp_data); + WRITE_DATA(tmp_control); + tmp_control ^= CMD_BSY; + WRITE_CONTROL(tmp_control); +#endif /* OLDCNTDATASCEME */ while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock) && !st0x_aborted) #if 0 && (DEBUG & PHASE_SELECTION) diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/st.c linux/drivers/scsi/st.c --- v2.1.63/linux/drivers/scsi/st.c Sat Oct 25 02:44:17 1997 +++ linux/drivers/scsi/st.c Thu Nov 13 07:23:32 1997 @@ -11,7 +11,7 @@ Copyright 1992 - 1997 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Tue May 27 22:29:00 1997 by makisara@home + Last modified: Wed Nov 5 23:39:52 1997 by makisara@home Some small formal changes - aeb, 950809 */ @@ -230,18 +230,9 @@ } else (STp->buffer)->last_result = SCpnt->result; -#if 0 - if ((STp->buffer)->writing) { - /* Process errors before releasing request */ - (STp->buffer)->last_result_fatal = st_chk_result(SCpnt); - SCpnt->request.rq_status = RQ_INACTIVE; - } - else - SCpnt->request.rq_status = RQ_SCSI_DONE; -#else + SCpnt->request.rq_status = RQ_SCSI_DONE; (STp->buffer)->last_SCpnt = SCpnt; -#endif #if DEBUG STp->write_pending = 0; @@ -645,8 +636,10 @@ } if ((STp->buffer)->last_result_fatal != 0) { - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) { + if ((STp->device)->scsi_level >= SCSI_2 && + (SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY && + SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */ STp->ready = ST_NO_TAPE; } else STp->ready = ST_NOT_READY; @@ -872,8 +865,6 @@ if (!SCpnt) goto out; - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - if ((STp->buffer)->last_result_fatal != 0 && ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || (SCpnt->sense_buffer[2] & 0x4f) != 0x40 || @@ -882,11 +873,13 @@ SCpnt->sense_buffer[5] | SCpnt->sense_buffer[6]) == 0))) { /* Filter out successful write at EOM */ + SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ printk(KERN_ERR "st%d: Error on write filemark.\n", dev); if (result == 0) result = (-EIO); } else { + SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ if (STps->drv_file >= 0) STps->drv_file++ ; STps->drv_block = 0; @@ -977,8 +970,12 @@ } STp = &(scsi_tapes[dev]); - if (STp->ready != ST_READY) - return (-EIO); + if (STp->ready != ST_READY) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } STm = &(STp->modes[STp->current_mode]); if (!STm->defined) return (-ENXIO); @@ -1349,7 +1346,7 @@ } else { SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - SCpnt = NULL; + SCpnt = *aSCpnt = NULL; if (transfer == blks) { /* We did not get anything, error */ printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); if (STps->drv_block >= 0) @@ -1465,8 +1462,12 @@ } STp = &(scsi_tapes[dev]); - if (STp->ready != ST_READY) - return (-EIO); + if (STp->ready != ST_READY) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } STm = &(STp->modes[STp->current_mode]); if (!STm->defined) return (-ENXIO); @@ -1544,7 +1545,7 @@ if ((STp->buffer)->buffer_bytes > 0) { #if DEBUG if (debugging && STps->eof != ST_NOEOF) - printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev, + printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev, STps->eof, (STp->buffer)->buffer_bytes, count - total); #endif transfer = (STp->buffer)->buffer_bytes < count - total ? @@ -1886,8 +1887,12 @@ int dev = TAPE_NR(inode->i_rdev); STp = &(scsi_tapes[dev]); - if (STp->ready != ST_READY && cmd_in != MTLOAD) - return (-EIO); + if (STp->ready != ST_READY && cmd_in != MTLOAD) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } timeout = STp->long_timeout; STps = &(STp->ps[STp->partition]); fileno = STps->drv_file; @@ -2523,6 +2528,7 @@ if (!SCpnt) return (-EBUSY); + SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ STps->drv_block = STps->drv_file = (-1); STps->eof = ST_NOEOF; if ((STp->buffer)->last_result_fatal != 0) { @@ -2548,7 +2554,6 @@ STps->drv_block = STps->drv_file = 0; result = 0; } - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ return result; } diff -u --recursive --new-file v2.1.63/linux/drivers/scsi/st.h linux/drivers/scsi/st.h --- v2.1.63/linux/drivers/scsi/st.h Wed May 28 10:51:32 1997 +++ linux/drivers/scsi/st.h Thu Nov 13 07:23:32 1997 @@ -64,7 +64,6 @@ unsigned capacity; struct wait_queue * waiting; Scsi_Device* device; - Scsi_Cmnd SCpnt; struct semaphore sem; ST_buffer * buffer; diff -u --recursive --new-file v2.1.63/linux/drivers/sound/Config.in linux/drivers/sound/Config.in --- v2.1.63/linux/drivers/sound/Config.in Wed Nov 12 13:34:26 1997 +++ linux/drivers/sound/Config.in Fri Nov 14 18:40:32 1997 @@ -1,15 +1,311 @@ -# -# Sound driver configuration -# -#-------- -# There is another confic script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +bool 'ProAudioSpectrum 16 support' CONFIG_PAS +bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB +bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB +bool 'Gravis Ultrasound support' CONFIG_GUS +bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 +bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS +bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 +bool 'GUS MAX support' CONFIG_GUSMAX +bool 'Microsoft Sound System support' CONFIG_MSS +bool 'Ensoniq SoundScape support' CONFIG_SSCAPE +bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX +bool 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_MAD16 +bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 +bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI +bool 'Yamaha OPL3-SA1 audio controller' CONFIG_OPL3SA1 +bool 'SoftOSS software wave table engine' CONFIG_SOFTOSS +bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 + +if [ "$CONFIG_AEDSP16" = "y" ]; then +hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330 +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'Enter -1 to the following question if you have something else such as SB16/32.' +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 +fi + + +if [ "$CONFIG_MAUI" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with Maui.' +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 +fi + + +if [ "$CONFIG_PSS" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with PSS cards.' +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS I/O base 220 or 240' PSS_BASE 220 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 +fi +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 +fi + + +if [ "$CONFIG_TRIX" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with OPL3-SA1.' +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 audio DMA 0, 1 or 3' TRIX_DMA 0 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'OPL3-SA1 SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'OPL3-SA1 SB DMA 1 or 3' TRIX_SB_DMA 1 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' OPL3SA1_BASE 530 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' OPL3SA1_IRQ 11 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +int 'OPL3-SA1 audio DMA 0, 1 or 3' OPL3SA1_DMA 0 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' OPL3SA1_DMA2 3 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' OPL3SA1_MPU_BASE 330 +fi + +if [ "$CONFIG_OPL3SA1" = "y" ]; then +int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' OPL3SA1_MPU_IRQ 9 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 +fi + +if [ "$CONFIG_SOFTOSS" = "y" ]; then +int 'Sampling rate for SoftOSS 8000 to 48000' SOFTOSS_RATE 22050 +fi + +if [ "$CONFIG_SOFTOSS" = "y" ]; then +int 'Max # of concurrent voices for SoftOSS 4 to 32' SOFTOSS_VOICES 32 +fi +# +$MAKE -C drivers/sound kernelconfig || exit 1 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff -u --recursive --new-file v2.1.63/linux/drivers/sound/lowlevel/awe_config.h linux/drivers/sound/lowlevel/awe_config.h --- v2.1.63/linux/drivers/sound/lowlevel/awe_config.h Wed Nov 12 13:34:27 1997 +++ linux/drivers/sound/lowlevel/awe_config.h Fri Nov 14 10:53:27 1997 @@ -27,6 +27,7 @@ /*---------------------------------------------------------------- * system configuration *----------------------------------------------------------------*/ +#define AWE_NEW_KERNEL_INTERFACE /* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD), * define the following line. diff -u --recursive --new-file v2.1.63/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c --- v2.1.63/linux/fs/ext2/fsync.c Tue Sep 23 16:48:48 1997 +++ linux/fs/ext2/fsync.c Wed Nov 12 13:03:27 1997 @@ -57,72 +57,6 @@ return 0; } -#ifndef __LITTLE_ENDIAN -static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!le32_to_cpu(*block)) - return 0; - bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} -#else -#define sync_block_swab32 sync_block -#endif - - -static int sync_iblock (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; - tmp = le32_to_cpu(*iblock); - if (!tmp) - return 0; - rc = sync_block_swab32 (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} -#else -#define sync_iblock_swab32 sync_iblock -#endif static int sync_direct (struct inode * inode, int wait) { @@ -137,122 +71,15 @@ return err; } -static int sync_indirect (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} -#else -#define sync_indirect_swab32 sync_indirect -#endif - -static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} -#else -#define sync_dindirect_swab32 sync_dindirect -#endif - -static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) -{ - int i; - struct buffer_head * tind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, tiblock, &tind_bh, wait); - if (rc || !tind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_dindirect_swab32 (inode, - ((u32 *) tind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (tind_bh); - return err; -} - /* * File may be NULL when we are called. Perhaps we shouldn't * even pass file to fsync ? + * + * This currently falls back to synching the whole device when + * the file is larger than can fit directly in the inode. This + * is because dirty-buffer handling is indexed by the device + * of the buffer, which makes it much faster to sync the whole + * device than to sync just one large file. */ int ext2_sync_file(struct file * file, struct dentry *dentry) @@ -269,18 +96,14 @@ */ goto skip; + if (inode->i_size > EXT2_NDIR_BLOCKS*blocksize) { + err = fsync_dev(inode->i_dev); + goto skip; + } + for (wait=0; wait<=1; wait++) { err |= sync_direct (inode, wait); - err |= sync_indirect (inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, - wait); - err |= sync_dindirect (inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect (inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, - wait); } skip: err |= ext2_sync_inode (inode); diff -u --recursive --new-file v2.1.63/linux/fs/isofs/symlink.c linux/fs/isofs/symlink.c --- v2.1.63/linux/fs/isofs/symlink.c Mon Aug 18 18:19:46 1997 +++ linux/fs/isofs/symlink.c Wed Nov 12 21:36:03 1997 @@ -56,7 +56,7 @@ if (!pnt) return 0; - i = strlen(pnt)+1; + i = strlen(pnt); if (i > buflen) i = buflen; if (copy_to_user(buffer, pnt, i)) diff -u --recursive --new-file v2.1.63/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c --- v2.1.63/linux/fs/ncpfs/dir.c Wed Nov 12 13:34:27 1997 +++ linux/fs/ncpfs/dir.c Thu Nov 13 13:14:50 1997 @@ -118,22 +118,23 @@ ncp_delete_dentry /* d_delete(struct dentry *) */ }; + +/* + * XXX: It would be better to use the tolower from linux/ctype.h, + * but _ctype is needed and it is not exported. + */ +#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c)) + + static int ncp_hash_dentry(struct dentry *dentry, struct qstr *this) { - char al[NCP_MAXPATHLEN]; unsigned long hash; int i; - memcpy(al,this->name,this->len); - al[this->len] = 0; - - if (!ncp_preserve_case(dentry->d_inode)) - str_lower(al); - hash = init_name_hash(); for (i=0; ilen ; i++) - hash = partial_name_hash(al[i],hash); + hash = partial_name_hash(tolower(this->name[i]),hash); this->hash = end_name_hash(hash); return 0; @@ -142,21 +143,18 @@ static int ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) { - char al[NCP_MAXPATHLEN],bl[NCP_MAXPATHLEN]; + int i; if (a->len != b->len) return 1; - if (ncp_preserve_case(dentry->d_inode)) + if (ncp_case_sensitive(dentry->d_inode)) return strncmp(a->name, b->name, a->len); - memcpy (al,a->name,a->len); - memcpy (bl,b->name,b->len); - al[a->len] = bl[b->len] = 0; - - str_lower(al); - str_lower(bl); - - return strcmp(al,bl); + for (i=0; ilen; i++) + if (tolower(a->name[i]) != tolower(b->name[i])) + return 1; + + return 0; } /* @@ -223,10 +221,16 @@ static ino_t find_inode_number(struct dentry *dir, struct qstr *name) { + unsigned long hash; + int i; struct dentry * dentry; ino_t ino = 0; - - name->hash = full_name_hash(name->name, name->len); + + hash = init_name_hash(); + for (i=0; ilen ; i++) + hash = partial_name_hash(tolower(name->name[i]),hash); + name->hash = end_name_hash(hash); + dentry = d_lookup(dir, name); if (dentry) { @@ -278,8 +282,11 @@ struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; - printk("ncp_lookup_validate called\n"); - + if (!dentry->d_inode) { + DPRINTK(KERN_DEBUG "ncp_lookup_validate: called with dentry->d_inode already NULL.\n"); + return 0; + } + if (!dir || !S_ISDIR(dir->i_mode)) { printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n"); goto finished; diff -u --recursive --new-file v2.1.63/linux/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c --- v2.1.63/linux/fs/ncpfs/mmap.c Wed Nov 12 13:34:27 1997 +++ linux/fs/ncpfs/mmap.c Thu Nov 13 13:14:50 1997 @@ -40,7 +40,7 @@ unsigned long tmp; int bufsize; int pos; - mm_segment_t fs; + unsigned long fs; page = __get_free_page(GFP_KERNEL); if (!page) diff -u --recursive --new-file v2.1.63/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c --- v2.1.63/linux/fs/ncpfs/sock.c Wed Nov 12 13:34:27 1997 +++ linux/fs/ncpfs/sock.c Thu Nov 13 13:14:50 1997 @@ -82,7 +82,7 @@ struct file *file; struct inode *inode; struct socket *sock; - mm_segment_t fs; + unsigned long fs; int result; char *start = server->packet; poll_table wait_table; diff -u --recursive --new-file v2.1.63/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.63/linux/fs/nfs/dir.c Wed Nov 12 13:34:27 1997 +++ linux/fs/nfs/dir.c Thu Nov 13 09:28:50 1997 @@ -31,10 +31,6 @@ #define NFS_MAX_AGE 10*HZ /* max age for dentry validation */ -#ifndef shrink_dcache_parent -#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb) -#endif - /* needed by smbfs as well ... move to dcache? */ extern void nfs_renew_times(struct dentry *); @@ -59,7 +55,7 @@ static int nfs_safe_remove(struct dentry *); -static int nfs_dir_open(struct inode * inode, struct file * file); +static int nfs_dir_open(struct inode *, struct file *); static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *); static int nfs_readdir(struct file *, void *, filldir_t); static int nfs_lookup(struct inode *, struct dentry *); @@ -435,6 +431,21 @@ if (age > NFS_MAX_AGE) d_drop(dentry); } + +#ifdef NFS_PARANOIA + /* + * Sanity check: if the dentry has been unhashed and the + * inode still has users, we could have problems ... + */ + if (list_empty(&dentry->d_hash) && dentry->d_inode) { + struct inode *inode = dentry->d_inode; + if (inode->i_count > 1) { +printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +inode->i_ino, inode->i_count, inode->i_nlink); + } + } +#endif } static struct dentry_operations nfs_dentry_operations = { @@ -785,7 +796,7 @@ { struct inode *dir = dentry->d_parent->d_inode; struct inode *inode = dentry->d_inode; - int error; + int error, rehash = 0; error = -EBUSY; if (inode) { @@ -813,8 +824,22 @@ #endif goto out; } + /* + * Unhash the dentry while we remove the file ... + */ + if (!list_empty(&dentry->d_hash)) { + d_drop(dentry); + rehash = 1; + } error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + /* + * ... then restore the hashed state. This ensures that the + * dentry can't become busy after having its file deleted. + */ + if (rehash) { + d_add(dentry, inode); + } #ifdef NFS_PARANOIA if (dentry->d_count > 1) printk("nfs_safe_remove: %s/%s busy after delete?? d_count=%d\n", @@ -827,6 +852,7 @@ nfs_invalidate_dircache(dir); if (inode && inode->i_nlink) inode->i_nlink --; + d_delete(dentry); } out: return error; @@ -858,7 +884,6 @@ error = nfs_safe_remove(dentry); if (!error) { nfs_renew_times(dentry); - d_delete(dentry); } } out: @@ -966,8 +991,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct inode *inode = old_dentry->d_inode; - int update = 1, error; + struct inode *old_inode = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; + int error, rehash = 0, update = 1; #ifdef NFS_DEBUG_VERBOSE printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n", @@ -988,6 +1014,24 @@ if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN) goto out; + + /* + * First check whether the target is busy ... we can't + * safely do _any_ rename if the target is in use. + */ + if (new_dentry->d_count > 1) { + if (new_inode && S_ISDIR(new_inode->i_mode)) + shrink_dcache_parent(new_dentry); + } + error = -EBUSY; + if (new_dentry->d_count > 1) { +#ifdef NFS_PARANOIA +printk("nfs_rename: target %s/%s busy, d_count=%d\n", +new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); +#endif + goto out; + } + /* * Check for within-directory rename ... no complications. */ @@ -996,15 +1040,14 @@ /* * Cross-directory move ... check whether it's a file. */ - error = -EBUSY; - if (S_ISREG(inode->i_mode)) { - if (NFS_WRITEBACK(inode)) { + if (S_ISREG(old_inode->i_mode)) { + if (NFS_WRITEBACK(old_inode)) { #ifdef NFS_PARANOIA printk("nfs_rename: %s/%s has pending writes\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name); #endif - nfs_flush_dirty_pages(inode, 0, 0, 0); - if (NFS_WRITEBACK(inode)) { + nfs_flush_dirty_pages(old_inode, 0, 0, 0); + if (NFS_WRITEBACK(old_inode)) { #ifdef NFS_PARANOIA printk("nfs_rename: %s/%s has pending writes after flush\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name); @@ -1030,7 +1073,6 @@ #endif goto out; } - if (new_dentry->d_count > 1) { #ifdef NFS_PARANOIA printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n", @@ -1040,13 +1082,28 @@ } d_drop(old_dentry); - d_drop(new_dentry); update = 0; do_rename: + /* + * We must prevent any new references to the target while + * the rename is in progress, so we unhash the dentry. + */ + if (!list_empty(&new_dentry->d_hash)) { + d_drop(new_dentry); + rehash = 1; + } error = nfs_proc_rename(NFS_SERVER(old_dir), NFS_FH(old_dir), old_dentry->d_name.name, NFS_FH(new_dir), new_dentry->d_name.name); + if (rehash) { + d_add(new_dentry, new_inode); + } +#ifdef NFS_PARANOIA +if (new_dentry->d_count > 1) +printk("nfs_rename: %s/%s busy after rename, d_count=%d\n", +new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); +#endif if (!error) { nfs_invalidate_dircache(new_dir); nfs_invalidate_dircache(old_dir); diff -u --recursive --new-file v2.1.63/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.63/linux/fs/nfs/inode.c Sat Nov 1 11:04:27 1997 +++ linux/fs/nfs/inode.c Thu Nov 13 09:28:50 1997 @@ -629,13 +629,12 @@ * Big trouble! The inode has become a different object. */ #ifdef NFS_PARANOIA -printk("nfs_refresh_inode: mode changed, %07o to %07o\n", -inode->i_mode, fattr->mode); +printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n", +inode->i_ino, inode->i_mode, fattr->mode); #endif fattr->mode = inode->i_mode; /* save mode */ make_bad_inode(inode); inode->i_mode = fattr->mode; /* restore mode */ - inode->i_nlink = 0; /* * No need to worry about unhashing the dentry, as the * lookup validation will know that the inode is bad. diff -u --recursive --new-file v2.1.63/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h --- v2.1.63/linux/include/asm-i386/bugs.h Wed Nov 12 13:34:27 1997 +++ linux/include/asm-i386/bugs.h Fri Nov 14 18:54:26 1997 @@ -142,7 +142,7 @@ : "edx" (inp) : "eax", "ecx", "edx", "edi" ); /* If this fails, it means that any user program may lock CPU hard. Too bad. */ - if (res != 12345678) printk( "Bad.\n" ); + if (res != 12345678) printk( "Buggy.\n" ); else printk( "Ok.\n" ); #endif } @@ -172,23 +172,19 @@ */ extern int pentium_f00f_bug; +extern void trap_init_f00f_bug(void); + __initfunc(static void check_pentium_f00f(void)) { /* * Pentium and Pentium MMX */ - printk("checking for F00F bug ..."); - if(x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) - { - extern void trap_init_f00f_bug(void); - - printk(KERN_INFO "\nIntel Pentium/[MMX] F0 0F bug detected - turning on workaround.\n"); + pentium_f00f_bug = 0; + if (x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) { + printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n"); pentium_f00f_bug = 1; trap_init_f00f_bug(); - } else { - printk(KERN_INFO " no F0 0F bug in this CPU, great!\n"); - pentium_f00f_bug = 0; } } diff -u --recursive --new-file v2.1.63/linux/include/asm-i386/system.h linux/include/asm-i386/system.h --- v2.1.63/linux/include/asm-i386/system.h Tue May 13 22:41:17 1997 +++ linux/include/asm-i386/system.h Fri Nov 14 18:40:12 1997 @@ -255,13 +255,13 @@ :"ax","dx") #define set_intr_gate(n,addr) \ - _set_gate(&idt[n],14,0,addr) + _set_gate(idt+(n),14,0,addr) #define set_trap_gate(n,addr) \ - _set_gate(&idt[n],15,0,addr) + _set_gate(idt+(n),15,0,addr) #define set_system_gate(n,addr) \ - _set_gate(&idt[n],15,3,addr) + _set_gate(idt+(n),15,3,addr) #define set_call_gate(a,addr) \ _set_gate(a,12,3,addr) diff -u --recursive --new-file v2.1.63/linux/include/linux/head.h linux/include/linux/head.h --- v2.1.63/linux/include/linux/head.h Sun Jan 22 04:38:45 1995 +++ linux/include/linux/head.h Fri Nov 14 18:39:55 1997 @@ -1,11 +1,20 @@ #ifndef _LINUX_HEAD_H #define _LINUX_HEAD_H -typedef struct desc_struct { +struct desc_struct { unsigned long a,b; -} desc_table[256]; +}; -extern desc_table idt,gdt; +extern struct desc_struct idt_table[],gdt_table[]; +extern struct desc_struct *idt, *gdt; + +struct Xgt_desc_struct { + unsigned short size; + unsigned long address __attribute__((packed)); +}; + +#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2)) +#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2)) #define GDT_NUL 0 #define GDT_CODE 1 diff -u --recursive --new-file v2.1.63/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h --- v2.1.63/linux/include/linux/miscdevice.h Wed Nov 12 13:34:28 1997 +++ linux/include/linux/miscdevice.h Thu Nov 13 06:00:43 1997 @@ -9,10 +9,10 @@ #define ATARIMOUSE_MINOR 5 #define SUN_MOUSE_MINOR 6 #define PC110PAD_MINOR 9 -#define RADIO_MINOR 129 #define RTC_MINOR 135 #define SUN_OPENPROM_MINOR 139 #define NVRAM_MINOR 144 +#define RADIO_MINOR 152 #define MISC_DYNAMIC_MINOR 255 extern int misc_init(void); diff -u --recursive --new-file v2.1.63/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h --- v2.1.63/linux/include/linux/ncp_fs.h Wed Nov 12 13:34:28 1997 +++ linux/include/linux/ncp_fs.h Fri Nov 14 18:42:23 1997 @@ -202,12 +202,13 @@ static inline int ncp_preserve_case(struct inode *i) { - /* If we can get case-sensitive server lookups working, then - * - * return (ncp_namespace(i) == NW_NS_OS2); - */ - return 0; + return (ncp_namespace(i) == NW_NS_OS2); } + +static inline int ncp_case_sensitive(struct inode *i) +{ + return 0; +} #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.1.63/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.1.63/linux/include/linux/pci.h Sat Oct 25 02:44:18 1997 +++ linux/include/linux/pci.h Fri Nov 14 11:03:41 1997 @@ -404,6 +404,7 @@ #define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 #define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_DEVICE_ID_PROMISE_IDE_UDMA 0x4d33 #define PCI_DEVICE_ID_PROMISE_5300 0x5300 #define PCI_VENDOR_ID_N9 0x105d diff -u --recursive --new-file v2.1.63/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.63/linux/kernel/ksyms.c Wed Nov 12 13:34:28 1997 +++ linux/kernel/ksyms.c Thu Nov 13 06:15:42 1997 @@ -198,6 +198,7 @@ EXPORT_SYMBOL(get_cached_page); EXPORT_SYMBOL(put_cached_page); EXPORT_SYMBOL(shrink_dcache_sb); +EXPORT_SYMBOL(shrink_dcache_parent); #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) EXPORT_SYMBOL(do_nfsservctl); diff -u --recursive --new-file v2.1.63/linux/net/Config.in linux/net/Config.in --- v2.1.63/linux/net/Config.in Tue Sep 23 16:48:50 1997 +++ linux/net/Config.in Thu Nov 13 12:58:50 1997 @@ -26,9 +26,6 @@ tristate 'The IPX protocol' CONFIG_IPX if [ "$CONFIG_IPX" != "n" ]; then bool 'Full internal IPX network' CONFIG_IPX_INTERN - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'IPX Type 20 Routing' CONFIG_IPX_PPROP_ROUTING - fi fi tristate 'Appletalk DDP' CONFIG_ATALK tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25 diff -u --recursive --new-file v2.1.63/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c --- v2.1.63/linux/net/ipx/af_ipx.c Wed Nov 12 13:34:28 1997 +++ linux/net/ipx/af_ipx.c Thu Nov 13 12:58:50 1997 @@ -750,7 +750,6 @@ } } -#ifdef CONFIG_IPX_PPROP_ROUTING if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 && skb->pkt_type == PACKET_HOST ) { int i; @@ -802,7 +801,6 @@ } } -#endif if (!ipx->ipx_dest.net) ipx->ipx_dest.net = intrfc->if_netnum;